//========================================================================
//
// JArithmeticDecoder.cc
//
// Copyright 2002-2004 Glyph & Cog, LLC
//
//========================================================================

//========================================================================
//
// Modified under the Poppler project - http://poppler.freedesktop.org
//
// All changes made under the Poppler project to this file are licensed
// under GPL version 2 or later
//
// Copyright (C) 2019 Volker Krause <vkrause@kde.org>
//
// To see a description of the changes please see the Changelog file that
// came with your tarball or type make ChangeLog if you are building from git
//
//========================================================================

#include <config.h>

#include "Object.h"
#include "Stream.h"
#include "JArithmeticDecoder.h"

//------------------------------------------------------------------------
// JArithmeticDecoderStates
//------------------------------------------------------------------------

JArithmeticDecoderStats::JArithmeticDecoderStats(int contextSizeA) {
  contextSize = contextSizeA;
  cxTab = (unsigned char *)gmallocn(contextSize, sizeof(unsigned char));
  reset();
}

JArithmeticDecoderStats::~JArithmeticDecoderStats() {
  gfree(cxTab);
}

JArithmeticDecoderStats *JArithmeticDecoderStats::copy() {
  JArithmeticDecoderStats *stats;

  stats = new JArithmeticDecoderStats(contextSize);
  memcpy(stats->cxTab, cxTab, contextSize);
  return stats;
}

void JArithmeticDecoderStats::reset() {
  memset(cxTab, 0, contextSize);
}

void JArithmeticDecoderStats::copyFrom(JArithmeticDecoderStats *stats) {
  memcpy(cxTab, stats->cxTab, contextSize);
}

void JArithmeticDecoderStats::setEntry(unsigned int cx, int i, int mps) {
  cxTab[cx] = (i << 1) + mps;
}

//------------------------------------------------------------------------
// JArithmeticDecoder
//------------------------------------------------------------------------

unsigned const int JArithmeticDecoder::qeTab[47] = {
  0x56010000, 0x34010000, 0x18010000, 0x0AC10000,
  0x05210000, 0x02210000, 0x56010000, 0x54010000,
  0x48010000, 0x38010000, 0x30010000, 0x24010000,
  0x1C010000, 0x16010000, 0x56010000, 0x54010000,
  0x51010000, 0x48010000, 0x38010000, 0x34010000,
  0x30010000, 0x28010000, 0x24010000, 0x22010000,
  0x1C010000, 0x18010000, 0x16010000, 0x14010000,
  0x12010000, 0x11010000, 0x0AC10000, 0x09C10000,
  0x08A10000, 0x05210000, 0x04410000, 0x02A10000,
  0x02210000, 0x01410000, 0x01110000, 0x00850000,
  0x00490000, 0x00250000, 0x00150000, 0x00090000,
  0x00050000, 0x00010000, 0x56010000
};

const int JArithmeticDecoder::nmpsTab[47] = {
   1,  2,  3,  4,  5, 38,  7,  8,  9, 10, 11, 12, 13, 29, 15, 16,
  17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
  33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 45, 46
};

const int JArithmeticDecoder::nlpsTab[47] = {
   1,  6,  9, 12, 29, 33,  6, 14, 14, 14, 17, 18, 20, 21, 14, 14,
  15, 16, 17, 18, 19, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
  30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 46
};

const int JArithmeticDecoder::switchTab[47] = {
  1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

JArithmeticDecoder::JArithmeticDecoder() {
  str = nullptr;
  dataLen = 0;
  limitStream = false;
  nBytesRead = 0;
}

inline unsigned int JArithmeticDecoder::readByte() {
  if (limitStream) {
    --dataLen;
    if (dataLen < 0) {
      return 0xff;
    }
  }
  ++nBytesRead;
  return (unsigned int)str->getChar() & 0xff;
}

JArithmeticDecoder::~JArithmeticDecoder() {
  cleanup();
}

void JArithmeticDecoder::start() {
  buf0 = readByte();
  buf1 = readByte();

  // INITDEC
  c = (buf0 ^ 0xff) << 16;
  byteIn();
  c <<= 7;
  ct -= 7;
  a = 0x80000000;
}

void JArithmeticDecoder::restart(int dataLenA) {
  unsigned int cAdd;
  bool prevFF;
  int k, nBits;

  if (dataLen >= 0) {
    dataLen = dataLenA;
  } else if (dataLen == -1) {
    dataLen = dataLenA;
    buf1 = readByte();
  } else {
    k = (-dataLen - 1) * 8 - ct;
    dataLen = dataLenA;
    cAdd = 0;
    prevFF = false;
    while (k > 0) {
      buf0 = readByte();
      if (prevFF) {
	cAdd += 0xfe00 - (buf0 << 9);
	nBits = 7;
      } else {
	cAdd += 0xff00 - (buf0 << 8);
	nBits = 8;
      }
      prevFF = buf0 == 0xff;
      if (k > nBits) {
	cAdd <<= nBits;
	k -= nBits;
      } else {
	cAdd <<= k;
	ct = nBits - k;
	k = 0;
      }
    }
    c += cAdd;
    buf1 = readByte();
  }
}

void JArithmeticDecoder::cleanup() {
  if (limitStream) {
    while (dataLen > 0) {
      buf0 = buf1;
      buf1 = readByte();
    }
  }
}

int JArithmeticDecoder::decodeBit(unsigned int context,
				  JArithmeticDecoderStats *stats) {
  int bit;
  unsigned int qe;
  int iCX, mpsCX;

  iCX = stats->cxTab[context] >> 1;
  mpsCX = stats->cxTab[context] & 1;
  qe = qeTab[iCX];
  a -= qe;
  if (c < a) {
    if (a & 0x80000000) {
      bit = mpsCX;
    } else {
      // MPS_EXCHANGE
      if (a < qe) {
	bit = 1 - mpsCX;
	if (switchTab[iCX]) {
	  stats->cxTab[context] = (nlpsTab[iCX] << 1) | (1 - mpsCX);
	} else {
	  stats->cxTab[context] = (nlpsTab[iCX] << 1) | mpsCX;
	}
      } else {
	bit = mpsCX;
	stats->cxTab[context] = (nmpsTab[iCX] << 1) | mpsCX;
      }
      // RENORMD
      do {
	if (ct == 0) {
	  byteIn();
	}
	a <<= 1;
	c <<= 1;
	--ct;
      } while (!(a & 0x80000000));
    }
  } else {
    c -= a;
    // LPS_EXCHANGE
    if (a < qe) {
      bit = mpsCX;
      stats->cxTab[context] = (nmpsTab[iCX] << 1) | mpsCX;
    } else {
      bit = 1 - mpsCX;
      if (switchTab[iCX]) {
	stats->cxTab[context] = (nlpsTab[iCX] << 1) | (1 - mpsCX);
      } else {
	stats->cxTab[context] = (nlpsTab[iCX] << 1) | mpsCX;
      }
    }
    a = qe;
    // RENORMD
    do {
      if (ct == 0) {
	byteIn();
      }
      a <<= 1;
      c <<= 1;
      --ct;
    } while (!(a & 0x80000000));
  }
  return bit;
}

int JArithmeticDecoder::decodeByte(unsigned int context,
				   JArithmeticDecoderStats *stats) {
  int byte;
  int i;

  byte = 0;
  for (i = 0; i < 8; ++i) {
    byte = (byte << 1) | decodeBit(context, stats);
  }
  return byte;
}

bool JArithmeticDecoder::decodeInt(int *x, JArithmeticDecoderStats *stats) {
  int s;
  unsigned int v;
  int i;

  prev = 1;
  s = decodeIntBit(stats);
  if (decodeIntBit(stats)) {
    if (decodeIntBit(stats)) {
      if (decodeIntBit(stats)) {
	if (decodeIntBit(stats)) {
	  if (decodeIntBit(stats)) {
	    v = 0;
	    for (i = 0; i < 32; ++i) {
	      v = (v << 1) | decodeIntBit(stats);
	    }
	    v += 4436;
	  } else {
	    v = 0;
	    for (i = 0; i < 12; ++i) {
	      v = (v << 1) | decodeIntBit(stats);
	    }
	    v += 340;
	  }
	} else {
	  v = 0;
	  for (i = 0; i < 8; ++i) {
	    v = (v << 1) | decodeIntBit(stats);
	  }
	  v += 84;
	}
      } else {
	v = 0;
	for (i = 0; i < 6; ++i) {
	  v = (v << 1) | decodeIntBit(stats);
	}
	v += 20;
      }
    } else {
      v = decodeIntBit(stats);
      v = (v << 1) | decodeIntBit(stats);
      v = (v << 1) | decodeIntBit(stats);
      v = (v << 1) | decodeIntBit(stats);
      v += 4;
    }
  } else {
    v = decodeIntBit(stats);
    v = (v << 1) | decodeIntBit(stats);
  }

  if (s) {
    if (v == 0) {
      return false;
    }
    *x = -(int)v;
  } else {
    *x = (int)v;
  }
  return true;
}

int JArithmeticDecoder::decodeIntBit(JArithmeticDecoderStats *stats) {
  int bit;

  bit = decodeBit(prev, stats);
  if (prev < 0x100) {
    prev = (prev << 1) | bit;
  } else {
    prev = (((prev << 1) | bit) & 0x1ff) | 0x100;
  }
  return bit;
}

unsigned int JArithmeticDecoder::decodeIAID(unsigned int codeLen,
				     JArithmeticDecoderStats *stats) {
  unsigned int i;
  int bit;

  prev = 1;
  for (i = 0; i < codeLen; ++i) {
    bit = decodeBit(prev, stats);
    prev = (prev << 1) | bit;
  }
  return prev - (1 << codeLen);
}

void JArithmeticDecoder::byteIn() {
  if (buf0 == 0xff) {
    if (buf1 > 0x8f) {
      if (limitStream) {
	buf0 = buf1;
	buf1 = readByte();
	c = c + 0xff00 - (buf0 << 8);
      }
      ct = 8;
    } else {
      buf0 = buf1;
      buf1 = readByte();
      c = c + 0xfe00 - (buf0 << 9);
      ct = 7;
    }
  } else {
    buf0 = buf1;
    buf1 = readByte();
    c = c + 0xff00 - (buf0 << 8);
    ct = 8;
  }
}
