module coneneko.mkm;
import std.stream, std.string, coneneko.math, coneneko.serializer, std.conv, std.stdio, std.windows.charset;

class MkmKeyFrame : Serializer
{
	unittest
	{
		auto s = new MemoryStream();
		s.writefln("\t\t0 (-0.081737 -63.294903 2.752960)");
		s.seekSet(0);
		auto a = new MkmKeyFrame();
		a.deserialize(s);
		auto d = new MemoryStream();
		a.serialize(d);
		assert(s.size == d.size);
		assert(s.data == d.data);
	}
	
	unittest
	{
		auto s = new MemoryStream();
		s.writefln("\t\t15 (-0.707107 0.000000 0.000000 0.707107)");
		s.seekSet(0);
		auto a = new MkmKeyFrame();
		a.deserialize(s);
		auto d = new MemoryStream();
		a.serialize(d);
		assert(s.size == d.size);
		assert(s.data == d.data);
	}
	
	uint t;
	Vector value;
	
	void serialize(Stream writer)
	{
		writer.writef("\t\t%d (%.6f %.6f %.6f", t, value.x, value.y, value.z);
		if (value.w != 0.0f) writer.writef(" %.6f", value.w);
		writer.writefln(")");
	}
	
	void deserialize(Stream reader)
	{
		auto a = (cast(string)reader.readLine()).removechars("()").split(); // d2
		t = a[0].toUint();
		value = vector(a[1].toFloat(), a[2].toFloat(), a[3].toFloat());
		if (a.length == 5) value.w = a[4].toFloat();
	}
}

class MkmMotionItem : Serializer
{
	unittest
	{
		auto s = new MemoryStream();
		s.writefln("\tVector {");
		s.writefln("\t\tname = \"j_boneroot1\"");
		s.writefln("\t\tclass = \"Locate\"");
		s.writefln("\t\tmember = \"pos\"");
		s.writefln("\t\tcurve = \"spline\"");
		s.writefln("\t\t0 (-0.081737 -63.294903 2.752960)");
		s.writefln("\t\t30 (-0.081737 -80.094910 2.752960)");
		s.writefln("\t\t45 (-0.081737 -53.650467 2.752960)");
		s.writefln("\t\t60 (-0.081737 -63.294903 2.752960)");
		s.writefln("\t}");
		s.seekSet(0);
		auto a = new MkmMotionItem();
		a.deserialize(s);
		auto d = new MemoryStream();
		a.serialize(d);
		assert(s.size == d.size);
		assert(s.data == d.data);
	}
	
	unittest
	{
		auto s = new MemoryStream();
		s.writefln("\tQuaternion {");
		s.writefln("\t\tname = \"boneroot1\"");
		s.writefln("\t\tclass = \"Bone\"");
		s.writefln("\t\tmember = \"rot\"");
		s.writefln("\t\tcurve = \"spline\"");
		s.writefln("\t\t0 (-0.707107 0.000000 0.000000 0.707107)");
		s.writefln("\t\t30 (-0.707107 0.000000 0.000000 0.707107)");
		s.writefln("\t\t45 (-0.707107 0.000000 0.000000 0.707107)");
		s.writefln("\t\t60 (-0.707107 0.000000 0.000000 0.707107)");
		s.writefln("\t}");
		s.seekSet(0);
		auto a = new MkmMotionItem();
		a.deserialize(s);
		auto d = new MemoryStream();
		a.serialize(d);
		assert(s.size == d.size);
		assert(s.data == d.data);
	}
	
	string name, _class, member, curve;
	MkmKeyFrame[] keyFrames;
	
	void serialize(Stream writer)
	in
	{
		assert(1 <= keyFrames.length);
	}
	body
	{
		bool isVector = _class == "Locate";
		writer.writefln(isVector ? "\tVector {" : "\tQuaternion {");
		writer.writefln("\t\tname = \"%s\"", name);
		writer.writefln("\t\tclass = \"%s\"", _class);
		writer.writefln("\t\tmember = \"%s\"", member);
		writer.writefln("\t\tcurve = \"%s\"", curve);
		foreach (v; keyFrames) v.serialize(writer);
		writer.writefln("\t}");
	}
	
	void deserialize(Stream reader)
	{
		auto line = reader.readLine();
		if (line != "\tQuaternion {" && line != "\tVector {") throw new Error("MkmMotionItem");
		name = (cast(string)reader.readLine()).split("\"")[1]; // d2
		name = fromMBSz(name.toStringz()); // d1
		//name = fromMBSz(cast(invariant)name.toStringz()); // d2
		_class = (cast(string)reader.readLine()).split("\"")[1]; // d2
		member = (cast(string)reader.readLine()).split("\"")[1]; // d2
		curve = (cast(string)reader.readLine()).split("\"")[1]; // d2
		keyFrames.length = 0;
		while (true)
		{
			reader.getc(); // tab
			auto c = reader.getc();
			if (c == '}') break;
			reader.ungetc(c);
			reader.ungetc('\t');
			keyFrames ~= new MkmKeyFrame();
			keyFrames[$ - 1].deserialize(reader);
		}
		reader.readLine();
	}
}

class MkmMotion : Serializer
{
	unittest
	{
		auto s = new MemoryStream();
		s.writefln("Motion {");
		s.writefln("\tname = \"motion\"");
		s.writefln("\tendframe = 60");
		s.writefln("\tloop = 0");
		s.writefln("\tVector {");
		s.writefln("\t\tname = \"j_anchor_a4\"");
		s.writefln("\t\tclass = \"Locate\"");
		s.writefln("\t\tmember = \"pos\"");
		s.writefln("\t\tcurve = \"spline\"");
		s.writefln("\t\t0 (0.000000 24.633301 -21.720800)");
		s.writefln("\t\t30 (0.000000 24.633301 -21.720800)");
		s.writefln("\t\t60 (0.000000 24.633301 -21.720800)");
		s.writefln("\t}");
		s.writefln("\tQuaternion {");
		s.writefln("\t\tname = \"anchor_a4\"");
		s.writefln("\t\tclass = \"Bone\"");
		s.writefln("\t\tmember = \"rot\"");
		s.writefln("\t\tcurve = \"spline\"");
		s.writefln("\t\t0 (0.000000 0.000000 0.000000 1.000000)");
		s.writefln("\t\t30 (0.000000 0.000000 0.000000 1.000000)");
		s.writefln("\t\t60 (0.000000 0.000000 0.000000 1.000000)");
		s.writefln("\t}");
		s.writefln("}");
		s.seekSet(0);
		auto a = new MkmMotion();
		a.deserialize(s);
		auto d = new MemoryStream();
		a.serialize(d);
		assert(s.size == d.size);
		assert(s.data == d.data);
	}
	
	string name;
	uint endframe, loop;
	MkmMotionItem vector;
	MkmMotionItem[] quaternions;
	
	void serialize(Stream writer)
	{
		writer.writefln("Motion {");
		writer.writefln("\tname = \"%s\"", name);
		writer.writefln("\tendframe = %d", endframe);
		writer.writefln("\tloop = %d", loop);
		vector.serialize(writer);
		foreach (v; quaternions) v.serialize(writer);
		writer.writefln("}");
	}
	
	void deserialize(Stream reader)
	{
		if (reader.readLine() != "Motion {") throw new Error("MkmMotion");
		name = (cast(string)reader.readLine()).split("\"")[1]; // d2
		name = fromMBSz(name.toStringz()); // d1
		//name = fromMBSz(cast(invariant)name.toStringz()); // d2
		endframe = (cast(string)reader.readLine()).split("= ")[1].toUint(); // d2
		loop = (cast(string)reader.readLine()).split("= ")[1].toUint(); // d2
		vector = new MkmMotionItem();
		vector.deserialize(reader);
		quaternions.length = 0;
		while (true)
		{
			quaternions ~= new MkmMotionItem();
			quaternions[$ - 1].deserialize(reader);
			auto c = reader.getc();
			if (c == '}') break;
			reader.ungetc(c);
		}
		reader.readLine();
	}
}

class Mkm : Serializer
{
	unittest
	{
		auto s = new MemoryStream();
		s.writefln("Mikoto Motion Ver 2");
		s.writefln("Motion {");
		s.writefln("\tname = \"motion\"");
		s.writefln("\tendframe = 60");
		s.writefln("\tloop = 0");
		s.writefln("\tVector {");
		s.writefln("\t\tname = \"j_anchor_a4\"");
		s.writefln("\t\tclass = \"Locate\"");
		s.writefln("\t\tmember = \"pos\"");
		s.writefln("\t\tcurve = \"spline\"");
		s.writefln("\t\t0 (0.000000 24.633301 -21.720800)");
		s.writefln("\t\t30 (0.000000 24.633301 -21.720800)");
		s.writefln("\t\t60 (0.000000 24.633301 -21.720800)");
		s.writefln("\t}");
		s.writefln("\tQuaternion {");
		s.writefln("\t\tname = \"anchor_a4\"");
		s.writefln("\t\tclass = \"Bone\"");
		s.writefln("\t\tmember = \"rot\"");
		s.writefln("\t\tcurve = \"spline\"");
		s.writefln("\t\t0 (0.000000 0.000000 0.000000 1.000000)");
		s.writefln("\t\t30 (0.000000 0.000000 0.000000 1.000000)");
		s.writefln("\t\t60 (0.000000 0.000000 0.000000 1.000000)");
		s.writefln("\t}");
		s.writefln("}");
		s.writefln("Eof");
		s.seekSet(0);
		auto a = new Mkm();
		a.deserialize(s);
		auto d = new MemoryStream();
		a.serialize(d);
		assert(s.size == d.size);
		assert(s.data == d.data);
	}
	
	MkmMotion[] motions;
	
	void serialize(Stream writer)
	{
		writer.writefln("Mikoto Motion Ver 2");
		foreach (v; motions) v.serialize(writer);
		writer.writefln("Eof");
	}
	
	void deserialize(Stream reader)
	{
		if (reader.readLine() != "Mikoto Motion Ver 2") throw new Error("not mkm");
		motions.length = 0;
		while (true)
		{
			auto c = reader.getc();
			if (c == 'E') break;
			reader.ungetc(c);
			motions ~= new MkmMotion();
			motions[$ - 1].deserialize(reader);
		}
	}
}
