#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cassert>
#include <new>

#include <rw.h>
#include <args.h>
#include <src/gtaplg.h>

using namespace std;
using namespace rw;

static struct {
	char *str;
	uint32 val;
} platforms[] = {
	{ "mobile", PLATFORM_OGL },
	{ "ps2",    PLATFORM_PS2 },
	{ "xbox",   PLATFORM_XBOX },
	{ "d3d8",   PLATFORM_D3D8 },
	{ "d3d9",   PLATFORM_D3D9 },
	{ NULL, 0 }
};

char *argv0;

void
usage(void)
{
	fprintf(stderr, "usage: %s [-u] [-i] [-s] [-v version] [-o platform] in.dff [out.dff]\n", argv0);
	fprintf(stderr, "\t-u uninstance\n");
	fprintf(stderr, "\t-i instance\n");
	fprintf(stderr, "\t-v RW version, e.g. 33004 for 3.3.0.4\n");
	fprintf(stderr, "\t-o output platform. ps2, xbox, mobile, d3d8, d3d9\n");
	exit(1);
}

void
dumpUVAnim(Animation *anim)
{
	UVAnimCustomData *cust = (UVAnimCustomData*)anim->customData;
	printf(" %s", cust->name);
	for(int i = 0; i < 8; i++)
		printf(" %d", cust->nodeToUVChannel[i]);
	printf("\n %d %x\n", anim->numFrames, anim->interpInfo->id);
	UVAnimKeyFrame *kf = (UVAnimKeyFrame*)anim->keyframes;
	for(int i = 0; i < anim->numFrames; i++){
		printf(" %f\n", kf->time);
		printf("  %f %f %f %f %f %f\n", kf->uv[0], kf->uv[1], kf->uv[2], kf->uv[3], kf->uv[4], kf->uv[5]);
		kf++;
	}
}

void
dumpFrameHier(Frame *frame, int ind = 0)
{
	for(int i = 0; i < ind; i++)
		printf("  ");
	char *name = gta::getNodeName(frame);
	HAnimData *hanim = HAnimData::get(frame);
	printf("*%s %d %d %s\n", name[0] ? name : "---", frame->objectList.count(), hanim->id, hanim->hierarchy ? "HIERARCHY" : "");
	if(hanim->hierarchy){
		HAnimHierarchy *h = hanim->hierarchy;
		for(int i = 0; i < h->numNodes; i++){
			name = h->nodeInfo[i].frame ? gta::getNodeName(h->nodeInfo[i].frame) : "";
			printf("\t\t%d %d\t%p %s\n", h->nodeInfo[i].id, h->nodeInfo[i].flags, h->nodeInfo[i].frame, name);
		}
	}
	for(Frame *child = frame->child;
	    child; child = child->next)
		dumpFrameHier(child, ind+1);
}

int
main(int argc, char *argv[])
{
	gta::attachPlugins();

	rw::version = 0;
//	rw::version = 0x34003;
//	rw::version = 0x33002;
//	rw::platform = rw::PLATFORM_PS2;
//	rw::platform = rw::PLATFORM_OGL;
//	rw::platform = rw::PLATFORM_XBOX;
	rw::platform = rw::PLATFORM_D3D8;
//	rw::platform = rw::PLATFORM_D3D9;

	int uninstance = 0;
	int instance = 0;
	int outplatform = rw::PLATFORM_D3D8;

	char *s;
	ARGBEGIN{
	case 'u':
		uninstance++;
		break;
	case 'i':
		instance++;
		break;
	case 'v':
		sscanf(EARGF(usage()), "%x", &rw::version);
		break;
	case 'o':
		s = EARGF(usage());
		for(int i = 0; platforms[i].str; i++){
			if(strcmp(platforms[i].str, s) == 0){
				outplatform = platforms[i].val;
				goto found;
			}
		}
		printf("unknown platform %s\n", s);
		outplatform = PLATFORM_D3D8;
	found:
		break;
	default:
		usage();
	}ARGEND;

	if(uninstance && instance){
		fprintf(stderr, "cannot both instance and uninstance, choose one!\n");
		return 1;
	}

	if(argc < 1)
		usage();

	Clump *c;
	//uint32 len;
	//uint8 *data = getFileContents(argv[0], &len);
	//assert(data != NULL);
	//StreamMemory in;
	//in.open(data, len);
	StreamFile in;
	in.open(argv[0], "rb");
	currentUVAnimDictionary = NULL;
	currentTexDictionary = TexDictionary::create();
	ChunkHeaderInfo header;
	readChunkHeaderInfo(&in, &header);
	if(header.type == ID_UVANIMDICT){
		UVAnimDictionary *dict = UVAnimDictionary::streamRead(&in);
		currentUVAnimDictionary = dict;
		readChunkHeaderInfo(&in, &header);
	}
	if(header.type != ID_CLUMP){
		in.close();
		return 0;
	}
	debugFile = argv[0];
	c = Clump::streamRead(&in);
	assert(c != NULL);
	in.close();

//	printf("%s\n", argv[arg]);

/*
	for(int32 i = 0; i < c->numAtomics; i++){
		Atomic *a = c->atomicList[i];
		Pipeline *ap = a->pipeline;
		Geometry *g = a->geometry;
		for(int32 j = 0; j < g->numMaterials; j++){
			Pipeline *mp = g->materialList[j]->pipeline;
			if(ap && mp)
				printf("%s %x %x\n", argv[arg], ap->pluginData, mp->pluginData);
		}
	}
*/
	//FORLIST(lnk, c->lights){
	//	Light *l = Light::fromClump(lnk);
	//	printf("%p %p\n", l, lnk);
	//	printf("%d %f %f %f\n", l->getType(), l->color.red, l->color.green, l->color.blue);
	//}

	HAnimHierarchy *hier = HAnimHierarchy::find(c->getFrame());
	if(hier)
		hier->attach();
	dumpFrameHier(c->getFrame());

	//if(currentUVAnimDictionary){
	//	FORLIST(lnk, currentUVAnimDictionary->animations){
	//		UVAnimDictEntry *de = UVAnimDictEntry::fromDict(lnk);
	//		Animation *anim = de->anim;
	//		dumpUVAnim(anim);
	//	}
	//}

	int32 platform = findPlatform(c);
	if(platform){
		rw::platform = platform;
		switchPipes(c, rw::platform);
	}

	if(uninstance)
		FORLIST(lnk, c->atomics){
			Atomic *a = Atomic::fromClump(lnk);
			ObjPipeline *p = a->getPipeline();
			p->uninstance(a);
			if(outplatform != PLATFORM_PS2)
				ps2::unconvertADC(a->geometry);
		}

	rw::platform = outplatform;
	switchPipes(c, rw::platform);

	if(instance)
		FORLIST(lnk, c->atomics){
			Atomic *a = Atomic::fromClump(lnk);
			ObjPipeline *p = a->getPipeline();
			p->instance(a);
			if(outplatform != PLATFORM_PS2)
				ps2::convertADC(a->geometry);
		}

	if(rw::version == 0){
		rw::version = header.version;
		rw::build = header.build;
	}

	StreamFile out;
	if(argc > 1)
		assert(out.open(argv[1], "wb"));
	else
		assert(out.open("out.dff", "wb"));
	if(currentUVAnimDictionary)
		currentUVAnimDictionary->streamWrite(&out);
	c->streamWrite(&out);
	out.close();

//	data = new rw::uint8[1024*1024];
//	rw::StreamMemory out;
//	out.open(data, 0, 1024*1024);
//	if(currentUVAnimDictionary)
//		currentUVAnimDictionary->streamWrite(&out);
//	c->streamWrite(&out);
//
//	FILE *cf;
//	if(argc > 1)
//		cf = fopen(argv[1], "wb");
//	else
//		cf = fopen("out.dff", "wb");
//	assert(cf != NULL);
//	fwrite(data, out.getLength(), 1, cf);
//	fclose(cf);
//	out.close();
//	delete[] data;

	c->destroy();
	if(currentUVAnimDictionary)
		currentUVAnimDictionary->destroy();
	currentTexDictionary->destroy();

	return 0;
}