

//#define SpineLibrary
//#define CheckMemoryLeak

#ifdef CheckMemoryLeak
#define _CRTDBG_MAP_ALLOC
#include <cstdlib>
#include <crtdbg.h>
#endif

#include <iostream>

#include <CommonMemoryStream.h>
#include <AnimationrigMisc.h>
#include <Animationrig.h>
#include <AnimationrigState.h>
#include <AnimationrigPreset.h>
#include <AnimationrigLogger.h>

#ifdef SpineLibrary
#include <AnimationrigSpine.h>
#endif

//#include <spine/Debug.h>

using namespace std;
using namespace CommonMemoryStream;
using namespace Animrig;

class Program {
public:
	// log callback
	static void logCallback(const wstring &s)
	{
		wcout << s + L"\n";
	}
	// event callback
	static void evenetCallback(AnimationrigState *stste, StateEventData &data)
	{
		//wchar_t c = L'\"';   //!important : wstring + wchar_t + wstring , crash string!
		wstring c = L"\"";
		Logger::log(L"event");
		Logger::log(L"  name : " + data.ID);
		Logger::log(L"  strings : " + c + data.strings[0] + c + L" , " + c + data.strings[1] + c);
		Logger::log(L"  floats : " + Logger::floatToStr(data.floats[0]) + L" , " + Logger::floatToStr(data.floats[1]));
	}
	// object log
	static void objectLogOut(AnimObject *o)
	{
		Logger::log(L"object.state");
		Logger::log(L"  name : " + o->name);
		Logger::log(L"  enable : " + Logger::boolToStr(o->enable));
		Logger::log(L"  visible : " + Logger::boolToStr(o->visible));
		Logger::log(L"  offset : " + Logger::vectorToStr(o->offset));
		Logger::log(L"  scale : " + Logger::vectorToStr(o->scale));
		Logger::log(L"  parent object index : " + Logger::intToStr(o->parentObjectIndex));
		Logger::log(L"  draw priority : " + Logger::floatToStr(o->drawPriority));
	}
	// track log
	static void trackLogOut(StateTrack *o, int index, const wstring &name)
	{
		Logger::log(name + L".statetrack[" + Logger::intToStr(index) + L"]");
		if (o == nullptr)
		{
			Logger::log(L"  null");
			return;
		}

		Logger::log(L"  enable : " + Logger::boolToStr(o->enable));
		Logger::log(L"  play frame : " + Logger::floatToStr((float)o->playFrame));
		Logger::log(L"  track alpha : " + Logger::floatToStr(o->trackAlphaEase.value));
		Logger::log(L"  mix alpha : " + Logger::floatToStr(o->mixAlphaEase.value));
	}
	static void update(AnimationrigState *state)
	{
		state->update(0.1f, true);
		trackLogOut(state->tracksSafe(0), 0, L"track[0]");
		trackLogOut(state->tracksSafe(1), 1, L"track[1]");
		trackLogOut(state->tracksSafe(2), 2, L"track[2]");
		trackLogOut(state->tracksSafe(3), 3, L"track[3]");
	}
	static bool test() {
		// log callback
		Logger::onLog = logCallback;
		// setup data
		auto anim = new Animationrig();
		// load
		{
			auto ms = CommonMS::loadFromFile(L"data/sample.animrig");
			if (ms == nullptr)
			{
				wcout << L"file error : sample.animrig";
				return false;
			}

			if (!anim->loadFromBinary(ms))
			{
				delete ms;
				wcout << L"load error : AnimationRig : " + anim->lastError;
				return false;
			}
			delete ms;
		}

		// setup state
		auto state = new AnimationrigState(anim);
		state->onEvent = evenetCallback; //std::bind(evenetCallback, this);

		// setup preset
		auto preset = new Preset();
		{
			auto ms = CommonMS::loadFromFile(L"data/sample.animrig.preset.txt");
			if (ms == nullptr)
			{
				wcout << L"file error : sample.animrig.preset.txt";
				return false;
			}
			if (!preset->loadFromStream(ms))
			{
				delete ms;
				wcout << L"load error : preset";
				return false;
			}
			delete ms;
		}

		// setup logger
		auto logger = new StateLogRenderer(ObjectType::Spine);
		logger->name = L"logger";
		// set logger
		state->inputs[0]->setRenderer(logger);

		// object log
		objectLogOut(anim->objects[0].get());

		// update
		{
			auto interval = 60;
			for (auto i = 0; i < interval; i++) update(state);

			state->entryTrack(0, L"setup");
			state->entryTrack(1, L"pose_test");
			state->entryTrack(3, L"physics_wind");
			preset->setDefault(state->inputs[0].get());
			for (auto i = 0; i < interval; i++) update(state);

			preset->setPreset(state->inputs[0].get(), L"lookdown", 1.0f);
			for (auto i = 0; i < interval; i++) update(state);

			state->entryTrackFade(2, L"facial_lol", 1.0f);
			for (auto i = 0; i < interval; i++) update(state);

			state->entryTrackFade(2, L"facial_boo", 1.0f);
			auto ms = new MemoryStream();
			state->saveToBinary(ms);
			wcout << L"state save to binary : " + std::to_wstring(ms->length()) + L"KB\n";
			for (auto i = 0; i < interval; i++) update(state);

			state->clearTrackFade(2, 1.0f);
			preset->setClear(state->inputs[0].get(), 1.0f);
			for (auto i = 0; i < interval; i++) update(state);

			state->clearTrackFade(0, 1.0f);
			state->clearTrackFade(1, 1.0f);
			state->clearTrackFade(2, 1.0f);
			for (auto i = 0; i < interval; i++) update(state);

			// state load
			ms->position(0);
			if (state->loadFromBinary(ms))
			{
				for (auto i = 0; i < interval; i++) update(state);
			}
			else
			{
				wcout << "state load error : state deserialize error";
				delete ms;
				return false;
			}

			state->clearTracks();
			ms->position(0);
			if (state->loadFromBinary(ms))
			{
				for (auto i = 0; i < interval; i++) update(state);
			}
			else
			{
				wcout << "state load error : state deserialize error";
				delete ms;
				return false;
			}

			delete ms;
		}

		// unset logger
		state->inputs[0]->setRenderer(nullptr);
		delete logger;

		delete preset;
		delete state;
		delete anim;

		return true;
	}
};

// *** important
#ifdef SpineLibrary
namespace spine {
	SpineExtension* getDefaultExtension() {
		return new DefaultSpineExtension();
	}
}
#endif


int main()
{
#ifdef CheckMemoryLeak
	_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#endif
	if (Program::test()) {
		wcout << "completed." << "\n";
	}
	else {
		wcout << "error exists." << "\n";
	}
	return 0;
}
