#include "../../clip.h"

void usage();
void cmdline(int argc, char* argv[]);
int readline(FILE* fp);
void writec(FILE* fp, void* p, int len);
void readmesh(FILE* fp);
void writemesh(FILE* fp);

/****************************************************************************
 *	
 ****************************************************************************/

#define NULDEV	"NUL"
FILE* msg = stdout;

char out_label[32];
char out_prefix[32];

char in_path[_MAX_PATH];
char out_path[_MAX_PATH];
char drive[_MAX_DRIVE];
char dir[_MAX_DIR];
char fname[_MAX_FNAME];
char ext[_MAX_EXT];

#define LINEBUF_SIZE	1024
char linebuf[LINEBUF_SIZE];
char i_line;

#define N_TOKENS_MAX	32
char* tokens[N_TOKENS_MAX];
int n_tokens;

#define NV_MAX	255
#define NC_MAX	255
#define NF_MAX	255
#define NFV_MAX	255

MESH mesh;
VECTOR vectors[NV_MAX];
TCOORD tcoords[NC_MAX];
FACE faces[NF_MAX];
FACEVERTEX facevertices[NF_MAX][NFV_MAX];

/****************************************************************************
 *	main
 ****************************************************************************/

int
main(int argc, char* argv[])
{
	FILE* fp;

	cmdline(argc, argv);

	fp = fopen(in_path, "rb");
	if(fp == NULL) {
		fprintf(stderr, "### %s open error\n", in_path);
		exit(1);
	}
	readmesh(fp);
	fclose(fp);

	fp = fopen(out_path, "wt");
	if(fp == NULL) {
		fprintf(stderr, "### %s create error\n", out_path);
		exit(1);
	}
	writemesh(fp);
	fclose(fp);

	return 0;
}

/****************************************************************************
 *	usage
 ****************************************************************************/

void
usage()
{
	fprintf(stderr, "USAGE:\n");
	fprintf(stderr, "	dmeshcnv [options] <filename.msh>\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "OPTIONS:\n");
	fprintf(stderr, "	-o<fname>  filename\n");
	fprintf(stderr, "	-l<label>  label name\n");
	fprintf(stderr, "	-p<prefix> label prefix\n");
	fprintf(stderr, "	-q         quiet\n");
	exit(1);
}

/****************************************************************************
 *	cmdline
 ****************************************************************************/

void
cmdline(int argc, char* argv[])
{
	int i;
	char* opt;

	for(i = 1; i < argc; i++) {
		opt = argv[i];
		if(opt[0] != '-') {
			if(strlen(in_path) != 0) usage();
			strcpy(in_path, opt);
		} else {
			switch(opt[1]) {
			case 'o':
				if(strlen(out_path) != 0) usage();
				strcpy(out_path, &opt[2]);
				break;
			case 'l':
				if(strlen(out_label) != 0) usage();
				strcpy(out_label, &opt[2]);
				break;
			case 'p':
				if(strlen(out_prefix) != 0) usage();
				strcpy(out_prefix, &opt[2]);
				break;
			case 'q':
				msg = fopen(NULDEV, "wt");
				break;
			default:
				usage();
			}
		}
	}

	if(strlen(in_path) == 0) usage();
	_splitpath(in_path, drive, dir, fname, ext);
	if(strlen(ext) == 0) {
		_makepath(in_path, drive, dir, fname, ".msh");
	}
	if(strlen(out_path) == 0) {
		_splitpath(in_path, drive, dir, fname, ext);
		_makepath(out_path, drive, dir, fname, ".c");
	}
	if(strlen(out_label) == 0) {
		_splitpath(out_path, drive, dir, fname, ext);
		for(i = 0; fname[i] != '\0'; i++) {
			out_label[i] = toupper(fname[i]);
		}
	}
	if(strlen(out_prefix) != 0) {
		strcat(out_prefix, out_label);
		strcpy(out_label, out_prefix);
	}

	fprintf(msg, "IN  FILE : %s\n", in_path);
	fprintf(msg, "OUT FILE : %s\n", out_path);
	fprintf(msg, "OUT LABEL: %s\n", out_label);
}

/****************************************************************************
 *	readline
 ****************************************************************************/

#define DELIM	" \t\r\n,()"

int
readline(FILE* fp)
{
	char* p;

	n_tokens = 0;
	do {
		/* 一行読みます。 */
		if(fgets(linebuf, sizeof linebuf, fp) == NULL) return 0; /* EOF */
		i_line++;

		/* コメント以降を削除。 */
		p = strchr(linebuf, '#');
		if(p != NULL) *p = '\0';

		/* トークン分割。 */
		p = strtok(linebuf, DELIM);
		while(p != NULL) {
			if(n_tokens == N_TOKENS_MAX) {
				fprintf(stderr, "### (%d) too many tokens\n", i_line);
				exit(1);
			}
			tokens[n_tokens] = p;
			n_tokens++;
			p = strtok(NULL, DELIM);
		}
	} while(n_tokens == 0); /* 空行なら飛ばします。 */

	return 1;
}

/****************************************************************************
 *	writec
 ****************************************************************************/

void
writec(FILE* fp, void* p, int len)
{
	int i;
	for(i = 0; i < len; i++) {
		fprintf(fp, "0x%02x,", ((unsigned char*)p)[i]);
	}
}

/****************************************************************************
 *	readmesh
 ****************************************************************************/

void
readmesh(FILE* fp)
{
	int i, n;
	char* p;

	/* 最初は必ずVECTORセクション。 */
	if(!readline(fp)) goto ERR;
	if(n_tokens != 1 || _strcmpi(tokens[0], "VECTOR") != 0) goto ERR;

	/* VECTORセクションの読み込み。 */
	while(readline(fp)) {
		if(n_tokens == 1) break;
		if(n_tokens != 3) goto ERR;

		if(mesh.nv == NV_MAX) goto ERR;

		/* X座標 */
		vectors[mesh.nv].x = strtod(tokens[0], &p);
		if(*p != '\0') goto ERR;

		/* Y座標 */
		vectors[mesh.nv].y = strtod(tokens[1], &p);
		if(*p != '\0') goto ERR;

		/* Z座標 */
		vectors[mesh.nv].z = strtod(tokens[2], &p);
		if(*p != '\0') goto ERR;

		mesh.nv++;
	}
	if(mesh.nv == 0) goto ERR; /* 空セクション */
	if(n_tokens == 1 && _strcmpi(tokens[0], "TCOORD") == 0) goto L10;
	if(n_tokens == 1 && _strcmpi(tokens[0], "FACE") == 0) goto L20;
	goto ERR;

L10:
	/* TCOORDセクションの読み込み。 */
	while(readline(fp)) {
		if(n_tokens == 1) break;
		if(n_tokens != 2) goto ERR;

		if(mesh.nc == NC_MAX) goto ERR;

		/* S座標 */
		n = strtol(tokens[0], &p, 0);
		if(*p != '\0') goto ERR;
		if(n < 0 || 255 < n) goto ERR;
		tcoords[mesh.nc].s = n;

		/* T座標 */
		n = strtol(tokens[1], &p, 0);
		if(*p != '\0') goto ERR;
		if(n < 0 || 255 < n) goto ERR;
		tcoords[mesh.nc].t = n;

		mesh.nc++;
	}
	if(mesh.nc == 0) goto ERR; /* 空セクション */
	if(n_tokens == 1 && _strcmpi(tokens[0], "FACE") == 0) goto L20;
	goto ERR;

L20:
	/* FACEセクションの読み込み。 */
	while(readline(fp)) {
		if(mesh.nf == NF_MAX) goto ERR;

		/* テクスチャなし */
		if(mesh.nc == 0) {
			if(n_tokens < 1 + 1 * 3) goto ERR;

			/* カラー */
			n = strtol(tokens[0], &p, 0);
			if(*p != '\0') goto ERR;
			if((n & ~0x33) != 0) goto ERR;
			faces[mesh.nf].c = n;

			/* フェイス頂点 */
			for(i = 1; i < n_tokens; i += 1) {
				if(faces[mesh.nf].nfv == NFV_MAX) goto ERR;

				/* VECTORインデクス */
				n = strtol(tokens[i], &p, 0);
				if(*p != '\0') goto ERR;
				if(n < 0 || mesh.nv <= n) goto ERR;
				facevertices[mesh.nf][faces[mesh.nf].nfv].iv = n;

				faces[mesh.nf].nfv++;
			}

		/* テクスチャあり */
		} else {
			if(n_tokens < 1 + 2 * 3) goto ERR;
			if((n_tokens - 1) % 2 != 0) goto ERR;

			/* カラー */
			n = strtol(tokens[0], &p, 0);
			if(*p != '\0') goto ERR;
			if((n & ~0x33) != 0) goto ERR;
			faces[mesh.nf].c = n;

			for(i = 1; i < n_tokens; i += 2) {
				if(faces[mesh.nf].nfv == NFV_MAX) goto ERR;

				/* VECTORインデクス */
				n = strtol(tokens[i + 0], &p, 0);
				if(*p != '\0') goto ERR;
				if(n < 0 || mesh.nv <= n) goto ERR;
				facevertices[mesh.nf][faces[mesh.nf].nfv].iv = n;

				/* TCOORDインデクス */
				n = strtol(tokens[i + 1], &p, 0);
				if(*p != '\0') goto ERR;
				if(n < 0 || mesh.nc <= n) goto ERR;
				facevertices[mesh.nf][faces[mesh.nf].nfv].ic = n;

				faces[mesh.nf].nfv++;
			}
		}

		mesh.nf++;
	}
	if(mesh.nf == 0) goto ERR; /* 空セクション */

	return;
ERR:
	fprintf(stderr, "### (%d) data error\n", i_line);
	exit(1);
}

/****************************************************************************
 *	writemesh
 ****************************************************************************/

void
writemesh(FILE* fp)
{
	int i;

	/* C言語配列の開始。 */
	fprintf(fp, "const unsigned char %s[] = {\n", out_label);

	/* メッシュヘッダ */
	writec(fp, &mesh, sizeof mesh);
	fprintf(fp, "\n");

	/* VECTOR */
	fprintf(fp, "/*VECTOR*/\n");
	for(i = 0; i < mesh.nv; i++) {
		writec(fp, &vectors[i], sizeof(VECTOR));
		fprintf(fp, "\n");
	}

	/* TCOORD */
	if(mesh.nc != 0) {
		fprintf(fp, "/*TCOORD*/\n");
		for(i = 0; i < mesh.nc; i++) {
			writec(fp, &tcoords[i], sizeof(TCOORD));
			fprintf(fp, "\n");
		}
	}

	/* FACE */
	fprintf(fp, "/*FACE*/\n");
	for(i = 0; i < mesh.nf; i++) {
		writec(fp, &faces[i], sizeof(FACE));
		writec(fp, facevertices[i], sizeof(FACEVERTEX) * faces[i].nfv);
		fprintf(fp, "\n");
	}

	/* C言語配列の終了。 */
	fprintf(fp, "};\n");
	fclose(fp);
}
