// #pragma once
#ifndef Animationrig_H
#define Animationrig_H

#include <CommonMemoryStream.h>
#include <AnimationrigMisc.h>

#include <iostream>
#include <vector>

using namespace CommonMemoryStream;

namespace Animrig {
	class AnimKey
	{
	public:
		static const int SaveVersion = 2;
		inline LayerType layerType() { return fLayerType; }
		int frame = 0;   // AnimationSet has base frame rate.
		float prevTrackAlpha = 1.0f;
		float currentTrackAlpha = 1.0f;
		Easing trackAlphaEasing = Easing::Linear;
		float trackAlphaPower = 1.0f;
		bool isPrevKey = false;

		AnimKey() {
			fLayerType = LayerType::None;
			frame = 0;
			isPrevKey = false;
		}
		virtual ~AnimKey() { }

		virtual void clear() {
			prevTrackAlpha = 1.0f;
			currentTrackAlpha = 1.0f;
			trackAlphaEasing = Easing::Linear;
			trackAlphaPower = 1.0f;
		}
		bool loadFromBinary(MemoryStream *ms)
		{
			// sub class
			if (!loadFromBinarySubClass(ms)) return false;
			// end of data
			return true;
		}

		static AnimKey* createKeyFromLayerType(LayerType layertype, int frame_);
		static AnimKey* createKeyFromBinary(MemoryStream *ms, const wstring &head, LayerType layertype)
		{
			int ver = 0;
			int f = 0;
			AnimKey *key = nullptr;
			try
			{
				Misc::readHead(ms, head);
				ver = Misc::readInteger(ms, 0, SaveVersion); // ver
				// TAnimrigMisc.WriteString(ms,LayerType);  // * use parent layer
				f = Misc::readInteger(ms, 0, 0xffffff);

				key = AnimKey::createKeyFromLayerType(layertype, f);
			}
			catch (...)
			{
				return nullptr;
			}

			try
			{
				key->prevTrackAlpha = Misc::readFloat(ms, 0.0f, 2.0f, true);
				key->currentTrackAlpha = Misc::readFloat(ms, 0.0f, 2.0f, true);
				if (ver >= 2)
				{
					key->trackAlphaEasing = Misc::readEasing(ms);
					key->trackAlphaPower = Misc::readFloat(ms, 0.0f, 2.0f, true);
				}
			}
			catch (...)
			{
				// key = null;
				return nullptr;
			}
			return key;
		}
	protected:
		LayerType fLayerType;
		virtual bool loadFromBinarySubClass(MemoryStream *ms) = 0;
	};
	class KeyHasTransform : public AnimKey
	{
	public:
		Vector3 position;
		Vector3 scale;
		Vector3 rotate;
		RotateOrder rotateOrder;

		Spline transSpline;
		Easing transformEasing;
		float transformPower;

		KeyHasTransform() 
			:AnimKey()
		{
			//
		}
		~KeyHasTransform() override { }

		void clear() override
		{
			Misc::v3SetVector(position, 0.0f, 0.0f, 0.0f);
			Misc::v3SetVector(scale, 1.0f, 1.0f, 1.0f);
			Misc::v3SetVector(rotate, 0.0f, 0.0f, 0.0f);
			rotateOrder = RotateOrder::Pass;

			transSpline = Spline::Linear;
			transformEasing = Easing::Linear;
			transformPower = 1.0f;
		}
	};
	class AnimBlend4Key : public AnimKey {
	public:
		static const int ClassSaveVersion = 2;
		wstring animation = L"";
		bool animationAllowAlways;
		bool animationAdd;
		MixBlend animationMixBlend;
		bool animationLoop;
		float animationMixDuration;
		float blendAlphaX;
		float blendAlphaY;
		Spline blendAlphaSpline;
		Easing blendAlphaEasing;
		float blendAlphaPower;

		AnimBlend4Key()
			: AnimKey()
		{
			fLayerType = LayerType::AnimBlend4;
			clear();
		}
		~AnimBlend4Key() override {}

		void clear() override 
		{
			AnimKey::clear();

			animation = L"none";
			animationAllowAlways = true;
			animationAdd = false;
			animationMixBlend = MixBlend::Add;
			animationMixDuration = -1.0f;

			blendAlphaX = 0.0f;
			blendAlphaY = 0.0f;
			blendAlphaSpline = Spline::Linear;
			blendAlphaEasing = Easing::Linear;
			blendAlphaPower = 1.0f;
		}
	protected:
		bool loadFromBinarySubClass(MemoryStream *ms) override 
		{
			try
			{
				Misc::readHead(ms, L"animblend4key");
				auto ver = Misc::readInteger(ms, 0, ClassSaveVersion); // ver

				animation = Misc::readString(ms);
				animationAllowAlways = Misc::readBool(ms);
				animationAdd = Misc::readBool(ms);
				animationMixBlend = (MixBlend)Misc::readInteger(ms, 0, 1);
				animationLoop = Misc::readBool(ms);
				if (ver >= 2) animationMixDuration = Misc::readFloat(ms, -0xffff, 0xffff, true, true);

				blendAlphaX = Misc::readFloat(ms, -4.0f, +4.0f, true, true);
				blendAlphaY = Misc::readFloat(ms, -4.0f, +4.0f, true, true);
				blendAlphaSpline = Misc::readSpline(ms);
				blendAlphaEasing = Misc::readEasing(ms);
				blendAlphaPower = Misc::readFloat(ms, 0.0f, 2.0f, true);
			}
			catch (...)
			{
				return false;
			}
			return true;
		}
	};
	class AnimBlend1Key : public AnimBlend4Key
	{
	public:
		AnimBlend1Key()
			: AnimBlend4Key()
		{
			fLayerType = LayerType::AnimBlend1;
			clear();
		}
		~AnimBlend1Key() override {}
	};
	class SeekBlend4Key : public AnimKey {
	public:
		static const int ClassSaveVersion = 2;
		wstring animation = L"";
		bool animationAllowAlways;
		bool animationAdd;
		MixBlend animationMixBlend;
		bool animationLoop;
		float animationMixDuration;
		float seekTimeX;
		float seekTimeY;
		Spline seekTimeSpline;
		Easing seekTimeEasing;
		float seekTimePower;

		SeekBlend4Key()
			:AnimKey()
		{
			fLayerType = LayerType::SeekBlend4;
			clear();
		}
		~SeekBlend4Key() override {}

		void clear() override
		{
			AnimKey::clear();

			animation = L"none";
			animationAllowAlways = true;
			animationAdd = false;
			animationMixBlend = MixBlend::Add;
			animationMixDuration = -1.0f;

			seekTimeX = 0.0f;
			seekTimeY = 0.0f;
			seekTimeSpline = Spline::Linear;
			seekTimeEasing = Easing::Linear;
			seekTimePower = 1.0f;
		}
	protected:
		bool loadFromBinarySubClass(MemoryStream *ms) override
		{
			try
			{
				Misc::readHead(ms, L"seekblend4key");
				auto ver = Misc::readInteger(ms, 0, ClassSaveVersion); // ver

				animation = Misc::readString(ms);
				animationAllowAlways = Misc::readBool(ms);
				animationAdd = Misc::readBool(ms);
				animationMixBlend = (MixBlend)Misc::readInteger(ms, 0, 1);
				animationLoop = Misc::readBool(ms);
				animationMixDuration = Misc::readFloat(ms, -0xffff, 0xffff, true, true);

				seekTimeX = Misc::readFloat(ms, -4.0f, +4.0f, true, true);
				seekTimeY = Misc::readFloat(ms, -4.0f, +4.0f, true, true);
				seekTimeSpline = Misc::readSpline(ms);
				seekTimeEasing = Misc::readEasing(ms);
				seekTimePower = Misc::readFloat(ms, 0.0f, 2.0f, true);
			}
			catch (...)
			{
				return false;
			}
			return true;
		}
	};
	class SeekBlend1Key : public SeekBlend4Key {
	public:
		SeekBlend1Key()
			: SeekBlend4Key()
		{
			fLayerType = LayerType::SeekBlend1;
			clear();
		}
		~SeekBlend1Key() override {}
	};
	class TransformKey : public KeyHasTransform
	{
	public:
		static const int ClassSaveVersion = 1;
		int mulColor;
		float mulAlpha;
		Easing colorEasing;
		float colorPower;

		TransformKey()
			: KeyHasTransform()
		{
			fLayerType = LayerType::Transform;
			clear();
		}
		~TransformKey() override {}

		void clear() override
		{
			KeyHasTransform::clear();

			mulColor = 0xffffff;
			mulAlpha = 1.0f;
			colorEasing = Easing::Linear;
			colorPower = 1.0f;
		}
	protected:
		bool loadFromBinarySubClass(MemoryStream *ms) override
		{
			try {
				Misc::readHead(ms, L"transformkey");
				auto ver = Misc::readInteger(ms, 0, ClassSaveVersion); // ver

				position.x = Misc::readFloat(ms, -0xffff, +0xffff, true, true);
				position.y = Misc::readFloat(ms, -0xffff, +0xffff, true, true);
				position.z = Misc::readFloat(ms, -0xffff, +0xffff, true, true);

				scale.x = Misc::readFloat(ms, -0xffff, +0xffff, true, true);
				scale.y = Misc::readFloat(ms, -0xffff, +0xffff, true, true);
				scale.z = Misc::readFloat(ms, -0xffff, +0xffff, true, true);

				rotate.x = Misc::readFloat(ms, -0xffff, +0xffff, true, true);
				rotate.y = Misc::readFloat(ms, -0xffff, +0xffff, true, true);
				rotate.z = Misc::readFloat(ms, -0xffff, +0xffff, true, true);
				rotateOrder = Misc::readRotateOrder(ms);

				transSpline = Misc::readSpline(ms);
				transformEasing = Misc::readEasing(ms);
				transformPower = Misc::readFloat(ms, 0.0f, 2.0f, true);

				mulColor = Misc::readInteger(ms, 0x000000, 0xffffff, true, true);
				mulAlpha = Misc::readFloat(ms, 0, 1.0f, true, true);
				colorEasing = Misc::readEasing(ms);
				colorPower = Misc::readFloat(ms, 0.0f, 2.0f, true);
			}
			catch (...)
			{
				return false;
			}
			return true;
		}
	};
	class EventKey : public AnimKey {
	public:
		static const int ClassSaveVersion = 1;
		wstring ID = L"";
		wstring eventString[2];	
		float eventFloat[2];
		bool allowAlways;     // ID compare
		bool ignitionAlways;

		EventKey()
			:AnimKey()
		{			
			fLayerType = LayerType::Event;
			clear();
		}
		~EventKey() override {}

		void clear() override
		{
			AnimKey::clear();

			ID = L"none";
			eventString[0] = L"";
			eventString[1] = L"";
			eventFloat[0] = 0.0f;
			eventFloat[1] = 0.0f;

			allowAlways = true;
			ignitionAlways = false;
		}
	protected:
		bool loadFromBinarySubClass(MemoryStream *ms) override
		{
			try
			{
				Misc::readHead(ms, L"eventkey");
				auto ver = Misc::readInteger(ms, 0, ClassSaveVersion); // ver

				ID = Misc::readString(ms);
				eventString[0] = Misc::readString(ms);
				eventString[1] = Misc::readString(ms);
				eventFloat[0] = Misc::readFloat(ms, -0xffff, +0xffff, true, true);
				eventFloat[1] = Misc::readFloat(ms, -0xffff, +0xffff, true, true);

				allowAlways = Misc::readBool(ms);
				ignitionAlways = Misc::readBool(ms);
			}
			catch (...)
			{
				return false;
			}
			return true;
		}
	};
	class PriorityKey : public AnimKey {
	public:
		static const int ClassSaveVersion = 1;
		float priority;
		Easing priorityEasing;
		float priorityPower;
		
		PriorityKey()
			: AnimKey()
		{
			fLayerType = LayerType::Priority;
			clear();
		}
		~PriorityKey() override {}

		void clear() override 
		{
			AnimKey::clear();

			priority = 0.0f;
			priorityEasing = Easing::None;
			priorityPower = 1.0f;
		}
	protected:
		bool loadFromBinarySubClass(MemoryStream *ms) override
		{
			try
			{
				Misc::readHead(ms, L"prioritykey");
				auto ver = Misc::readInteger(ms, 0, ClassSaveVersion); // ver

				priority = Misc::readFloat(ms, -0xffff, +0xffff, true, true);
				priorityEasing = Misc::readEasing(ms);
				priorityPower = Misc::readFloat(ms, 0.0f, 1.0f, true);
			}
			catch (...)
			{
				return false;
			}
			return true;
		}
	};
	// layer 
	class AnimLayer {
	public:
		inline LayerType layerType() { return fLayerType; };
		unique_ptr<AnimKey> prevKey;
		wstring name = L"";
		bool enable;
		vector<unique_ptr<AnimKey>> keys;
		int keyCount() { return (int)keys.size(); };
		
		static const int SaveVersion = 1;

		static AnimLayer* createFromLayerType(LayerType layertype);
		static AnimLayer* createFromBinary(MemoryStream *ms, const wstring &head)
		{
			AnimLayer *layer = nullptr;
			try
			{
				Misc::readHead(ms, head);
				auto ver = Misc::readInteger(ms, SaveVersion);  // ver
				auto t = Misc::stringToLayerType(Misc::readString(ms));

				layer = AnimLayer::createFromLayerType(t);
			}
			catch (...)
			{
				return nullptr;
			}

			try
			{
				layer->name = Misc::readString(ms);
				layer->enable = Misc::readBool(ms);
			}
			catch (...)
			{
				// layer = null;
				return nullptr;
			}
			return layer;
		}
		
		AnimLayer() { }
		virtual ~AnimLayer() { }

		virtual void clear()
		{
			keys.clear();
			prevKey->clear();
		}
		bool loadFromBinary(MemoryStream *ms)
		{
			// prevkey
			auto key = AnimKey::createKeyFromBinary(ms, L"key", fLayerType);
			if (key == nullptr) return false;

			if (!key->loadFromBinary(ms)) return false;
			key->isPrevKey = true;
			prevKey.reset(key);

			// keys
			int count = 0;
			try
			{
				count = Misc::readInteger(ms);
			}
			catch (...)
			{
				return false;
			}

			keys.clear();
			for (auto i = 0; i < count; i++)
			{
				key = AnimKey::createKeyFromBinary(ms, L"key", fLayerType);
				if (key == nullptr) return false;

				//auto ptr = unique_ptr<AnimKey>(key);
				keys.emplace_back(key);
				if (!key->loadFromBinary(ms)) return false;
			}
			return true;
		}
		bool loadReserveFromBinary(MemoryStream *ms)
		{
			// sub class
			// * no data
			// keys
			// * no data
			return true;
		}
		int getKeyIndex(int frame)
		{
			auto count = (int)keys.size();	// size_t
			for (auto i = count - 1; i >= 0; i--)
			{
				if (frame >= keys[i]->frame)
				{
					return i;
				}
			}
			return -1;
		}
	protected:
		LayerType fLayerType;
		void CreateSetup()
		{
			name = Misc::_noname;
			enable = true;

			auto key = AnimKey::createKeyFromLayerType(fLayerType, 0);
			key->isPrevKey = true;
			prevKey.reset();
			prevKey.reset(key);	
		}
	};
	class AnimBlend4Layer : public AnimLayer
	{
	public:
		AnimBlend4Layer()
			:AnimLayer()
		{
			fLayerType = LayerType::AnimBlend4;
			CreateSetup();
		}
		~AnimBlend4Layer() override { }
	};
	class AnimBlend1Layer : public AnimLayer
	{
	public:
		AnimBlend1Layer()
			:AnimLayer()
		{
			fLayerType = LayerType::AnimBlend1;
			CreateSetup();
		}
		~AnimBlend1Layer() override { }
	};
	class SeekBlend4Layer : public AnimLayer
	{
	public:
		SeekBlend4Layer()
			:AnimLayer()
		{
			fLayerType = LayerType::SeekBlend4;
			CreateSetup();
		}
		~SeekBlend4Layer() override { }
	};
	class SeekBlend1Layer : public AnimLayer
	{
	public:
		SeekBlend1Layer()
			:AnimLayer()
		{
			fLayerType = LayerType::SeekBlend1;
			CreateSetup();
		}
		~SeekBlend1Layer() override { }
	};
	class TransformLayer : public AnimLayer
	{
	public:
		TransformLayer()
			:AnimLayer()
		{
			fLayerType = LayerType::Transform;
			CreateSetup();
		}
		~TransformLayer() override { }
	};
	class EventLayer : public AnimLayer
	{
	public:
		EventLayer()
			:AnimLayer()
		{
			fLayerType = LayerType::Event;
			CreateSetup();
		}
		~EventLayer() override { }
	};
	class PriorityLayer : public AnimLayer
	{
	public:
		PriorityLayer()
			:AnimLayer()
		{
			fLayerType = LayerType::Priority;
			CreateSetup();
		}
		~PriorityLayer() override { }
	};
	// object
	class AnimObject{
	public:
		static const int SaveVersion = 2;
		static const int ReserveSaveVersion = 4;
		ObjectType objectType() { return fObjectType; };
		wstring name = L"";
		bool enable = true;
		bool visible = true;
		Vector3 offset;
		Vector3 scale;
		int parentObjectIndex = -1; // -1 : none
		float drawPriority;
		vector<wstring> inputPanelField;
		vector<unique_ptr<AnimLayer>> layers;
		int layerCount() { return (int)layers.size(); };

		AnimObject() {}
		virtual ~AnimObject() {};

		static AnimObject* createFromObjectType(ObjectType objecttype);
		static AnimObject* createFromBinary(MemoryStream *ms, const wstring &head)
		{
			AnimObject *obj = nullptr;
			try
			{
				// base head
				Misc::readHead(ms, head);
				auto ver = Misc::readInteger(ms, 0, SaveVersion);
				auto t = Misc::stringToObjectType(Misc::readString(ms));
				auto n = Misc::readString(ms);

				obj = AnimObject::createFromObjectType(t);
				obj->name = n;
			}
			catch (...)
			{
				return nullptr;
			}
			return obj;
		}
		void clear(bool keeplayer = true)
		{
			if (keeplayer) {
				for (auto &l : layers) {
					l->clear();
				}
			}
			else
			{
				layers.clear();
				inputPanelField.clear();
			}
		}
		bool loadFromBinary(MemoryStream *ms)
		{
			int count = 0;
			try
			{
				count = Misc::readInteger(ms, 0, 255);
			}
			catch (...)
			{
				return false;
			}

			layers.clear();
			for (auto i = 0; i < count; i++)
			{
				auto layer = AnimLayer::createFromBinary(ms, L"layer");
				if (layer == nullptr) return false;

				//auto ptr = unique_ptr<AnimLayer>(layer);
				layers.emplace_back(layer);
				if (!layer->loadFromBinary(ms)) return false;
			}
			return true;
		}
		bool loadReserveFromBinary(MemoryStream *ms)
		{
			if (!loadReserveFromBinarySubClass(ms)) return false;
			// layers
			int count = 0;
			try
			{
				count = Misc::readInteger(ms, 0, 255);
			}
			catch(...)
			{
				return false;
			}

			layers.clear();
			for (auto i = 0; i < count; i++)
			{
				auto layer = AnimLayer::createFromBinary(ms, L"reservelayer");
				if (layer == nullptr) return false;

				//auto ptr = unique_ptr<AnimLayer>(layer);
				layers.emplace_back(layer);
				if (!layer->loadReserveFromBinary(ms)) return false;
			}
			return true;
		}
		
	protected:
		ObjectType fObjectType;
		void createSetup()
		{
			name = Misc::_noname;
			enable = true;
			visible = true;
			// fUserObject := nil;

			Misc::v3SetVector(offset, 0.0f, 0.0f, 0.0f);
			Misc::v3SetVector(scale, 1.0f, 1.0f, 1.0f);
			parentObjectIndex = -1;
			drawPriority = 0.0f;

			//Layers := TObjectList.Create;
			//InputPanelField:= TStringList.Create;
		}
		virtual bool loadReserveFromBinarySubClass(MemoryStream *ms)
		{
			try
			{
				// version
				auto ver = Misc::readInteger(ms, 0, ReserveSaveVersion);
				// state
				enable = Misc::readBool(ms);
				visible = Misc::readBool(ms);
				// offset / scale
				if (ver >= 2)
				{
					offset.x = Misc::readFloat(ms, -0xffff, +0xffff, true);
					offset.y = Misc::readFloat(ms, -0xffff, +0xffff, true);
					offset.z = Misc::readFloat(ms, -0xffff, +0xffff, true);
					scale.x = Misc::readFloat(ms, -0xffff, +0xffff, true);
					scale.y = Misc::readFloat(ms, -0xffff, +0xffff, true);
					scale.z = Misc::readFloat(ms, -0xffff, +0xffff, true);
				}
				if (ver >= 3) parentObjectIndex = Misc::readInteger(ms, -1, 0xff, true);
				if (ver >= 4) drawPriority = Misc::readFloat(ms, -0xffff, 0xffff, true);
				// input field
				inputPanelField.clear();
				auto n = Misc::readInteger(ms, 0, 256);
				for (auto i = 0; i < n; i++)
				{
					inputPanelField.emplace_back(Misc::readString(ms));
				}
			}
			catch (...)
			{
				return false;
			}
			return true;
		}
	};
	class NullObject : public AnimObject {
	public:
		static const int ClassSaveVersion = 1;
		wstring imageFilename;

		NullObject()
		{
			fObjectType = ObjectType::Null;
			createSetup();

			imageFilename = L"";
		}
		~NullObject() override 
		{
			// AnimObject::~AnimObject();
		}
	protected:
		bool loadReserveFromBinarySubClass(MemoryStream *ms) override
		{
			// inherited
			if (!AnimObject::loadReserveFromBinarySubClass(ms)) return false;

			try
			{
				Misc::readHead(ms, L"nullobject");
				auto ver = Misc::readInteger(ms, 0, ClassSaveVersion);
				imageFilename = Misc::readString(ms);
			}
			catch (...)
			{
				return false;
			}
			return true;
		}

	};
	class SpineObject : public AnimObject {
	public:
		static const int ClassSaveVersion = 2;
		wstring spineFilename;
		float mixDuration;
		float baseScale;
		bool preMultiplyAlpha;

		SpineObject()
		{
			fObjectType = ObjectType::Spine;
			createSetup();

			spineFilename = L"";
			mixDuration = 0.25f;
			baseScale = 1.0f;
			preMultiplyAlpha = false;
		}
		~SpineObject() override
		{
			// AnimObject::~AnimObject();
		}
	protected:
		bool loadReserveFromBinarySubClass(MemoryStream *ms) override
		{
			// inherited
			if (!AnimObject::loadReserveFromBinarySubClass(ms)) return false;

			try
			{
				Misc::readHead(ms, L"spineobject");
				auto ver = Misc::readInteger(ms, 0, ClassSaveVersion);
				spineFilename = Misc::readString(ms);
				mixDuration = Misc::readFloat(ms, 0, 0xffff);
				baseScale = Misc::readFloat(ms, 0, 0xffff);
				if (ver >= 2) preMultiplyAlpha = Misc::readBool(ms);
			}
			catch (...)
			{
				return false;
			}
			return true;
		}
	};
	// animation
	class Animation {
	public:
		static const int SaveVersion = 1;
		wstring name = L"";
		wstring previewBaseAnimation = L"";
		bool loopEnable = true;
		int loopBeginFrame = 0;
		int loopEndFrame = 0;
		vector<unique_ptr<AnimObject>> objects;
		int objectCount() { return (int)objects.size(); };

		Animation() {
			// Objects = new List<AnimObject>();
			// Name = Misc._noname;
		}
		virtual ~Animation() {}

		void clear(bool keepobject = true) {
			if (keepobject)
			{
				auto count = objectCount();
				for(auto &o : objects)
				{
					o->clear();
				}
			}
			else
			{
				objects.clear();
				loopEnable = false;
				loopBeginFrame = 0;
				loopEndFrame = 0;
			}
		}
		double getLoopedFrame(double frame)
		{
			if (!loopEnable) return frame;
			if (frame < loopEndFrame) return frame;

			auto len = loopEndFrame - loopBeginFrame;
			if (len < 1) return frame;

			frame = frame - loopEndFrame;
			frame = frame - (floor(frame / len)*len);

			return max(loopBeginFrame + frame, (double)loopBeginFrame);
		}
		bool loadFromBinary(MemoryStream *ms)
		{
			clear(false);

			try
			{
				Misc::readHead(ms, L"animation");
				auto ver = Misc::readInteger(ms, 0, SaveVersion);
				name = Misc::readString(ms);
				loopEnable = Misc::readBool(ms);
				loopBeginFrame = Misc::readInteger(ms, 0, 0xffff);
				loopEndFrame = Misc::readInteger(ms, 0, 0xffff);
				previewBaseAnimation = Misc::readString(ms, 256);
			}
			catch (...)
			{
				return false;
			}

			// objects
			int count = 0;
			try
			{
				count = Misc::readInteger(ms, 0, 255);
			}
			catch (...)
			{
				return false;
			}
			objects.clear();
			for (auto i = 0; i < count; i++)
			{
				auto obj = AnimObject::createFromBinary(ms, L"object");
				if (obj == nullptr) return false;

				//auto ptr = unique_ptr<AnimObject>(obj);
				objects.emplace_back(obj);
				if (!obj->loadFromBinary(ms)) return false;
			}
			return true;
		}
	protected:

	};
	// Animtionrig
	class Animationrig {
	public:
		static const int GlobalChunkSaveVersion = 2;
		static const int SaveVersion =
			AnimKey::SaveVersion +               // keys
			AnimBlend4Key::ClassSaveVersion +
			AnimBlend1Key::ClassSaveVersion +
			SeekBlend4Key::ClassSaveVersion +
			SeekBlend1Key::ClassSaveVersion +
			TransformKey::ClassSaveVersion +
			EventKey::ClassSaveVersion +
			PriorityKey::ClassSaveVersion +
			AnimLayer::SaveVersion +             // layer
			AnimObject::SaveVersion + AnimObject::ReserveSaveVersion + // object
			SpineObject::ClassSaveVersion +
			NullObject::ClassSaveVersion +
			Animation::SaveVersion +             // animation
			GlobalChunkSaveVersion;             // self

		int framerate = 30;
		vector<wstring> userMemo;
		int loadVersion;
		wstring lastError;
		Vector3 offset;
		Vector3 size;

		vector<unique_ptr<Animation>> animations;
		int animationCount() { return (int)animations.size(); };

		vector<unique_ptr<AnimObject>> objects;
		int objectCount(){ return (int)objects.size(); };

		Animationrig() {
			// Objects = new 
			// Animations = new
			// UserMemo = new
			framerate = 30;

			clear();
		}
		virtual ~Animationrig() { }

		void clear(bool keepobject = true) {
			animations.clear();
			userMemo.clear();

			if (!keepobject) objects.clear();
			if (!keepobject) framerate = 30;

			loadVersion = SaveVersion;
		}
		bool loadFromBinary(MemoryStream *ms)
		{
			lastError = L"";
			int loadver = 0;

			// head
			try
			{
				uint8_t hed[8];
				if (ms->read(&hed[0], 8) != 8)
				{
					lastError = L"Head error : can not read stream.";
					return false;
				}
				if (hed[0] != (uint8_t)'a') throw exception(); // goto :Q
				if (hed[1] != (uint8_t)'n') throw exception();
				if (hed[2] != (uint8_t)'i') throw exception();
				if (hed[3] != (uint8_t)'m') throw exception();
				if (hed[4] != (uint8_t)'r') throw exception();
				if (hed[5] != (uint8_t)'i') throw exception();
				if (hed[6] != (uint8_t)'g') throw exception();
				if (hed[7] != 0) throw exception();
			}
			catch(...)
			{
				lastError = L"Head error : char code error.";
				return false;
			}

			// version
			try
			{
				loadver = Misc::readInteger(ms);
				if ((loadver < 0) || (loadver > SaveVersion))
				{
					lastError = L"Load version error : " + std::to_wstring(loadver);
					return false;
				}
			}
			catch(...)
			{
				lastError = L"Header error : version chank error.";
				return false;
			}

			// clear
			clear(false);

			// global
			try
			{
				Misc::readHead(ms, L"global");
				auto ver = Misc::readInteger(ms, 0, GlobalChunkSaveVersion);
				framerate = Misc::readInteger(ms, 5, 120);
				offset.x = Misc::readFloat(ms, -0xffff, +0xffff);
				offset.y = Misc::readFloat(ms, -0xffff, +0xffff);
				offset.z = Misc::readFloat(ms, -0xffff, +0xffff);
				size.x = Misc::readFloat(ms, -0xffff, +0xffff);
				size.y = Misc::readFloat(ms, -0xffff, +0xffff);
				size.z = Misc::readFloat(ms, -0xffff, +0xffff);
				if (ver >= 2)
				{
					Misc::readStringList(ms, userMemo);
				}
			}
			catch(...)
			{
				lastError = L"Global chunk error.";
				return false;
			}

			// load version
			loadVersion = loadver;

			// reserve object
			try
			{
				Misc::readHead(ms, L"reserveobjects");
				auto ver = Misc::readInteger(ms, 0, 1);
				auto count = Misc::readInteger(ms, 0, 255);

				objects.clear();
				for (auto i = 0; i < count; i++)
				{
					auto obj = AnimObject::createFromBinary(ms, L"reserveobject");
					if (obj == nullptr)
					{
						lastError = L"Reserve object Head Error";
						return false;
					}
					
					//auto ptr = unique_ptr<AnimObject>(obj);
					objects.emplace_back(obj);

					if (!obj->loadReserveFromBinary(ms))
					{
						lastError = L"Reserve object Load Error";
						return false;
					}
				}
			}
			catch(...)
			{
				lastError = L"Reserve chunk error.";
				return false;
			}

			// animation
			try
			{
				Misc::readHead(ms, L"animations");
				auto ver = Misc::readInteger(ms, 0, 1);
				auto count = Misc::readInteger(ms, 0, 255);

				animations.clear();
				for (auto i = 0; i < count; i++)
				{
					auto anim = new Animation();
					//auto ptr = unique_ptr<Animation>(anim);
					animations.emplace_back(anim);
					if (!anim->loadFromBinary(ms))
					{
						lastError = L"Animation load error.";
						return false;
					}
				}
			}
			catch(...)
			{
				lastError = L"Animation chunk error.";
				return false;
			}
			return true;
		}
		int search(const wstring &animationname)
		{
			auto ls = Misc::stringToLower(animationname);
			auto count = animationCount();
			for (auto i = 0; i < count; i++)
			{
				if (Misc::stringToLower(animations[i]->name) == ls)
				{
					return i;
				}
			}
			return -1;
		}
	protected:
	};
}

#endif