/*
 *	clipobj3.c
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2006 Naoyuki Sawa
 *
 *	* Wed Jun 28 00:42:05 JST 2006 Naoyuki Sawa
 *	- 作成開始。
 *	  Expatライブラリをリンクするので、実行バイナリが大きくなります。
 *	  mesh_load()を使わないバイナリが、無駄に大きくなるのを避けるため、
 *	  通常の描画モジュールclipren2.c,clipren3.cとは別ファイルにしました。
 *	- 2006/06/28現在、テクスチャ座標の読み込みの動作確認が未だです。
 *	  ExportPieceClipプラグイン側で、テクスチャ座標出力が未実装だからです。
 *	  一応、本モジュール側のテクスチャ座標読み込み処理は、全部書いてあるので、
 *	  バグが無ければこのままで大丈夫なはずです。
 *	  ↓2006/06/29追記 
 *	  ExportPieceClipプラグインに、テクスチャ座標出力を追加しました。
 *	  本モジュールの読み込み処理も、問題なく動作しているようです。
 *	* Wed Jun 28 04:51:07 JST 2006 Naoyuki Sawa
 *	- "XML_Parser parser"を、mesh_load()のローカル変数から、MESHLOAD構造体のメンバへ移動しました。
 *	  現時点では特に意味はありませんが、今後、エレメントハンドラ中でエラーを表示する場合に備えて、
 *	  "XML_GetCurrentLineNumber(ml->parser)"などの呼び出しを可能とするためです。
 *	* Thu Jun 29 14:44:46 JST 2006 Naoyuki Sawa
 *	- ファイル名をclipmesh.c→clipobj3.cに変更しました。
 *	- wire_load()を追加しました。
 */
#include "clip.h"

/*****************************************************************************
 *	mesh_load
 *****************************************************************************/

#define MESHLOAD_STAT_MESH_START	0
#define MESHLOAD_STAT_MESH_END		1
#define MESHLOAD_STAT_VECTOR_START	2
#define MESHLOAD_STAT_VECTOR_END	3
#define MESHLOAD_STAT_TCOORD_START	4
#define MESHLOAD_STAT_TCOORD_END	5
#define MESHLOAD_STAT_FACE_START	6
#define MESHLOAD_STAT_FACE_END		7
#define MESHLOAD_STAT_FACEVERTEX_START	8
#define MESHLOAD_STAT_FACEVERTEX_END	9
#define MESHLOAD_STAT_EOF		-1

#define MESHLOAD_BUFLEN	64	/* 読み込みバッファ長 (調整可) */

/*{{clipren3.cより*/
#define N_VERTICES_MAX	8	/* 1ポリゴン当たりの最大頂点数 */
/*}}clipren3.cより*/

typedef struct _MESHLOAD {
	XML_Parser parser;
	//
	int stat;		/* MESHLOAD_STAT_* */
	int iv;
	int ic;
	int if_;
	int ifv;
	//
	MESH m;
	vector v[255/*nv*/];
	TCOORD c[255/*nc*/];
	struct {
		FACE f;
		FACEVERTEX fv[N_VERTICES_MAX/*nfv*/];
	} f[255/*nf*/];
	//
	unsigned char buf[MESHLOAD_BUFLEN];
} MESHLOAD;

static void
mesh_load_start_element(void* user_data, const char* name, const char* atts[])
{
	MESHLOAD* ml = user_data;

	switch(ml->stat) {
	case MESHLOAD_STAT_MESH_START:
		if(strcasecmp(name, "mesh") ||
		   !atts[0] || strcasecmp(atts[0], "nv") || !atts[1] ||
		   !atts[2] || strcasecmp(atts[2], "nc") || !atts[3] ||
		   !atts[4] || strcasecmp(atts[4], "nf") || !atts[5] ||
		    atts[6]) {
			DIE();
		}
		ml->m.nv = atoi(atts[1]);
		ml->m.nc = atoi(atts[3]);
		ml->m.nf = atoi(atts[5]);
		if(ml->m.nv) {        /* 頂点座標有り */
			ml->stat = MESHLOAD_STAT_VECTOR_START;
			ml->iv   = 0;
		} else if(ml->m.nc) { /* テクスチャ座標有り */
			ml->stat = MESHLOAD_STAT_TCOORD_START;
			ml->ic   = 0;
		} else if(ml->m.nf) { /* フェイス有り */
			ml->stat = MESHLOAD_STAT_FACE_START;
			ml->if_  = 0;
		} else {              /* 空メッシュ */
			ml->stat = MESHLOAD_STAT_MESH_END;
		}
		break;

	case MESHLOAD_STAT_VECTOR_START:
		if(strcasecmp(name, "vector") ||
		   !atts[0] || strcasecmp(atts[0], "x") || !atts[1] ||
		   !atts[2] || strcasecmp(atts[2], "y") || !atts[3] ||
		   !atts[4] || strcasecmp(atts[4], "z") || !atts[5] ||
		    atts[6]) {
			DIE();
		}
		ml->v[ml->iv].x = (fixed)atoi(atts[1]);
		ml->v[ml->iv].y = (fixed)atoi(atts[3]);
		ml->v[ml->iv].z = (fixed)atoi(atts[5]);
		ml->stat = MESHLOAD_STAT_VECTOR_END;
		break;

	case MESHLOAD_STAT_TCOORD_START:
		if(strcasecmp(name, "tcoord") ||
		   !atts[0] || strcasecmp(atts[0], "s") || !atts[1] ||
		   !atts[2] || strcasecmp(atts[2], "t") || !atts[3] ||
		    atts[4]) {
			DIE();
		}
		ml->c[ml->ic].s = atoi(atts[1]);
		ml->c[ml->ic].t = atoi(atts[3]);
		ml->stat = MESHLOAD_STAT_TCOORD_END;
		break;

	case MESHLOAD_STAT_FACE_START:
		if(strcasecmp(name, "face") ||
		   !atts[0] || strcasecmp(atts[0], "nfv") || !atts[1] ||
		   !atts[2] || strcasecmp(atts[2], "c"  ) || !atts[3] ||
		    atts[4]) {
			DIE();
		}
		ml->f[ml->if_].f.nfv = atoi(atts[1]);
		ml->f[ml->if_].f.c   = atoi(atts[3]);
		if(ml->f[ml->if_].f.nfv) { /* フェイス頂点有り */
			if(ml->f[ml->if_].f.nfv > N_VERTICES_MAX) {
				DIE();
			}
			ml->stat = MESHLOAD_STAT_FACEVERTEX_START;
			ml->ifv  = 0;
		} else {                   /* 空フェイス */
			ml->stat = MESHLOAD_STAT_FACE_END;
		}
		break;

	case MESHLOAD_STAT_FACEVERTEX_START:
		if(strcasecmp(name, "facevertex") ||
		   !atts[0] || strcasecmp(atts[0], "iv") || !atts[1] ||
		   !atts[2] || strcasecmp(atts[2], "ic") || !atts[3] ||
		    atts[4]) {
			DIE();
		}
		ml->f[ml->if_].fv[ml->ifv].iv = atoi(atts[1]);
		ml->f[ml->if_].fv[ml->ifv].ic = atoi(atts[3]);
		ml->stat = MESHLOAD_STAT_FACEVERTEX_END;
		break;

	default:
		DIE();
	}
}

static void
mesh_load_end_element(void* user_data, const char* name)
{
	MESHLOAD* ml = user_data;

	switch(ml->stat) {
	case MESHLOAD_STAT_MESH_END:
		if(strcasecmp(name, "mesh")) {
			DIE();
		}
		ml->stat = MESHLOAD_STAT_EOF;
		break;

	case MESHLOAD_STAT_VECTOR_END:
		if(strcasecmp(name, "vector")) {
			DIE();
		}
		if(++ml->iv < ml->m.nv) { /* 頂点座標継続 */
			ml->stat = MESHLOAD_STAT_VECTOR_START;
		} else if(ml->m.nc) {     /* テクスチャ座標有り */
			ml->stat = MESHLOAD_STAT_TCOORD_START;
			ml->ic   = 0;
		} else if(ml->m.nf) {     /* フェイス有り */
			ml->stat = MESHLOAD_STAT_FACE_START;
			ml->if_  = 0;
		} else {                  /* メッシュ終了 */
			ml->stat = MESHLOAD_STAT_MESH_END;
		}
		break;

	case MESHLOAD_STAT_TCOORD_END:
		if(strcasecmp(name, "tcoord")) {
			DIE();
		}
		if(++ml->ic < ml->m.nc) { /* テクスチャ座標継続 */
			ml->stat = MESHLOAD_STAT_TCOORD_START;
		} else if(ml->m.nf) {     /* フェイス有り */
			ml->stat = MESHLOAD_STAT_FACE_START;
			ml->if_ = 0;
		} else {                  /* メッシュ終了 */
			ml->stat = MESHLOAD_STAT_MESH_END;
		}
		break;

	case MESHLOAD_STAT_FACE_END:
		if(strcasecmp(name, "face")) {
			DIE();
		}
		if(++ml->if_ < ml->m.nf) { /* フェイス継続 */
			ml->stat = MESHLOAD_STAT_FACE_START;
		} else {                   /* メッシュ終了 */
			ml->stat = MESHLOAD_STAT_MESH_END;
		}
		break;

	case MESHLOAD_STAT_FACEVERTEX_END:
		if(strcasecmp(name, "facevertex")) {
			DIE();
		}
		if(++ml->ifv < ml->f[ml->if_].f.nfv) { /* フェイス頂点継続 */
			ml->stat = MESHLOAD_STAT_FACEVERTEX_START;
		} else {                               /* メッシュ終了 */
			ml->stat = MESHLOAD_STAT_FACE_END;
		}
		break;

	default:
		DIE();
	}
}

MESH*
mesh_load(const char* filename)
{
	int result;
	int size;
	int done;
	FILE* fp;
	MESHLOAD* ml;
	MESH* m;
	unsigned char* p;

	/* ファイルを開きます。 */
	fp = fopen(filename, "rt");
	if(!fp) {
		DIE();
	}

	/* MESHLOAD構造体のメモリを確保します。 */
	ml = calloc(1, sizeof(MESHLOAD));
	if(!ml) {
		DIE();
	}

	/* XMLパーサを作成します。 */
	ml->parser = XML_ParserCreate(NULL);
	if(!ml->parser) {
		DIE();
	}

	/* ユーザデータを設定します。 */
	XML_SetUserData(ml->parser, ml);

	/* 要素ハンドラを設定します。 */
	XML_SetElementHandler(ml->parser, mesh_load_start_element, mesh_load_end_element);

	do {
		/* ファイルを読み込みます。 */
		size = fread(ml->buf, 1, MESHLOAD_BUFLEN, fp);

		/* ファイル終端に達したか調べます。 */
		done = feof(fp);

		/* 読み込んだ文字列をパースします。 */
		result = XML_Parse(ml->parser, ml->buf, size, done);
		if(result == XML_STATUS_ERROR) {
			DIE();
		}
	} while(!done); /* ファイル終端に達するまで */

	/* メッシュ全体のサイズを求めます。 */
	size =  sizeof(MESH);
	size += sizeof(vector) * ml->m.nv;
	size += sizeof(TCOORD) * ml->m.nc;
	for(ml->if_ = 0; ml->if_ < ml->m.nf; ml->if_++) {
		size += sizeof(FACE);
		size += sizeof(FACEVERTEX) * ml->f[ml->if_].f.nfv;
	}

	/* メッシュ全体のメモリを確保します。 */
	m = calloc(size, 1);
	if(!m) {
		DIE();
	}

	/* メッシュ全体を組み立てます。 */
	p = (unsigned char*)m;
	memcpy(p, &ml->m, sizeof(MESH));
	       p +=       sizeof(MESH);
	memcpy(p, ml->v, sizeof(vector) * ml->m.nv);
	       p +=      sizeof(vector) * ml->m.nv;
	memcpy(p, ml->c, sizeof(TCOORD) * ml->m.nc);
	       p +=      sizeof(TCOORD) * ml->m.nc;
	for(ml->if_ = 0; ml->if_ < ml->m.nf; ml->if_++) {
		memcpy(p, &ml->f[ml->if_].f, sizeof(FACE));
		       p +=                  sizeof(FACE);
		memcpy(p, ml->f[ml->if_].fv, sizeof(FACEVERTEX) * ml->f[ml->if_].f.nfv);
		       p +=                  sizeof(FACEVERTEX) * ml->f[ml->if_].f.nfv;
	}

	/* XMLパーサを削除します。 */
	XML_ParserFree(ml->parser);

	/* MESHLOAD構造体のメモリを開放します。 */
	free(ml);

	/* ファイルを閉じます。 */
	fclose(fp);

	return m;
}

/*****************************************************************************
 *	wire_load
 *****************************************************************************/

#define WIRELOAD_STAT_WIRE_START	0
#define WIRELOAD_STAT_WIRE_END		1
#define WIRELOAD_STAT_VECTOR_START	2
#define WIRELOAD_STAT_VECTOR_END	3
#define WIRELOAD_STAT_WSEG_START	4
#define WIRELOAD_STAT_WSEG_END		5
#define WIRELOAD_STAT_EOF		-1

#define WIRELOAD_BUFLEN	64	/* 読み込みバッファ長 (調整可) */

typedef struct _WIRELOAD {
	XML_Parser parser;
	//
	int stat;	/* WIRELOAD_STAT_* */
	int iv;
	int is;
	//
	WIRE* w;	/* <mesh>と違って、先頭の<wire>タグだけでサイズが決定できます。 */
	//		/* 従って、MESHLOAD構造体にあるような中間バッファは不要であり、 */
	//		/* <wire>タグ読み込み時に、全体のメモリブロックを確保できます。 */
	//
	vector* v;	/* MESHLOAD.wに確保したメモリブロックの中をポイントしています。 */
	WSEG* s;	/* MESHLOAD.wに確保したメモリブロックの中をポイントしています。 */
	//
	unsigned char buf[WIRELOAD_BUFLEN];
} WIRELOAD;

static void
wire_load_start_element(void* user_data, const char* name, const char* atts[])
{
	WIRELOAD* wl = user_data;
	int nv;
	int ns;

	switch(wl->stat) {
	case WIRELOAD_STAT_WIRE_START:
		if(strcasecmp(name, "wire") ||
		   !atts[0] || strcasecmp(atts[0], "nv") || !atts[1] ||
		   !atts[2] || strcasecmp(atts[2], "ns") || !atts[3] ||
		    atts[4]) {
			DIE();
		}
		nv = atoi(atts[1]);
		ns = atoi(atts[3]);
		wl->w = calloc(sizeof(WIRE) + sizeof(vector) * nv + sizeof(WSEG) * ns, 1);
		if(!wl->w) {
			DIE();
		}
		wl->w->nv = nv;
		wl->w->ns = ns;
		wl->v = (vector*)(wl->w + 1 );
		wl->s = (  WSEG*)(wl->v + nv);
		if(wl->w->nv) {        /* 頂点座標有り */
			wl->stat = WIRELOAD_STAT_VECTOR_START;
			wl->iv   = 0;
		} else if(wl->w->ns) { /* セグメント有り */
			wl->stat = WIRELOAD_STAT_WSEG_START;
			wl->is   = 0;
		} else {               /* 空ワイヤフレーム */
			wl->stat = WIRELOAD_STAT_WIRE_END;
		}
		break;

	case WIRELOAD_STAT_VECTOR_START:
		if(strcasecmp(name, "vector") ||
		   !atts[0] || strcasecmp(atts[0], "x") || !atts[1] ||
		   !atts[2] || strcasecmp(atts[2], "y") || !atts[3] ||
		   !atts[4] || strcasecmp(atts[4], "z") || !atts[5] ||
		    atts[6]) {
			DIE();
		}
		wl->v[wl->iv].x = (fixed)atoi(atts[1]);
		wl->v[wl->iv].y = (fixed)atoi(atts[3]);
		wl->v[wl->iv].z = (fixed)atoi(atts[5]);
		wl->stat = WIRELOAD_STAT_VECTOR_END;
		break;

	case WIRELOAD_STAT_WSEG_START:
		if(strcasecmp(name, "wseg") ||
		   !atts[0] || strcasecmp(atts[0], "iv0") || !atts[1] ||
		   !atts[2] || strcasecmp(atts[2], "iv1") || !atts[3] ||
		    atts[4]) {
			DIE();
		}
		wl->s[wl->is].iv0 = atoi(atts[1]);
		wl->s[wl->is].iv1 = atoi(atts[3]);
		wl->stat = WIRELOAD_STAT_WSEG_END;
		break;

	default:
		DIE();
	}
}

static void
wire_load_end_element(void* user_data, const char* name)
{
	WIRELOAD* wl = user_data;

	switch(wl->stat) {
	case WIRELOAD_STAT_WIRE_END:
		if(strcasecmp(name, "wire")) {
			DIE();
		}
		wl->stat = WIRELOAD_STAT_EOF;
		break;

	case WIRELOAD_STAT_VECTOR_END:
		if(strcasecmp(name, "vector")) {
			DIE();
		}
		if(++wl->iv < wl->w->nv) { /* 頂点座標継続 */
			wl->stat = WIRELOAD_STAT_VECTOR_START;
		} else if(wl->w->ns) {     /* セグメント有り */
			wl->stat = WIRELOAD_STAT_WSEG_START;
			wl->is   = 0;
		} else {                   /* ワイヤフレーム終了 */
			wl->stat = WIRELOAD_STAT_WIRE_END;
		}
		break;

	case WIRELOAD_STAT_WSEG_END:
		if(strcasecmp(name, "wseg")) {
			DIE();
		}
		if(++wl->is < wl->w->ns) { /* セグメント継続 */
			wl->stat = WIRELOAD_STAT_WSEG_START;
		} else {                   /* ワイヤフレーム終了 */
			wl->stat = WIRELOAD_STAT_WIRE_END;
		}
		break;

	default:
		DIE();
	}
}

WIRE*
wire_load(const char* filename)
{
	int result;
	int size;
	int done;
	FILE* fp;
	WIRELOAD* wl;
	WIRE* w;

	/* ファイルを開きます。 */
	fp = fopen(filename, "rt");
	if(!fp) {
		DIE();
	}

	/* WIRELOAD構造体のメモリを確保します。 */
	wl = calloc(1, sizeof(WIRELOAD));
	if(!wl) {
		DIE();
	}

	/* XMLパーサを作成します。 */
	wl->parser = XML_ParserCreate(NULL);
	if(!wl->parser) {
		DIE();
	}

	/* ユーザデータを設定します。 */
	XML_SetUserData(wl->parser, wl);

	/* 要素ハンドラを設定します。 */
	XML_SetElementHandler(wl->parser, wire_load_start_element, wire_load_end_element);

	do {
		/* ファイルを読み込みます。 */
		size = fread(wl->buf, 1, WIRELOAD_BUFLEN, fp);

		/* ファイル終端に達したか調べます。 */
		done = feof(fp);

		/* 読み込んだ文字列をパースします。 */
		result = XML_Parse(wl->parser, wl->buf, size, done);
		if(result == XML_STATUS_ERROR) {
			DIE();
		}
	} while(!done); /* ファイル終端に達するまで */

	/* ワイヤフレーム全体のメモリを取得します。 */
	w = wl->w;

	/* XMLパーサを削除します。 */
	XML_ParserFree(wl->parser);

	/* WIRELOAD構造体のメモリを開放します。 */
	free(wl);

	/* ファイルを閉じます。 */
	fclose(fp);

	return w;
}

