/******************************************************************************

    wavfilt --- SSKFバンクによる8帯域分割と非線形量子化

      version 3.50                           by とご(電脳わ〜るど:DW0243)

    02/20/1995                                           "main.c"

    オプションによる動作の決定とファイルデータ作成を行うルーチンを定義します。
  生成ファイルのフォーマットの詳細はファイルformat.refをご覧ください。

 *****************************************************************************/

#define __MAIN__

#include "common.h"
#include "buffer.h"
#include "wavinfo.h"
#include "mono_16.h"
#include "stereo16.h"

// 両フォームの共有値
static int fmt_size,data_size;

// CSNDで記録する値
static char version = VERSION; /*** 変換アルゴリズムのバージョン ***/
/*** char sup_value;                抑止シフト値                 ***/
/*** char at_least;                 量子化の最低有効値           ***/
static char uncertain1;        /*** 未定義                       ***/

static int state;   /*** WAVEファイルのデータ種状態   ***/

int checkRIFFandfmt_copy(int ID)
{
	long buf;
    state = 0;
    char *head;
	fread(&buf,1,sizeof(long),infile);
	if(buf != RIFFID)return -1;

	fread(&buf,1,sizeof(long),infile);
    fread(&buf,1,sizeof(long),infile);
	switch(buf){
      case WAVE:
        if(ID == WAVE)return -1;
        break;
      case CSND:
        if(ID == CSND)return -1;
        break;
      default:  
		return -1;
	}

	fread(&buf,1,sizeof(long),infile);
	if(buf != fmt_ID)return -1;
	fread(&fmt_size,1,4,infile);

    head = (char *)malloc(fmt_size);
    fread(head,fmt_size,1,infile);

//  headでstateの設定
    switch(*(short *)&head[0]){ /* 形式 */
      case 1:
        state += 1;
        break;
      default:
        return -1;
    }
    switch(*(short *)&head[2]){ /* チャンネル数 */
      case 1:
        state += 10;
        break;
      case 2:
        state += 20;
        break;
      default:
        return -1;
    }
    switch(*(short *)&head[14]){ /* 量子化ビット数 */
      case 8:
        state += 100;
        break;
      case 16:
        state += 200;
        break;
      default:
        return -1;
    }

//  出力ファイル共通部分書き出し
    buf = RIFFID;    /*** RIFF形式で記録する ***/
    fwrite(&buf,4,1,outfile);
    buf = 0;         
    /***(ファイルサイズ)-(チャンクIDとチャンクサイズの大きさの和)(後で記録)***/
    fwrite(&buf,4,1,outfile);
    buf = ID;        /*** WAVEかCSNDかのフォーマットであることを宣言 ***/
    fwrite(&buf,4,1,outfile);
    buf = fmt_ID;    /*** "fmt "チャンクの記録 ***/
    fwrite(&buf,4,1,outfile);
    buf = fmt_size;
    fwrite(&buf,4,1,outfile);
    fwrite(head,fmt_size,1,outfile);

    return 0;
}

int makeCSND(void)
/*** "fmt "チャンクの記録ののちに"h_cs"(ヘッダ部分)チャンクの記録と
     "data"チャンクの準備をする ***/
{
  int buf;

  fread(&buf,1,sizeof(long),infile);
  /*** ウェーブフォームの"data"チャンクであるとき作業を行う ***/
  if(buf != dataID)return -1;

  fread(&data_size,1,sizeof(long),infile);

  buf = h_csID;
  fwrite(&buf,4,1,outfile);
  buf = 8;          /*** 2ダブルワードのデータを持っているから ***/
  fwrite(&buf,4,1,outfile);
  buf = data_size;  /*** ウェーブフォームのデータサイズ ***/
  fwrite(&buf,4,1,outfile);

  /*** 変換手法に関する数値の書き込み ***/
  fwrite(&version,1,1,outfile);
  fwrite(&sup_value,1,1,outfile);
  at_least = 1; /*** 一応書き込んでおく ***/
  fwrite(&at_least,1,1,outfile);
  fwrite(&uncertain1,1,1,outfile);

  buf = dataID;     /*** データチャンクの宣言 ***/
  fwrite(&buf,4,1,outfile);
  buf = 0;          /*** データチャンクサイズ(後で書く) ***/
  fwrite(&buf,4,1,outfile);

  return 0;
}

int makeWAVE(void)
{
  int buf;

  fread(&buf,1,sizeof(long),infile);
  /*** "h_cs"チャンクであるとき作業を行う ***/
  if(buf != h_csID)return -1;

  /*** "h_cs"チャンクのサイズとチャンクデータを読む ***/
  fread(&buf,1,sizeof(long),infile);
  fread(&data_size,1,sizeof(long),infile);
  fread(&version,1,1,infile);
  fread(&sup_value,1,1,infile);
  fread(&at_least,1,1,infile);
  fread(&uncertain1,1,1,infile);

  fread(&buf,1,sizeof(long),infile);
  /*** "CSND"フォーマットの"data"チャンクであるとき作業を続ける ***/
  if(buf != dataID)return -1;

  fread(&buf,1,sizeof(long),infile);
  /*** データチャンクサイズ(今は使われていない) ***/

// "WAVE"フォームファイルの"data"チャンクの書き込み準備
  buf = dataID;
  fwrite(&buf,4,1,outfile);
  buf = 0;        /*** データチャンクサイズ(後で書く) ***/
  fwrite(&buf,4,1,outfile);

  return 0;
}

void apology(void)
{
   puts("すんまへん、このファイルは堪忍してm(_ _)m");
}

int encode(int state)
// データ種による場合分け
{
  switch(state){
    case 211:
      encode_mono_16(data_size/2);
      return 0;
    case 221:
      encode_stereo_16(data_size/4);
      return 0;
    default:
      apology();
      return -1;
  }
}

int decode(int state)
// データ種による場合分け
{
  switch(state){
    case 211:
      decode_mono_16(data_size/2);
      return 0;
    case 221:
      decode_stereo_16(data_size/4);
      return 0;
    default:
      apology();
      return -1;
  }
}


int main(int argc, char *argv[])
{

#define ENCODE 1
#define DECODE -1

  int j,flag,qfactor,sfactor;
  char *s;
  int ret,_ret;

  if(argc != 4){
    return EXIT_FAILURE;
  }

  s = argv[1];
  if(s[0] == '-'){
    flag = 0;
    for(j = 1; s[j]; ++j){ /* この部分は将来の拡張用 */
      switch(s[j]){
      /* 現れたもの勝ち */
        case 'e':  /* 変換 */
        case 'E':  /* 変換 */
          flag = ENCODE;
          break;

        case 'd':  /* 逆変換 */
        case 'D':  /* 逆変換 */
          flag = DECODE;
          break;

        case 'q':  /* 量子化因数 */
          qfactor = atoi(&s[j+1]);
          break;

        case 's':  /* 抑止シフト値 */
          sfactor = atoi(&s[j+1]);
          break;

        default:
          break;
      }
    }
  }


  switch(sfactor){
    case 0:
    case 1:
    case 2:
    case 3:
    case 4:
    case 5:
    case 6:
    case 7:
    case 8:
      sup_value = 8-(char)sfactor;
      break;
    default:
      sup_value = SUP_VALUE;
  }

  switch(qfactor){
    case 1:
      QMIN = 1;   QMAX = 1;
      break;
    case 2:
      QMIN = 2;   QMAX = 4;
      break;
    case 4:
      QMIN = 4;   QMAX = 8;
      break;
    case 8:
      QMIN = 8;   QMAX = 16;
      break;
    case 16:
      QMIN = 16;  QMAX = 32;
      break;
    case 32:
      QMIN = 32;  QMAX = 64;
      break;
    case 64:
      QMIN = 64;  QMAX = 128;
      break;
    case 128:
      QMIN = 128; QMAX = 256;
      break;
    default:
      QMIN = 1;  QMAX = 1;
  }

  switch(flag){
    case ENCODE:
      if(((infile  = fopen(argv[2], "rb")) != NULL)&&
         ((outfile = fopen(argv[3], "wb")) != NULL)){
        if(checkRIFFandfmt_copy(CSND) != -1){
          if(makeCSND() != -1){
            ret = encode(state);
          }
        }
        if(ret == 0){ /* チャンクサイズをきちんと書く */
          ret = ftell(outfile);
          fclose(outfile);
          outfile = fopen(argv[3], "r+b");
          _ret = ret - 8;
          ret = ret - fmt_size - 40;
          fseek(outfile,(40+fmt_size),SEEK_SET);
          fwrite(&ret,4,1,outfile);
          fseek(outfile,4,SEEK_SET);
          fwrite(&_ret,4,1,outfile);
        }
      }
      fclose(infile);  fclose(outfile);
      break;
    case DECODE:
      if(((infile  = fopen(argv[2], "rb")) != NULL)&&
         ((outfile = fopen(argv[3], "wb")) != NULL)){
        if(checkRIFFandfmt_copy(WAVE) != -1){
          if(makeWAVE() != -1){
            ret = decode(state);
          }
        }
        if(ret == 0){ /* チャンクサイズをきちんと書く */
          ret = ftell(outfile);
          ret -= 8;
          fclose(outfile);
          outfile = fopen(argv[3], "r+b");
          fseek(outfile,4,SEEK_SET);
          fwrite(&ret,4,1,outfile);
          fseek(outfile,24+fmt_size,SEEK_SET);
          fwrite(&data_size,4,1,outfile);
        }
      }
      fclose(infile);  fclose(outfile);
      break;
    default:
      return EXIT_FAILURE;
  }
  return EXIT_SUCCESS;
}
