/*
 *	bmlcnv - BulletML XML to C converter
 *	Copyright (C) 2007 Naoyuki Sawa
 *
 *	* Mon Feb 26 16:34:19 JST 2007 Naoyuki Sawa
 *	- 1st リリース。
 *	* Tue May 01 12:37:06 JST 2007 Naoyuki Sawa
 *	- 複数のXMLをまとめてコンバートできるよう、変更しました。
 *	- 内容の一致するオブジェクトを共有し、メモリ節約しました。
 *	- 複数のXMLをまとめてコンバートするので、区別のためのプレフィクスはもう不要です。
 *	  従って、起動時引数からプレフィクスは無くなりました。
 *	- 以上のように大きな変更を行ったので、念のために、旧版を「keep/bmlcnv-20070226.zip」に保存しました。
 */
#include "app.h"

/* [NOTE]
 * * コマンドライン引数のワイルドカードを展開するために、
 *   [プロジェクトの設定]->[リンク]->[オブジェクト/ライブラリモジュール]に
 *   「setargv.obj」を追加しています。
 */

#define VERSION	"20070501"

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

const char* get_node_symbol(BulletMLNode* node);
const char* get_value_symbol(BulletMLValue* value);
const char* get_label_symbol(StdString* label);
//
void reg_node(BulletMLNode* node);
void reg_value(BulletMLValue* value);
void reg_label(StdString* label);
//
void declare_node(BulletMLNode* node);
void declare_value(BulletMLValue* value);
void declare_label(StdString* label);
//
void define_node(BulletMLNode* node);
void define_value(BulletMLValue* value);
void define_label(StdString* label);
//
int compare_node(BulletMLNode* a, BulletMLNode* b);
int compare_value(BulletMLValue* a, BulletMLValue* b);
int compare_label(StdString* a, StdString* b);
//
int find_node(BulletMLNode* node);
int find_value(BulletMLValue* value);
int find_label(StdString* label);

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

/*{{clipfix.c,framfix.cより*/
fixed frnd(int* seed) {
	return (fixed)(rnd32(seed) & FRACT_MASK);
}
fixed fmul(fixed a, fixed b) {
	__int64 aa, bb, cc;
	aa = (int)a;
	bb = (int)b;
	cc = (aa * bb) >> FRACT_BITS;
	return (fixed)(int)cc;
}
fixed fdiv(fixed a, fixed b) {
	__int64 aa, bb, cc;
	aa = (int)a;
	bb = (int)b;
	cc = (aa << FRACT_BITS) / bb;
	return (fixed)(int)cc;
}
/*}}clipfix.c,framfix.cより*/

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

const char* const name_table[BULLETMLNODE_NAME_COUNT] = {
	"BULLETMLNODE_NAME_BULLETML",
	"BULLETMLNODE_NAME_BULLET",
	"BULLETMLNODE_NAME_ACTION",
	"BULLETMLNODE_NAME_FIRE",
	"BULLETMLNODE_NAME_CHANGEDIRECTION",
	"BULLETMLNODE_NAME_CHANGESPEED",
	"BULLETMLNODE_NAME_ACCEL",
	"BULLETMLNODE_NAME_WAIT",
	"BULLETMLNODE_NAME_VANISH",
	"BULLETMLNODE_NAME_REPEAT",
	"BULLETMLNODE_NAME_DIRECTION",
	"BULLETMLNODE_NAME_SPEED",
	"BULLETMLNODE_NAME_HORIZONTAL",
	"BULLETMLNODE_NAME_VERTICAL",
	"BULLETMLNODE_NAME_TERM",
	"BULLETMLNODE_NAME_TIMES",
	"BULLETMLNODE_NAME_BULLETREF",
	"BULLETMLNODE_NAME_ACTIONREF",
	"BULLETMLNODE_NAME_FIREREF",
	"BULLETMLNODE_NAME_PARAM",
};

const char* const type_table[BULLETMLNODE_TYPE_COUNT] = {
	"BULLETMLNODE_TYPE_NONE",
	"BULLETMLNODE_TYPE_VERTICAL",
	"BULLETMLNODE_TYPE_HORIZONTAL",
	"BULLETMLNODE_TYPE_AIM",
	"BULLETMLNODE_TYPE_ABSOLUTE",
	"BULLETMLNODE_TYPE_RELATIVE",
	"BULLETMLNODE_TYPE_SEQUENCE",
};

const char* const id_table[BULLETML_MAX_PARAMS + 2] = {
	"$rank",	/* -2 */
	"$rand",	/* -1 */
	"$1",		/*  0 */
	"$2",		/*  1 */
	"$3",		/*  2 */
	"$4",		/*  3 */
	"$5",		/*  4 */
	"$6",		/*  5 */
	"$7",		/*  6 */
	"$8",		/*  7 */
	"$9",		/*  8 */
};

#define MAX_BULLETML	10000	/* 調整可 */
int bulletml_count;
char* bulletml_fname[MAX_BULLETML];
BulletMLNode* bulletml_array[MAX_BULLETML];

#define MAX_NODE	10000	/* 調整可 */
int node_count;
BulletMLNode* node_array[MAX_NODE];

#define MAX_VALUE	10000	/* 調整可 */
int value_count;
BulletMLValue* value_array[MAX_VALUE];

#define MAX_LABEL	10000	/* 調整可 */
int label_count;
StdString* label_array[MAX_LABEL];

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

const char*
get_node_symbol(BulletMLNode* node)
{
	static char buf[256];
	int i = find_node(node);
	if(i < 0) {
		DIE(); /* 未登録 (有り得ません。プログラムのバグ?) */
	}
	sprintf(buf, "bulletml_node%d", i);
	return buf;
}

const char*
get_value_symbol(BulletMLValue* value)
{
	static char buf[256];
	int i = find_value(value);
	if(i < 0) {
		DIE(); /* 未登録 (有り得ません。プログラムのバグ?) */
	}
	sprintf(buf, "bulletml_value%d", i);
	return buf;
}

const char*
get_label_symbol(StdString* label)
{
	static char buf[256];
	int i = find_label(label);
	if(i < 0) {
		DIE(); /* 未登録 (有り得ません。プログラムのバグ?) */
	}
	sprintf(buf, "bulletml_label%d", i);
	return buf;
}

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

void
reg_node(BulletMLNode* node)
{
	if(find_node(node) >= 0) {
		return; /* 既に同じ内容の別オブジェクトが登録されているので、共有する */
	}
	if(node_count >= MAX_NODE) {
		DIE(); /* 多すぎ (明らかにP/ECEの許容量オーバー) */
	}
	node_array[node_count++] = node;
	//
	if(node->label) {
		reg_label(node->label);
	}
	if(node->value) {
		reg_value(node->value);
	}
	if(node->firstChild) {
		reg_node(node->firstChild);
	}
	if(node->nextSibling) {
		reg_node(node->nextSibling);
	}
}

void
reg_value(BulletMLValue* value)
{
	if(find_value(value) >= 0) {
		return; /* 既に同じ内容の別オブジェクトが登録されているので、共有する */
	}
	if(value_count >= MAX_VALUE) {
		DIE(); /* 多すぎ (明らかにP/ECEの許容量オーバー) */
	}
	value_array[value_count++] = value;
	//
	if((void*)value->vptr == &_BulletMLNumber_vtbl) {
		BulletMLNumber* number = (BulletMLNumber*)value;
		/** no job **/
	} else if((void*)value->vptr == &_BulletMLVar_vtbl) {
		BulletMLVar* var = (BulletMLVar*)value;
	} else if((void*)value->vptr == &_BulletMLUnExpr_vtbl) {
		BulletMLUnExpr* unExpr = (BulletMLUnExpr*)value;
		reg_value(unExpr->value);
	} else if((void*)value->vptr == &_BulletMLBinExpr_vtbl) {
		BulletMLBinExpr* binExpr = (BulletMLBinExpr*)value;
		reg_value(binExpr->lhs);
		reg_value(binExpr->rhs);
	} else {
		DIE(); /* 不明なBulletMLValueのサブタイプ (プログラムのバグ?) */
	}
}

void
reg_label(StdString* label)
{
	if(find_label(label) >= 0) {
		return; /* 既に同じ内容の別オブジェクトが登録されているので、共有する */
	}
	if(label_count >= MAX_LABEL) {
		DIE(); /* 多すぎ (明らかにP/ECEの許容量オーバー) */
	}
	label_array[label_count++] = label;
}

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

void
declare_node(BulletMLNode* node)
{
	printf("static BulletMLNode %s;\n", get_node_symbol(node));
}

void
declare_value(BulletMLValue* value)
{
	if((void*)value->vptr == &_BulletMLNumber_vtbl) {
		printf("static BulletMLNumber %s;\n", get_value_symbol(value));
	} else if((void*)value->vptr == &_BulletMLVar_vtbl) {
		printf("static BulletMLVar %s;\n", get_value_symbol(value));
	} else if((void*)value->vptr == &_BulletMLUnExpr_vtbl) {
		printf("static BulletMLUnExpr %s;\n", get_value_symbol(value));
	} else if((void*)value->vptr == &_BulletMLBinExpr_vtbl) {
		printf("static BulletMLBinExpr %s;\n", get_value_symbol(value));
	} else {
		DIE(); /* 不明なBulletMLValueのサブタイプ (プログラムのバグ?) */
	}
}

void
declare_label(StdString* label)
{
	printf("static StdString %s;\n", get_label_symbol(label));
}

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

void
define_node(BulletMLNode* node)
{
	printf("static BulletMLNode %s = {\n", get_node_symbol(node));
	printf("\t%s,\n", name_table[node->name]);
	printf("\t%s,\n", type_table[node->type]);
	if(node->label) {
		printf("\t&%s,\n", get_label_symbol(node->label));
	} else {
		printf("\tNULL,\n");
	}
	if(node->ref) {
		printf("\t&%s,\n", get_node_symbol(node->ref));
	} else {
		printf("\tNULL,\n");
	}
	if(node->value) {
		printf("\t(BulletMLValue*)&%s,\n", get_value_symbol(node->value));
	} else {
		printf("\tNULL,\n");
	}
	if(node->firstChild) {
		printf("\t&%s,\n", get_node_symbol(node->firstChild));
	} else {
		printf("\tNULL,\n");
	}
	if(node->nextSibling) {
		printf("\t&%s,\n", get_node_symbol(node->nextSibling));
	} else {
		printf("\tNULL,\n");
	}
	printf("};\n");
}

void
define_value(BulletMLValue* value)
{
	if((void*)value->vptr == &_BulletMLNumber_vtbl) {
		BulletMLNumber* number = (BulletMLNumber*)value;
		printf("static BulletMLNumber %s = {\n", get_value_symbol(value));
		printf("\t&_BulletMLNumber_vtbl,\n");
		printf("\t(fixed)%d, /* %f */\n", number->value, fst(number->value));
		printf("};\n");
	} else if((void*)value->vptr == &_BulletMLVar_vtbl) {
		BulletMLVar* var = (BulletMLVar*)value;
		printf("static BulletMLVar %s = {\n", get_value_symbol(value));
		printf("\t&_BulletMLVar_vtbl,\n");
		printf("\t%d, /* %s */\n", var->id, id_table[var->id + 2]);
		printf("};\n");
	} else if((void*)value->vptr == &_BulletMLUnExpr_vtbl) {
		BulletMLUnExpr* unExpr = (BulletMLUnExpr*)value;
		printf("static BulletMLUnExpr %s = {\n", get_value_symbol(value));
		printf("\t&_BulletMLUnExpr_vtbl,\n");
		printf("\t'%c',\n", unExpr->op);
		printf("\t(BulletMLValue*)&%s,\n", get_value_symbol(unExpr->value));
		printf("};\n");
	} else if((void*)value->vptr == &_BulletMLBinExpr_vtbl) {
		BulletMLBinExpr* binExpr = (BulletMLBinExpr*)value;
		printf("static BulletMLBinExpr %s = {\n", get_value_symbol(value));
		printf("\t&_BulletMLBinExpr_vtbl,\n");
		printf("\t'%c',\n", binExpr->op);
		printf("\t(BulletMLValue*)&%s,\n", get_value_symbol(binExpr->lhs));
		printf("\t(BulletMLValue*)&%s,\n", get_value_symbol(binExpr->rhs));
		printf("};\n");
	} else {
		DIE(); /* 不明なBulletMLValueのサブタイプ (プログラムのバグ?) */
	}
}

void
define_label(StdString* label)
{
	printf("static StdString %s = {\n", get_label_symbol(label));
	//printf("\t\"%s\",\n", StdString_c_str(label));
	//↓少しだけメモリ節約
	if(label->buf) {
		printf("\t\"%s\",\n", label->buf);
	} else {
		printf("\tNULL, /* \"\" */\n");
	}
	printf("};\n");
}

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

int
compare_node(BulletMLNode* a, BulletMLNode* b)
{
	if(a == b) return 0;	/* 両方ともNULLの場合も一致 */
	if(!a || !b) return 1;	/* 一方だけNULLならば不一致 */

	if(a->name != b->name) return 1;
	if(a->type != b->type) return 1;
	if(compare_label(a->label, b->label)) return 1;
	if(compare_node(a->ref, b->ref)) return 1;
	if(compare_value(a->value, b->value)) return 1;
	if(compare_node(a->firstChild, b->firstChild)) return 1;
	if(compare_node(a->nextSibling, b->nextSibling)) return 1;

	return 0;
}

int
compare_value(BulletMLValue* a, BulletMLValue* b)
{
	if(a == b) return 0;	/* 両方ともNULLの場合も一致 */
	if(!a || !b) return 1;	/* 一方だけNULLならば不一致 */

	if(((void*)a->vptr == &_BulletMLNumber_vtbl) &&
	   ((void*)b->vptr == &_BulletMLNumber_vtbl)) {
		BulletMLNumber* aa = (BulletMLNumber*)a;
		BulletMLNumber* bb = (BulletMLNumber*)b;
		if(aa->value != bb->value) return 1;
	} else if(((void*)a->vptr == &_BulletMLVar_vtbl) &&
		  ((void*)b->vptr == &_BulletMLVar_vtbl)) {
		BulletMLVar* aa = (BulletMLVar*)a;
		BulletMLVar* bb = (BulletMLVar*)b;
		if(aa->id != bb->id) return 1;
	} else if(((void*)a->vptr == &_BulletMLUnExpr_vtbl) &&
		  ((void*)b->vptr == &_BulletMLUnExpr_vtbl)) {
		BulletMLUnExpr* aa = (BulletMLUnExpr*)a;
		BulletMLUnExpr* bb = (BulletMLUnExpr*)b;
		if(aa->op != bb->op) return 1;
		if(compare_value(aa->value, bb->value)) return 1;
	} else if(((void*)a->vptr == &_BulletMLBinExpr_vtbl) &&
		  ((void*)b->vptr == &_BulletMLBinExpr_vtbl)) {
		BulletMLBinExpr* aa = (BulletMLBinExpr*)a;
		BulletMLBinExpr* bb = (BulletMLBinExpr*)b;
		if(aa->op != bb->op) return 1;
		if(compare_value(aa->lhs, bb->lhs)) return 1;
		if(compare_value(aa->rhs, bb->rhs)) return 1;
	} else {
		return 1; /* サブタイプが異なるので明らかに不一致 */
	}

	return 0;
}

int
compare_label(StdString* a, StdString* b)
{
	if(a == b) return 0;	/* 両方ともNULLの場合も一致 */
	if(!a || !b) return 1;	/* 一方だけNULLならば不一致 */

	return strcmp(StdString_c_str(a), StdString_c_str(b));
}

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

int
find_node(BulletMLNode* node)
{
	int i;
	for(i = 0; i < node_count; i++) {
		if(!compare_node(node, node_array[i])) {
			return i;
		}
	}
	return -1;
}

int
find_value(BulletMLValue* value)
{
	int i;
	for(i = 0; i < value_count; i++) {
		if(!compare_value(value, value_array[i])) {
			return i;
		}
	}
	return -1;
}

int
find_label(StdString* label)
{
	int i;
	for(i = 0; i < label_count; i++) {
		if(!compare_label(label, label_array[i])) {
			return i;
		}
	}
	return -1;
}

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

void
usage()
{
	fprintf(stderr, "bmlcnv - BulletML XML to C converter (%s)\n", VERSION);
	fprintf(stderr, "Copyright (C) 2007 Naoyuki Sawa\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "USAGE:\n");
	fprintf(stderr, "    bmlcnv input1.xml [input2.xml [...]] > output.c\n");
	exit(1);
}

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

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

	DEBUG_INIT();

	/* <bulletml>を全部読み込んで、内容の一致するオブジェクトを重複しないように登録します。 */
	if(argc < 1 + 1) {
		usage();
	}
	for(i = 1; i < argc; i++) {
		if(bulletml_count >= MAX_BULLETML) {
			DIE(); /* 多すぎ (明らかにP/ECEの許容量オーバー) */
		}
		bulletml_fname[bulletml_count] = argv[i];
		bulletml_array[bulletml_count] = BulletML_load(bulletml_fname[bulletml_count]);
		reg_node(bulletml_array[bulletml_count]);
		bulletml_count++;
	}

	/* アプリケーションヘッダをインクルードします。(不要ならば、手作業で削除してください。) */
	printf("#include \"app.h\"\n");

	/* 前方参照のために、まず、登録されたオブジェクトをすべて宣言します。 */
	for(i = 0; i < node_count; i++) {
		declare_node(node_array[i]);
	}
	for(i = 0; i < value_count; i++) {
		declare_value(value_array[i]);
	}
	for(i = 0; i < label_count; i++) {
		declare_label(label_array[i]);
	}

	/* 登録されたオブジェクトを定義します。 */
	for(i = 0; i < node_count; i++) {
		define_node(node_array[i]);
	}
	for(i = 0; i < value_count; i++) {
		define_value(value_array[i]);
	}
	for(i = 0; i < label_count; i++) {
		define_label(label_array[i]);
	}

	/* <bulletml>テーブルを定義します。 */
	printf("/* extern */ BulletMLNode* bulletml_table[%d] = {\n", bulletml_count);
	for(i = 0; i < bulletml_count; i++) {
		printf("\t&%s, /* %s */\n", get_node_symbol(bulletml_array[i]), bulletml_fname[i]);
	}
	printf("};\n");

	/* <bulletml>を開放します。 */
	for(i = 0; i < bulletml_count; i++) {
		BulletMLNode_delete(bulletml_array[i]);
	}

	return 0;
}

