
#ifndef  __JBXL_CPP_GRAPHIC_IO_H_
#define  __JBXL_CPP_GRAPHIC_IO_H_


/*
	グラフィック用ファイル入出力関数ヘッダ  

	CTファイルへの入出力もサポートする．また，ファイル入出力の統合関数もサポート
 
	JPEG については JpegTool.cpp 参照．


  	依存関係
		#include "common++.h"
		#include "tools++.h"
		#include "Gdata.h"

*/





#include "common++.h"
#include "tools++.h"
#include "Gdata.h"



//
namespace jbxl {



/**
扱う事のできるファイル形式と読み込んだ時のヘッダ種別


Common形式
	CmnHead  cmhd   : common header. 36Byte. 
	Graphic　Header : 画像データ固有のヘッダ
	Graphic  Data   : データ

	任意の画像データを保存する時、その画像データに識別ヘッダ(CmnHead)
	を付けて保存したもの．

-----------------------------------------------------------------------------------------------------
UN_KNOWN_DATA


MOON_DATA


DICOM_DATA


USERSET_DATA


RAS_DATA


JPEG_RGB_DATA


JOEG8_DATA


CT_DATA
	CmnHead  cmhd : common header. 36Byte. （省略可）
	CTHead   cthd : CT header (Moon Header). 64Byte. メンバは Word型. 
	Graphic  Data : データ

	Common Headerは省略可．
	いわゆる Moon型のヘッダを持つ CT画像データ．現在は CT画像を保存する場合は、
	デフォルトでこの型で保存される．
	CTHead の cthd.anydata[0]に画素間隔、cthd.anydata[1]に画像間隔が 1/RZXY_RATE mm単位
	で保存されている場合がある．ファイルから読み込まれ，CmnHead に格納
	された場合は，CT_3DMと区別されない．  


CT_3DM    
	CmnHead  cmhd : common header. 36Byte. 
	CTHead   cthd : CT header (Moon Header). 64Byte. 
	Graphic  Data : データ

	3Dの CTデータを保存する場合に使用されるデータの型．CTHead の cthd.anydata[0]
	に画素間隔、cthd.anydata[1]に画像間隔が 1/RZXY_RATE mm単位で保存されている
	場合がある．メモリ内で使用する場合（CmnHeadに格納された場合）は CT_Data と
	区別されない．


CT_3D_VOL

*/    
    


//////////////////////////////////////////////////////////////////////////////////////////////////////////
//  for Simple
//
//template <typename T>  int  writeRasFile(char *fname, MSGraph<T> vp);
//template <typename T>  MSGraph<T>  readRasFile(char *fname);




//////////////////////////////////////////////////////////////////////////////////////////////////////////
//  for SUN Raster & User Define
//
CmnHead	readUserSetData(FILE* fp, CmnHead* ch, bool cnt=false);	// read_user_data() for C  // ユーザ指定(ch)のデータ形式でファイルを読み込む
CmnHead	readRasData(FILE* fp);									// read_ras_data() for C   // SUN RASTER形式のファイルを読み込む 
int		writeRasData(FILE* fp, CmnHead* ch, int obit=8);		// write_ras_data() for C



//////////////////////////////////////////////////////////////////////////////////////////////////////////
//  for CT画像
//
CmnHead	readMoonFile(char* fn, bool no_ntoh=false);	
CmnHead	readMoonData(FILE* fp, unsigned int fsz=0, bool no_ntoh=false);	// MOON_DATA形式のファイルを読み込む．

int		dicomHeader(FILE* fp, int fsize, int* dsize, int* xsize, int* ysize, int* depth, float* rzxy);
MSGraph<sWord> readDicomFile(char* fn);
MSGraph<sWord> readDicomData(FILE* fp, int fsz);



/////////////////////////////////////////////////////////////////////////////////////////////////////////
//  for I/O 統合  多種ファイル対応
//

//template <typename T> MSGraph<T> readGraphicFile  (char* fname, CmnHead* chd=NULL, bool cnt=false);
//template <typename T> MSGraph<T> readGraphicSlices(char* frmt, int fst, int fnd, CmnHead* chd);
//template <typename T> int writeGraphicFile(char *fname, MSGraph<T> vp, int kd=0, int fn=0, int tn=-1, bool cnt=false);

CmnHead readXHead      (char* fn, CmnHead* ch=NULL);				 // ヘッダ部分のみ読み込み 
CmnHead	readXHeadFile  (char* fn, CmnHead* ch=NULL, bool cnt=false); // 拡張read関数．ファイルを自動判別して読み込む．
CmnHead	readCmnHeadFile(char* fn, CmnHead* ch, bool cnt=false);		 // 拡張read関数．ファイル種別を指定して読み込む．

int		writeCmnHeadFile(char* fn, CmnHead* hd, bool cnt=false);
int 	writeCmnHeadData(FILE* fp, CmnHead* hd, bool cnt=false);





/////////////////////////////////////////////////////////////////////////////////////////////////////////
//  Ｃ言語との関数対応
/*
CmnHead	   readRasData()		 	read_ras_data()  for C 
CmnHead	   readUserSetData()		read_user_data() for C 
int		   writeRasData()			write_ras_data() for C
int		   writeRasFile()			write_ras_file() for C
MSGraph<T> readRasFile()			read_ras_file()  for C
int		   dicomHeader() 			dicom_header()   for C
*/






///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ファイル名によるI/Oサポート

/**
template <typename T>  MSGraph<T> readGraphicFile(char* fname, CmnHead* chd=NULL, bool cnt=false)

    機  能: 色々なグラフィックファイルを読み込んで，MSGraph<T>に格納．			
			サポートしているデータ形式は, 
				Common 形式  : Common 形式にしたがって読み込む．(CT_RGN_SLは未サポート)
				Sun Raster　 : Common 形式に変換する．ヘッダは RAS_DATA になる 
				Moon(CT)　　 : Common 形式に変換する．ヘッダは MOON_DATA になる．
				USERSET_DATA : CmnHead* chd でヘッダ情報を指定して，読み込みを行う．ヘッダは USERSET_DATAになる．
				DICOM_DATA   : DICOM ファイル
				JPEG_RGB_DATA    : JPEG ファイル
				JPEG_MONO_DATA	 : モノクロ JPEG
				unkonwn 　　 : ファイルをそのまま，hd->grptrへ読み込む．ヘッダは UN_KNOWN_DATA になる．
			ただし，UN_KNOWN_DATAの場合は CmnHaedの hd->kind, hd->lsize, hd->gptr のみ有効．

    引  数: *fname -- 読み込むを行うファイル名．
			*chd   -- CmnHeadにも格納したい場合は，そのアドレスを指定． 			
					  USERSET_DATA指定の場合，ファイルのヘッダ情報を入れた CmnHead

    戻り値：読み込んだグラフィックデータ(MSGraph<T>)と ヘッダ情報(CmnHead)
			
			CmnHeadのヘッダ情報(chd!=NULLのときのみ)：　chd->kind 
				HEADER_ERROR→　データ読み込み失敗．
				UN_KNOWN_DATA 　 →  ヘッダが解析できないのでファイルイメージのままchd->grptrに格納される．
								chd->kind, chd->lsize, chd->gptrのみ有効．MSGraph<T>.gp は NULL．
				その他　	→	MSGraph<T> に画像データが入る．
								*chd にはヘッダ情報のみが入る．chd->buf, chd->gptr は NULL.
								CT_DATA, RAS_DATA, CT_3DM など．

	戻り値：読み込んだグラフィックデータと，それを記述する共通ヘッダ．
            エラーなら MSGraph.isNull()が真，または　CmnHead.kind = HEADER_ERROR となる．
			またエラーの種類により MSGraph.stateが変化する.
               state   ERROR_GRAPH_HEADER  ファイルのヘッダ読み取りエラー．
                       ERROR_GRAPH_MEMORY  メモリエラー．

	バ  グ：Common 形式の画素深度が 24,32bitの場合のエンディアン処理が未実装

*/
template <typename T>  MSGraph<T> readGraphicFile(char* fname, CmnHead* chd=NULL, bool cnt=false)
{
    MSGraph<T> vp;
	CmnHead    hd;
	CVCounter* counter = NULL;

	counter = GetUsableGlobalCounter();
	if (counter!=NULL) {
		counter->SetMax(100);
		counter->MakeChildCounter(80);
	}

	vp.init();

    hd = readXHeadFile(fname, chd, cnt);
	if (counter!=NULL) counter->DeleteChildCounter();	

	// Error
	if (hd.kind==HEADER_ERROR) {
		free_CmnHead(&hd);
		if (hd.xsize<0) vp.state = hd.xsize;
		else            vp.state = ERROR_GRAPH_HEADER;
		return vp;
	}

	// Unknown Data
	int chk = hd.kind & 0x00ff;
  	if (chk==UN_KNOWN_DATA) {
		if (chd!=NULL) *chd = hd;
		else  free_CmnHead(&hd);
		vp.state = ERROR_GRAPH_HEADER;
		return vp;
	}

	if (counter!=NULL) counter->MakeChildCounter(80);

	if (cnt) {
		CVCounter* counter = GetUsableGlobalCounter();
		if (counter!=NULL) counter->SetTitle("画像データへ変換");
	} 

	if (chd!=NULL) {
		*chd = hd;
		chd->grptr = chd->buf = NULL;
	}

	vp = copyCmnHead2MSGraph<T>(hd, CH2MG_NORMAL, cnt);
	free_CmnHead(&hd);
	if (vp.gp==NULL) return vp;


	if (ZeroBase!=0) {
		vp.base += ZeroBase;
		for (int i=0; i<vp.xs*vp.ys*vp.zs; i++) vp.gp[i] += (T)ZeroBase;
	}

	if (counter!=NULL){
		counter->DeleteChildCounter();
		counter->PutFill();
	}

  	return  vp;
}




/**
template <typename T>  MSGraph<T>  readGraphicSlices(char* fmt, int fst, int fnd, CmnHead* chd=NULL, bool cnt=false)

    機  能: 色々なグラフィックファイルをスライスデータとして読み込んで，3D画像として
			MSGraph<T>に格納．また，chd!=NULL ならCommon(CmnHead)形式にも変換する．
			
			Z方向の歪率をサポート（CmnHead, Moon形式のファイルのみ）
			グローバルカウンタが使用可能．カウンタによるキャンセル機能を持つ．

			readGraphicFile()と殆ど同じ手法を用いているので，その他は readGraphicFile()に準じる．

    引  数: *fname -- 読み込むを行うファイル名（%dなどの入った書式）．
			fst	   -- 最初に読み込むファイルの番号（ファイル名の中にその数字がなければならない）
			fnd	   -- 最後に読み込むファイルの番号（ファイル名の中にその数字がなければならない）
			*chd   -- CmnHeadにもデータを格納したい場合はそのアドレスを指定．
					　または DICOM_DATA, USERSET_DATA指定時のヘッダ情報を格納する．			

	例	　： vp = readGraphicSlices("i%02d", 0, 10);
				i00 〜 i10 までの11枚の画像データを読み込んで，vpに格納．

*/
template <typename T>  MSGraph<T>  readGraphicSlices(char* fmt, int fst, int fnd, CmnHead* chd=NULL, bool cnt=false)
{
    int   i, k, l, m, chk, kind;
	int   xsize, ysize, zsize, nsize, psize;	// zsize: 1枚の画像のZサイズ．
												// nsize: 出来上がりの 3D画像のZサイズ
    char  *fr_name;						
    CmnHead    dhd, hd;
	MSGraph<T> vp, gd;


	if (chd!=NULL) {
		DEBUG_MESGLN("readGraphicSlices: Called with kind = 0x%04x", chd->kind);
	}
	else {
		DEBUG_MESGLN("readGraphicSlices: Called with NULL Header");
	}

	gd.init();
	vp.init();

	// １枚目（ヘッダ情報）の読み出し
    fr_name = numbering_name(fmt, fst);
    dhd = readXHead(fr_name, chd);
    freeNull(fr_name);

	chk = dhd.kind & 0x00ff;
    if (dhd.kind==HEADER_ERROR || chk==UN_KNOWN_DATA) {
		DEBUG_MESGLN("readGraphicSlices: ヘッダ読み込みエラー： kind = %d", dhd.kind);
		free_CmnHead(&dhd);
		vp.state = ERROR_GRAPH_HEADER;
		vp.zs = fst;
		return vp;
	}
	kind  = dhd.kind;
	xsize = dhd.xsize;
	ysize = dhd.ysize;
	zsize = Max(dhd.zsize, 1);
	nsize = zsize*(Xabs(fnd - fst) + 1);
	psize = xsize*ysize*zsize;
	if (dhd.depth==16) gd.color = GRAPH_COLOR_MONO16;
	else               gd.color = GRAPH_COLOR_MONO;

	// カウンタ
	CVCounter* counter = NULL;
	if (nsize>=10 && cnt) {
		counter = GetUsableGlobalCounter();
		if (counter!=NULL) {
			//counter->SetTitle("マルチファイル読み込み中");
			counter->SetMax(nsize/10);
			counter->SetPos(1);
		}
	}

	// Z軸の歪, ベース，rbound の設定
	if (dhd.buf!=NULL && (chk==CT_DATA || chk==CT_3DM || chk==DICOM_DATA)) {
		sWord* rz  = (sWord*)dhd.buf;
		if (rz[9]==RZXY_RATE || checkBit(dhd.kind, HAS_RZXY)) {
			float rzm = (float)(rz[8])/rz[9];
			if (rzm<5.0 && rzm>0.) gd.RZxy = rzm;
		}

		if (rz[10]!=0 && checkBit(dhd.kind, HAS_BASE)) {
			gd.base = (T)rz[10];
		}

		if (checkBit(dhd.kind, HAS_RBOUND)) {
			gd.rbound.xmin = rz[6];
			gd.rbound.xmax = rz[7];
			gd.rbound.ymin = rz[4];
			gd.rbound.ymax = rz[5];
			gd.rbound.zmin = rz[2];
			gd.rbound.zmax = rz[3];
		}
	}
	free_CmnHead(&dhd);

	dhd.kind = kind;
	if (chd!=NULL) dhd.kind |= chd->kind & 0xff00;		// オプション
	*chd = getinfo_CmnHead(dhd);


	// データ読み込み（Z軸方向） １枚目から
    gd.getm(xsize, ysize, nsize);
	if (gd.isNull()) return gd;

	l = 0;
    k = fst;
	m = Sign(fnd - fst);
	while(k != fnd+m){
		fr_name = numbering_name(fmt, k);
		hd = readCmnHeadFile(fr_name, &dhd, false);	// カウンタ無効
		free(fr_name);

		chk = hd.kind & 0x00ff;
		if (hd.kind==HEADER_ERROR || chk==UN_KNOWN_DATA) {
			DEBUG_MESGLN("readGraphicSlices: データ読み込みエラー： n = %d, kind = %d", k, hd.kind);
			free_CmnHead(&hd);
			if (l==0) {		// 1枚目でエラー
				gd.free();
				vp.state = ERROR_GRAPH_HEADER;
				vp.zs = k;
				return vp;
			}
			else {			// 途中までは読み込み完了
				gd.state = l;
				gd.zs = l*zsize;
				break;
			}
		}

		vp = copyCmnHead2MSGraph<T>(hd, CH2MG_NOPARM, false);	// カウンタ無効
		if (vp.isNull()) {
			free_CmnHead(&hd);
			gd.free();
			vp.free();
			vp.state = ERROR_GRAPH_MEMORY;
			vp.zs = k;
			return vp;
		}

        // gd へコピー
		for (i=0; i<psize; i++) gd.gp[i + psize*l] = vp.gp[i] + ZeroBase;
		if (l==0) {
			gd.min = vp.min;
			gd.max = vp.max;
		}
		else {
			gd.min = Min(gd.min, vp.min);
			gd.max = Max(gd.max, vp.max);
		}

		// 後始末
		free_CmnHead(&hd);
		vp.free();
        k += m;
		l++;

		// カウンタ
		if (counter!=NULL && l%10==0) {
			counter->StepIt();
			if (counter->isCanceled()) {	// キャンセル
				gd.free();
				vp.state = ERROR_GRAPH_CANCEL;
				vp.zs = k;
				return vp;
			}
		}
	}
	gd.state = l;
	gd.base += ZeroBase;

	if (!checkBit(kind, HAS_RBOUND)) gd.rbound.set(0, gd.xs-1, 0, gd.ys-1, 0, gd.zs-1);
	if (chd!=NULL) {
		if ((chd->kind&0x00ff)==USERSET_DATA) {
			CVCounter* counter = GetUsableGlobalCounter();
			if (counter!=NULL) counter->SetTitle("表示用データへ変換");
			*chd = copyMSGraph2CmnHead(gd);
		}
	}
	if (counter!=NULL) {
		counter->PutFill();
 		//counter->Stop();
	}
   
	return gd;
}




/**
template <typename T>  int  writeGraphicFile(char *fname, MSGraph<T> vp, int kind=0, int mlt=FALSE, int fnum=0, int tnum=0, bool cnt=false)

    機能: グラフィックデータvpをファイルとして書き出す．
          kind でファイルの種別を指定可能．ただし kind==0 の場合は
				vp.zs<=1 なら CT_DATAとして保存される．
				vp.zs >1 なら CT_3DM として保存される．

		  サポートするデータ形式：CT_DATA, CT_3DM, MOON_DATA, RAS_DATA, JPEG_RGB_DATA, JPEG_MONO_DATA

          RZxy!=1.0 ならヘッダにその情報が埋め込まれる．
		  fnum, tnum を指定するとグラフィックデータの一部を保存できる．

    引数: *fname  -- 保存する際のファイル名．
          vp      -- 保存するグラフィックデータ．
		  kind    -- データ形式 + 属性ビット
		  mlt     -- 3Dデータとして保存しない
		  fnum    -- 保存するデータの始まりの Z座標．デフォルトは 0
		  tnum    -- 保存するデータの終わりの Z座標．デフォルトは vp.zs-1
		  cnt     -- カウンタ（プログレスバー）を表示するか？

	戻り値： 0 >   正常終了．
             ERROR_GRAPH_OPFILE  ファイルオープンエラー．
             ERROR_GRAPH_MEMORY  メモリエラー．
			 ERROR_GRAPH_WRFILE  書き込みファイルサイズ不一致
			 ERROR_GRAPH_RDFILE  書き込みファイルの再読み取り不可
			 ERROR_GRAPH_HEADER  書き込みファイルのヘッダ異常
			 ERROR_GRAPH_CANCEL  キャンセル

*/
template <typename T>  int  writeGraphicFile(char *fname, MSGraph<T> vp, int kind=0, int mlt=FALSE, int fnum=0, int tnum=0, bool cnt=false)
{
	int		ret;
    CmnHead hd;
	CTHead* chd;

	if (fnum<0)            fnum = 0;
	else if (fnum>vp.zs-1) fnum = vp.zs - 1;
	if (tnum<fnum)         tnum = fnum;
	else if (tnum>vp.zs-1) tnum = vp.zs - 1;


	// for JPEG
	if ((kind&0x00ff)==JPEG_RGB_DATA) {
		if		(vp.color==GRAPH_COLOR_ARGB)   kind = (kind&0xff00) + JPEG_ARGB_DATA;
		else if (vp.color==GRAPH_COLOR_RGBA)   kind = (kind&0xff00) + JPEG_RGBA_DATA;
		if		(vp.color==GRAPH_COLOR_RGB16)  kind = (kind&0xff00) + JPEG16_RGB_DATA;
		else if (vp.color==GRAPH_COLOR_ARGB16) kind = (kind&0xff00) + JPEG16_ARGB_DATA;
		else if (vp.color==GRAPH_COLOR_RGBA16) kind = (kind&0xff00) + JPEG16_RGBA_DATA;
	}

	//
    hd.xsize = vp.xs;
    hd.ysize = vp.ys;
    hd.zsize = tnum - fnum + 1;	//vp.zs;
    hd.depth = sizeof(T)*8;
    hd.grptr = (uByte*)(vp.gp + vp.xs*vp.ys*fnum);
    if (kind==0) {
		hd.kind  = CT_3DM;
		if (hd.zsize==1) hd.kind = CT_DATA;
	}
	else hd.kind = kind;

    hd.lsize = hd.zsize*hd.ysize*hd.zsize*2;
    hd.bsize = sizeof(CTHead);
    hd.buf   = (uByte*)malloc(hd.bsize);
    if (hd.buf==NULL) return ERROR_GRAPH_OPFILE;
   	memset(hd.buf, 0, hd.bsize);

    chd           = (CTHead*)hd.buf;
	chd->xsize    = (sWord)vp.xs;
	chd->ysize    = (sWord)vp.ys;
    chd->ctmin    = (sWord)vp.rbound.zmin;
    chd->ctmax    = (sWord)vp.rbound.zmax;
    chd->cutup    = (sWord)vp.rbound.ymin;
    chd->cutdown  = (sWord)vp.rbound.ymax;
    chd->cutleft  = (sWord)vp.rbound.xmin;
    chd->cutright = (sWord)vp.rbound.xmax;
	hd.kind      |= HAS_RBOUND;

	if (vp.RZxy!=1.0) {
		chd->anydata[0] = (sWord)(vp.RZxy*RZXY_RATE);
        chd->anydata[1] = (sWord)RZXY_RATE;
		hd.kind        |= HAS_RZXY;
    }

	if (vp.base!=0) {
		chd->anydata[2] = (sWord)vp.base + TempBase;
		hd.kind        |= HAS_BASE;
	}


	// 通常の保存
	if (!mlt) {
		ret = writeCmnHeadFile(fname, &hd, cnt);
	}

	// マルチスライス
	else {
		// カウンタ
		CVCounter* counter = NULL;
		if (hd.zsize>=10 && cnt) {
			counter = GetUsableGlobalCounter();
			if (counter!=NULL) {
				counter->SetMax((hd.zsize+1)/10);
				counter->SetPos(1);
			}
		}

		char filename[LNAME];
		int num = hd.zsize;
		int cnt = fnum;
		hd.zsize = 1;

		for (int i=0; i<num; i++) {
			snprintf(filename, LNAME-2, fname, cnt);
			hd.grptr = (uByte*)(vp.gp + vp.xs*vp.ys*cnt);
			ret = writeCmnHeadFile(filename, &hd, false);
			
			if (counter!=NULL && i%10==0) {
				counter->StepIt();
				if (counter->isCanceled()) {	// キャンセル
					if (hd.buf!=NULL) free(hd.buf);
					return ERROR_GRAPH_CANCEL;
				}
			}
			if (ret<0) break;
			cnt++;
		}
		if (counter!=NULL) counter->PutFill();
	}

    if (hd.buf!=NULL) free(hd.buf);
    
	return ret;
}





/**
template <typename T>  int  writeRasFile(char *fname, MSGraph<T> vp)


*/
template <typename T>  int  writeRasFile(char *fname, MSGraph<T> vp)
{
	if (fname==NULL) return ERROR_GRAPH_IVDARG;

	FILE* fp = fopen(fname, "wb");
	if (fp==NULL) return ERROR_GRAPH_OPFILE;

	CmnHead hd = copyMSGraph2CmnHead(vp);
	writeRasData(fp, &hd);

	fclose(fp);
}




/**
template <typename T>  MSGraph<T>  readRasFile(char *fname)


*/
template <typename T>  MSGraph<T>  readRasFile(char *fname)
{
	MSGraph<T> vp;
	CmnHead    hd;
	FILE* fp;

	vp.init();
	fp = fopen(fname, "rb");
	if (fp==NULL) return vp;

	hd = readRasData(fp);
	fclose(fp);

	vp = copyCmnHead2MSGraph<T>(hd, CH2MG_NORMAL);
	return vp;
}



/**/


}		// namespace


#endif
