///////////////////////////////////////////////////////////////////////////////
//
//   jpeg をデコードする
//

#include <stdlib.h>
#include <string.h>

#include "jdecoder.h"


JPEGDecoder::JPEGDecoder( InputStream *stream ) :
  stream_(stream)
{
  image_started_ = false;
  image_end_ = false;
}


//-----------------------------------------------------------------------------
/*! 16 ビットのマーカを得る
 * @exception  UnexpectedEOFException  ファイルが終端に達したとき
 */
unsigned long JPEGDecoder::get_marker() {
  while (stream_.show8() != 0xFF) {
    stream_.read1();
  }

  return stream_.read16();
}



//-----------------------------------------------------------------------------
/*!  スキャンの直前まで読み込み
 */
void JPEGDecoder::before_image() {

  while ( ! image_started_ ) {

    switch( get_marker() ) {
    case END_OF_IMAGE:
      image_end_= true;
      break;

    case START_OF_IMAGE :
      segment_startOfImage();
      break;

    case APPLICATION_0:
      segment_application0();
      break;

    case DEFINE_QUANTIZATION_TABLE:
      segment_defineQuantizationTable();
      break;

    case START_OF_FRAME_0:
      segment_startOfFrame0();
      break;

    case DEFINE_HUFFMAN_TABLE:
      segment_defineHuffmanTable();
      break;

    case START_OF_SCAN:
      segment_startOfScan();
      image_started_ = true;
      break;

    default:
      segment_unknown();
      break;
    }

  }

  return;
}


//-----------------------------------------------------------------------------
//! Start Of Image
void JPEGDecoder::segment_startOfImage() {
  return; // 中身は空
}



//-----------------------------------------------------------------------------
/*! Application 0
 * @exception UnexpectedEOFException
 */
void JPEGDecoder::segment_application0() {

  // 2 バイト .. セグメント長
  unsigned long len = stream_.read16();

  // 5 バイト .. 識別子
  for (int i=0; i<5; ++i)
    app0_identifier_[i] = (char)stream_.read8();

  // 識別子は "JFIF\0" ?
  if ( !strcmp(app0_identifier_, "JFIF") ) {

    // 1 バイト .. メジャーバージョン番号
    major_revision_ = stream_.read8();
    // 1 バイト .. マイナーバージョン番号
    minor_revision_ = stream_.read8();

    // 1 バイト .. 密度単位
    density_unit_ = stream_.read8();

    // 2 バイト .. 横密度
    horiz_density_ = stream_.read16();
    // 2 バイト .. 縦密度
    verti_density_ = stream_.read16();

    // 1 バイト .. サムネイル横幅
    thumb_width_ = stream_.read8();
    // 1 バイト .. サムネイル高さ
    thumb_height_ = stream_.read8();

    // 残り (len - 16) バイトはサムネイル画像
    len -= 16;
    while (len > 0) {
      len--;
      stream_.read8();
    }

  } else if ( !strcmp(app0_identifier_, "JFXX") ) {
    // unsupported
  }

  return;
}



//-----------------------------------------------------------------------------
//  Define Quantization Table  量子化テーブル定義
void JPEGDecoder::segment_defineQuantizationTable() {

  // 2 バイト .. セグメント長
  unsigned long len = stream_.read16();

  // テーブル(65バイト)が記述されている回数
  int count = (len - 2) / 65;

  while (count-- > 0) {
    unsigned long precision = stream_.read8(); // とりあえず 1 バイト取り出し
    unsigned long number = precision & 15;   //   下位 4 ビット .. 量子化テーブル識別子
    precision >>= 4;           //   上位 4 ビット .. 精度

    for (int i=0; i<64; ++i) {
      quant_tables_[number][i] = stream_.read8();
    }
  }

  return;
}



//-----------------------------------------------------------------------------
//  Start Of Frame 0

void JPEGDecoder::segment_startOfFrame0 () {

  // 2 バイト .. セグメント長
  unsigned int len = stream_.read16();

  // 1 バイト .. 画素深度 ( '8' 固定 )
  depth_per_component_ = stream_.read8();

  // 2 バイト .. 高さ
  height_ = stream_.read16();

  // 2 バイト .. 横幅
  width_ = stream_.read16();

  // 1 バイト .. 画像成分数
  component_count_ = stream_.read8();

  for (int i=0; i<component_count_; ++i) {
    // 1 バイト .. 成分識別子
    component_id_[i] = stream_.read8();

    // 1 バイト .. サンプリングファクタ
    unsigned int sampling_factor = stream_.read8();
      // そのうち上位 4 ビット .. 水平サンプリングファクタ
      horizontal_sampling_factor_[i] = (int)sampling_factor >> 4;
      // 下位 4 ビット .. 垂直サンプリングファクタ
      vertical_sampling_factor_[i] = (int)sampling_factor & 15;

    // 1 バイト.. 量子化テーブルセレクタ
    qt_selector_[i] = stream_.read8();
  }

  // 後の利便性のために，あらかじめ色々計算しておくとよい
  make_extradata_SOF0();

  return;

}

static
int get_max(int *begin, int *over) {
  int max = 0;
  while (begin < over) {
    if ( max < *begin ) max = *begin;
    ++begin;
  }
  return max;
}

static
int get_min(int *begin, int *over) {
  int min = 0x7fffffff;
  while (begin < over) {
    if (*begin < min) min = *begin;
    ++begin;
  }
  return min;
}

void JPEGDecoder::make_extradata_SOF0() {

  int i, count;
  int hsf_min, hsf_max;
  int vsf_min, vsf_max;
  int horiz_blocks, verti_blocks;

  // いくつか取得できたサンプリングファクタの中で，最大/最小値を保持
  count = component_count_;
  hsf_min = get_min( horizontal_sampling_factor_, horizontal_sampling_factor_ + count);
  hsf_max = get_max( horizontal_sampling_factor_, horizontal_sampling_factor_ + count);

  vsf_min = get_min( vertical_sampling_factor_, vertical_sampling_factor_ + count);
  vsf_max = get_max( vertical_sampling_factor_, vertical_sampling_factor_ + count);

  // 「ブロックの数」にまつわる各種の値を計算

  total_block_count_a_MCU_ = 0;

  for (i=0; i<count; ++i) {

    inverse_horizontal_sampling_factor_[i] =
        hsf_max / horizontal_sampling_factor_[i];

    inverse_vertical_sampling_factor_[i] =
        vsf_max / vertical_sampling_factor_[i];

    //jd->horizontal_block_count_a_MCU[i] =
    //    jd->horizontal_sampling_factor[i] / hsf_min;
    horizontal_block_count_a_MCU_[i] = horizontal_sampling_factor_[i];

    //jd->vertical_block_count_a_MCU[i] =
    //    jd->vertical_sampling_factor[i] / vsf_min;
    vertical_block_count_a_MCU_[i] = vertical_sampling_factor_[i];

    block_count_a_MCU_[i] =
        horizontal_block_count_a_MCU_[i] *
        vertical_block_count_a_MCU_[i];

    total_block_count_a_MCU_ += block_count_a_MCU_[i];
  }

  horiz_blocks = get_max(horizontal_block_count_a_MCU_, horizontal_block_count_a_MCU_ + count);
  verti_blocks = get_max(vertical_block_count_a_MCU_, vertical_block_count_a_MCU_ + count);

  MCU_width_ = horiz_blocks * 8;   // 1 MCU の横ドット数
  MCU_height_ = verti_blocks * 8;  //         高さ

  return;
}


//-----------------------------------------------------------------------------
// ハフマンテーブルをハフマン木に変換する

static
int make_huffman_tree( HuffmanTree *root,
                 int *src_codes, int *src_bits, int table_size )
{
  int code, bits;
  int i, b;
  HuffmanTree *next_tree, *current_tree;

  // 木を生成してゆく
  for (i=0; i<table_size; ++i) {
    code = src_codes[i];
    bits = src_bits[i];
    current_tree = root;

    while (bits--) {

      if (bits == 0) {
        current_tree->value[code & 1] = i;
        break;
      }

      b = (code & (1 << bits)) ? 1 : 0;
      next_tree = current_tree->next_tree[b];

      if (! next_tree) {
        // 新しく枝を生成する
        next_tree = new HuffmanTree();
        current_tree->next_tree[b] = next_tree;
      }

      current_tree = next_tree;
    }
  }

  return 0; // noerror
}

//-----------------------------------------------------------------------------
//   Define Huffman Table

/*static
void print_bits( unsigned int code, unsigned int bits ) {
  if ( 1 < bits )
    print_bits( code >> 1, bits - 1);
  printf("%c", code & 1 ? '1' : '0');
}

static
void print_component( HuffmanTable *table, int comp ) {
  printf(" %3d (zrl %2d, code %2d): ", comp, comp >> 4, comp & 15 );
  print_bits( table[comp].code, table[comp].bits );
  printf("\n");
}*/


void JPEGDecoder::segment_defineHuffmanTable() {

  // 2 バイト .. セグメント長
  int len = (int)stream_.read16() - 2;

  while (len > 0) {
    int i, k, lengths[16], code = 0;
    int codes[256] = {0}, bits[256] = {0};

    // 1 バイト .. テーブルのクラスと識別子
    unsigned int class_and_id = stream_.read8();
      unsigned int table_class = class_and_id >> 4;
      unsigned int table_id    = class_and_id & 15;
    --len;

    // どの木に値を記憶させるかを選択
    HuffmanTree  *tree  = &huffman_trees_[table_class][table_id];

    // 1 バイト x 16 回 .. 後ろに格納されているそれぞれの要素の個数
    for (i=0; i<16; ++i)
      lengths[i] = (int)stream_.read8();
    len -= 16;

    for (i=0; i<16; ++i) {

      // 1 バイト x lengths[i] 回 .. 要素
      for (k=0; k<lengths[i]; ++k) {
        unsigned int component = stream_.read8();
        codes[component] = code++;
        bits[component] = i+1;
        // print_component( table, component );
      }

      code <<= 1;
      len -= lengths[i];
    }

    // テーブルを木に変換
    make_huffman_tree( tree, codes, bits, 256);
  }

  return;
}

//-----------------------------------------------------------------------------
void JPEGDecoder::segment_startOfScan() {

  // 2 バイト .. セグメント長
  int len = (int)stream_.read16() - 2;

  // 1 バイト .. スキャン内の成分数
  int count = (int)stream_.read8();

  for (int i=0; i<count; ++i) {
    // 1 バイト .. スキャン成分セレクタ
    int component_id = (int)stream_.read8();

    // とりあえず 1 バイト
    unsigned int dc_ac_table = stream_.read8();
      // 上位 4 ビット .. DC 成分用ハフマンテーブルセレクタ
      huffman_table_for_dc_[i] = dc_ac_table >> 4;
      // 下位 4 ビット .. AC 成分用ハフマンテーブルセレクタ
      huffman_table_for_ac_[i] = dc_ac_table & 15;
  }

  // 以下 3 バイトはここでは未使用
  unsigned int unused;
  unused = stream_.read8();
  unused = stream_.read8();
  unused = stream_.read8();

  return;
}



//-----------------------------------------------------------------------------
void JPEGDecoder::segment_unknown(){
  int len = (int)stream_.read16() - 2;

  while (len-- > 0) {
    stream_.read8();
  }

  return;
}


//-----------------------------------------------------------------------------
void JPEGDecoder::decode_MCU_line() {

  return;
}


//=============================================================================
int JPEGDecoder::getWidth() {
  before_image();
  return width_;
}

//=============================================================================
int JPEGDecoder::getHeight() {
  before_image();
  return height_;
}


//=============================================================================
int JPEGDecoder::getDepth() {
  before_image();
  return depth_per_component_ * component_count_;
}

//=============================================================================
int JPEGDecoder::getBPL() {
  before_image();
  return getDepth() / 8 * getWidth();
}


//=============================================================================
int JPEGDecoder::getLine(unsigned char *) {
  return 0;
}


