//#pragma once
#ifndef AnimationrigSpine_H
#define AnimationrigSpine_H

#include <CommonMemoryStream.h>	// unicode
#include <AnimationrigMisc.h>
#include <Animationrig.h>
#include <AnimationrigState.h>

#include <spine/Animation.h>
#include <spine/AnimationState.h>
#include <spine/AnimationStateData.h>
#include <spine/Skeleton.h>
#include <spine/SkeletonData.h>
#include <spine/Bone.h>
#include <spine/BoneData.h>

#include <math.h>

using namespace std;

namespace Animrig {
	class SpineRenderer : public StateRenderer {
	protected:
		std::map<wstring, spine::Bone*> boneCacheList;
		Matrix4 extractWorldMatrix(spine::Bone *bone)
		{
			Matrix4 r = Misc::IdentityMatrix4;
			r._11 = bone->getA();
			r._12 = bone->getC();
			r._21 = bone->getB();
			r._22 = bone->getD();
			r._41 = bone->getWorldX() * antiScaling;
			r._42 = bone->getWorldY() * antiScaling;
			return r;
		}
	public :
		// spine string convert
		static wstring convertSpineToWString(const spine::String &s) {
			string utf8;
			utf8.resize(s.length());
			memcpy(&utf8[0], s.buffer(), s.length());

			return UnicodeConverter::utf8to16(utf8);
		}
		static spine::String *convertWStringToSpine(const wstring &s) {
			auto utf8 = UnicodeConverter::utf16to8(s);
			auto ss = new spine::String(utf8.c_str());	// hard copy

			return ss;
		}
		// property
		const int SaveVersion = 1;
		spine::AnimationState *state;
		spine::Skeleton *skeleton;
		float antiScaling = 1.0f;
		// public
		SpineRenderer()
			: StateRenderer()
		{
			fObjectType = ObjectType::Spine;

			state = nullptr;
			skeleton = nullptr;

			clear();
		}
		// common 
		void clear() override
		{
			StateRenderer::clear();

			boneCacheList.clear();
			state = nullptr;
			skeleton = nullptr;
		}
		void update(double deltatime, double deltaphysics=-1) override
		{
			if (state == nullptr) return;
			if (skeleton == nullptr) return;

			state->update((float)deltatime);
			state->apply(*skeleton);
			skeleton->updateWorldTransform();
		}
		// spine
		void setAnimation(int trackindex, int usetrack, const wstring &basename, bool loop, bool addanim, MixBlend trackmix, float duration, bool seekmode, float loopbegin, float loopend) override
		{
			struct LocalFunction {
				static void inSetEmpty(spine::AnimationState *state, int index, float dt, bool addanim)
				{
					if (dt < 0.0) dt = state->getData()->getDefaultMix();

					if (addanim)
						state->addEmptyAnimation(index, dt, 0.0f);
					else
						state->setEmptyAnimation(index, dt);
				}
				static void inSetAnimation(spine::AnimationState *state, int index, const wstring &name1, const wstring &name2, bool loop, bool addanim, MixBlend trackmix, float dt, bool seekmode, float loopbegin, float loopend)
				{
					// search
					spine::Animation *anim;
					{
						spine::String ss1(UnicodeConverter::utf16to8(name1).c_str());
						anim = state->getData()->getSkeletonData()->findAnimation(ss1);
						if (anim == nullptr) {
							spine::String ss2(UnicodeConverter::utf16to8(name2).c_str());
							anim = state->getData()->getSkeletonData()->findAnimation(ss2);
						}
					}
					if (anim == nullptr)
					{
						LocalFunction::inSetEmpty(state, index, dt, addanim);
					}
					else
					{
						// set animation
						spine::TrackEntry *track;
						if (addanim)
							track = state->addAnimation(index, anim, loop, 0.0f);
						else
							track = state->setAnimation(index, anim, loop);
						if (track != nullptr)
						{
							// mix blend
							if ((track->getMixBlend() == spine::MixBlend_Replace) || (track->getMixBlend() == spine::MixBlend_Add))     // safe code
							{
								switch (trackmix)
								{
								case MixBlend::Add:
									track->setMixBlend(spine::MixBlend_Add);
									break;
								case MixBlend::Replace:
									track->setMixBlend(spine::MixBlend_Replace);
									break;
								}
							}
							// duration
							if (dt < 0.0f)
							{
								// use default duration
							}
							else
							{
								track->setMixDuration(max(dt, 0.0f));
							}
							// seek mode
							if (seekmode)
								track->setTimeScale(0.0f);
							else
								track->setTimeScale(1.0f);
							// loop area
							if (loopbegin != loopend) {
								track->setAnimationStart(loopbegin);
								track->setAnimationEnd(loopend);
								// track->setAnimationLast(loopbegin);
							}
						}
					}
				}
			};

			if (state == nullptr) return;

			// 1 track
			if (usetrack == 1)
			{
				if (basename == L"empty")
				{
					LocalFunction::inSetEmpty(state, trackindex + 0, duration, addanim);
				}
				else
				{
					LocalFunction::inSetAnimation(state, trackindex + 0, basename, basename + L"@up", loop, addanim, trackmix, duration, seekmode, loopbegin, loopend);
				}
				return;
			}

			// 4 track animations
			if (usetrack == 4)
			{
				if (basename == L"empty")
				{
					LocalFunction::inSetEmpty(state,trackindex + 0, duration, addanim);
					LocalFunction::inSetEmpty(state,trackindex + 1, duration, addanim);
					LocalFunction::inSetEmpty(state,trackindex + 2, duration, addanim);
					LocalFunction::inSetEmpty(state,trackindex + 3, duration, addanim);
				}
				else
				{
					LocalFunction::inSetAnimation(state, trackindex + 0, basename, basename + L"@up", loop, addanim, trackmix, duration, seekmode, loopbegin, loopend);
					LocalFunction::inSetAnimation(state, trackindex + 1, basename + L"@right", L"", loop, addanim, trackmix, duration, seekmode, loopbegin, loopend);
					LocalFunction::inSetAnimation(state, trackindex + 2, basename + L"@down", L"", loop, addanim, trackmix, duration, seekmode, loopbegin, loopend);
					LocalFunction::inSetAnimation(state, trackindex + 3, basename + L"@left", L"", loop, addanim, trackmix, duration, seekmode, loopbegin, loopend);
				}
			}

			// 2 track animations
			if (usetrack == 2) 
			{
				if (basename == L"empty") 
				{
					LocalFunction::inSetEmpty(state, trackindex + 0, duration, addanim);
					LocalFunction::inSetEmpty(state, trackindex + 1, duration, addanim);
				}
				else 
				{
					LocalFunction::inSetAnimation(state, trackindex + 0, basename, basename + L"@up", loop, addanim, trackmix, duration, seekmode, loopbegin, loopend);
					LocalFunction::inSetAnimation(state, trackindex + 1, basename + L"@right", L"", loop, addanim, trackmix, duration, seekmode, loopbegin, loopend);
				}
			}
		}
		void setTrackBlendAlpha(int trackindex, int usetrack, float alpha1, float alpha2, float alpha3, float alpha4, bool exists) override
		{
			struct LocalFunction {
				static void inSet(spine::AnimationState *state, int index, float alpha) {
					auto track = state->getCurrent(index);
					if (track == nullptr) return;

					track->setAlpha(alpha);
				}
			};
			
			if (state == nullptr) return;

			if (usetrack == 1)
			{
				LocalFunction::inSet(state, trackindex + 0, alpha1);
			}

			if (usetrack == 4)
			{
				LocalFunction::inSet(state, trackindex + 0, alpha1);
				LocalFunction::inSet(state, trackindex + 1, alpha2);
				LocalFunction::inSet(state, trackindex + 2, alpha3);
				LocalFunction::inSet(state, trackindex + 3, alpha4);
			}

			if (usetrack == 2)
			{
				LocalFunction::inSet(state, trackindex + 0, alpha1 - alpha3);
				LocalFunction::inSet(state, trackindex + 1, alpha2 - alpha4);
			}
		}
		void setTrackSeekTime(int trackindex, int usetrack, float time1, float time2, float time3, float time4, bool exists) override
		{
			struct LocalFunction {
				static void inSet(spine::AnimationState *state, int index, float time) {
					auto track = state->getCurrent(index);
					if (track == nullptr) return;

					track->setTrackTime(time);
				}
			};

			if (state == nullptr) return;

			if (usetrack == 1)
			{
				LocalFunction::inSet(state, trackindex + 0, time1);
			}
			else
			{
				LocalFunction::inSet(state, trackindex + 0, time1);
				LocalFunction::inSet(state, trackindex + 1, time2);
				LocalFunction::inSet(state, trackindex + 2, time3);
				LocalFunction::inSet(state, trackindex + 3, time4);
			}
		}
		void setMixDuration(float duration) override
		{
			if (state == nullptr) return;

			state->getData()->setDefaultMix(duration);
		}
		void setSkin(const wstring &skinname) override {
			if (skeleton == nullptr) return;

			auto ss = convertWStringToSpine(skinname);
			auto skin = skeleton->getData()->findSkin(*ss);
			if (skin != nullptr)
			{
				skeleton->setSkin(skin);
				skeleton->setToSetupPose();
			}
			delete ss;
		}
		Matrix4 getBoneMatrix(const wstring &bonename) override
		{
			if (skeleton == nullptr) return Misc::IdentityMatrix4;
			if (state == nullptr) return Misc::IdentityMatrix4;

			// cache
			spine::Bone *bone = nullptr;
			bool cachehit = false;
			auto itr = boneCacheList.find(bonename);
			if (itr != end(boneCacheList)) {
				bone = itr->second;
				cachehit = true;
			}
			if (bone == nullptr) {
				auto ss = convertWStringToSpine(bonename);
				bone = skeleton->findBone(*ss);
				delete ss;
			}

			if (!cachehit) {
				boneCacheList[bonename] = bone;
			}

			if (bone == nullptr) return Misc::IdentityMatrix4;
			return extractWorldMatrix(bone);
		}
	};

	class SpinePhysicsRenderer : public SpineRenderer
	{
	protected:
		PhysicsMatrix4 extractWorldMatrix(spine::Bone *bone)
		{
			Matrix4 m = Misc::IdentityMatrix4;

			m._11 = bone->getA();
			m._12 = bone->getC();
			m._21 = bone->getB();
			m._22 = bone->getD();
			m._41 = (bone->getWorldX() - skeleton->getX()) * antiScaling;
			m._42 = (bone->getWorldY() - skeleton->getY()) * antiScaling;

			float sx = skeleton->getScaleX() < 0.0f ? -1.0f : +1.0f;
			float sy = skeleton->getScaleY() < 0.0f ? -1.0f : +1.0f;	// spplied YDown
			Misc::m4PushScale(m, sx, sy, 1.0);

			return m;
		}
		// get setting value
		double getSettingFloat(const map<wstring, wstring> &dic, int index, const wstring &key, double def_, double min_, double max_)
		{

			wstring s = L"";
			auto itr = dic.find(key);
			if (itr != end(dic)) {
				s = itr->second;
			}

			auto sl = Misc::divideString(',', s);
			if (index < (int)sl->size()) s = sl->at(index);
			delete sl;

			auto f = Misc::toFloatDef(s, (float)def_);
			return fmin(fmax(f, min_), max_);
		}
		wstring getSettingString(const map<wstring, wstring> &dic, const wstring &key, const wstring &def_)
		{
			wstring s = L"";
			auto itr = dic.find(key);
			if (itr != end(dic)) {
				s = itr->second;
			}
			if (s == L"") s = def_;
			return s;
		}
		// spring bone group
		void addBone(PhysicsGroupSpringBone *g, spine::Bone *bone, PhysicsNodeBone *parent)
		{
			auto &data = bone->getData();
			if (parent != nullptr)
			{
				auto &spinestr = data.getName();
				auto s = convertSpineToWString(spinestr);

				if (s.find(L"@end") > -1) return;  // terminate code
				if (s.find(L"@") > -1) return;     // when other code exists, safe exit.
			}

			PhysicsNodeBone *b = new PhysicsNodeBone(parent);
			b->userObject = bone;
			b->localAngle = data.getRotation() / 360.0;
			b->localLength = data.getLength() * antiScaling;
			b->localPosition.x = data.getX() * antiScaling;
			b->localPosition.y = data.getY() * antiScaling;
			b->localScaleX = data.getScaleX();
			b->localScaleY = data.getScaleY();

			g->addBone(b);

			auto &children = bone->getChildren();
			for (auto i = 0; i < (int)children.size(); i++) {
				addBone(g, children[i], b);
			}

			// create terminate bone
			if (children.size() == 0)
			{
				parent = b;
				b = new PhysicsNodeBone(parent);
				b->userObject = nullptr;
				b->localAngle = 0;
				b->localLength = 100;
				b->localPosition.x = parent->localLength;
				b->localPosition.y = 0;
				g->addBone(b);
			}
		}
		void addSpring(PhysicsGroupSpringBone *g, PhysicsNodeBone *bone, PhysicsSpringBone *spring)
		{
			for(auto b : bone->children)
			{
				auto s = new PhysicsSpringBone(g, spring, bone, b);
				g->addSpring(s);
				addSpring(g, b, s);
			}
		}
		void addSpringGroup(const map<wstring, wstring> &dic, wstring name, wstring alias, spine::Bone *bone)
		{
			// need has parent
			auto root = bone->getParent();
			if (root == nullptr) return;

			if (alias == L"") alias = name;

			// enable / disable
			if (getSettingString(dic, alias + L".enable", L"true") == L"false") return;

			auto g = new PhysicsGroupSpringBone();
			g->name = name;
			g->aliasName = alias;
			g->rootUserObject = root;

			auto s = getSettingString(dic, alias + L".mode", L"bone");
			if (s == L"strpoint") g->mode = PhysicsGroupSpringBoneMode::StrPoint;
			if (s == L"point") g->mode = PhysicsGroupSpringBoneMode::Point;
			if (s == L"strbone") g->mode = PhysicsGroupSpringBoneMode::StrBone;
			if (s == L"bone") g->mode = PhysicsGroupSpringBoneMode::Bone;

			g->K = getSettingFloat(dic, 0, alias + L".k", g->K, 0.1, 100 * 100);
			g->damping = getSettingFloat(dic, 0, alias + L".damping", g->damping, 0.1, 100 * 100);
			g->mass = getSettingFloat(dic, 0, alias + L".mass", g->mass, 0.001, 1000);
			g->limitVelocity = getSettingFloat(dic, 0, alias + L".velocitylimit", g->limitVelocity, 1, 0xffff);   // 100 = physics frame
			g->gravity = getSettingFloat(dic, 0, alias + L".gravity", g->gravity, 0, 10000 * 100);
			g->gravityVector.x = getSettingFloat(dic, 0, alias + L".gravityvector", g->gravityVector.x, -1000, +1000);
			g->gravityVector.y = getSettingFloat(dic, 1, alias + L".gravityvector", g->gravityVector.y, -1000, +1000);
			g->gravityVector.z = getSettingFloat(dic, 2, alias + L".gravityvector", g->gravityVector.z, -1000, +1000);
			g->gravityVector.normalize();
			g->limitAngleMin = getSettingFloat(dic, 0, alias + L".limitangle", g->limitAngleMin, -0xff, 0);
			g->limitAngleMax = getSettingFloat(dic, 1, alias + L".limitangle", g->limitAngleMax, 0, +0xff);
			g->limitMoveRatio = getSettingFloat(dic, 0, alias + L".limitmove", g->limitMoveRatio, 0, +0xff);
			
			g->forceRatio = getSettingFloat(dic, 0, alias + L".force", g->forceRatio, 0.0, 1.0);
			g->alpha = getSettingFloat(dic, 0, alias + L".alpha", g->alpha, -10.0, 10.0);
			g->forceWiggle = getSettingFloat(dic, 0, alias + L".wiggle", g->forceWiggle, 0.0, 1.0);

			// private
			g->forceRotateRatio = getSettingFloat(dic, 0, alias + L".forcerotate", g->forceRotateRatio, 0.0, 10.0);

			addBone(g, bone, nullptr);
			addSpring(g, g->getRootBone(), nullptr);

			g->rootMatrix = extractWorldMatrix(root);
			g->setupNatural();

			//auto o = new unique_ptr(g);
			physicsGroups.emplace_back(g);
		}
		// element bone group
		void addElementGroup(const map<wstring, wstring> &dic, wstring name, wstring alias, spine::Bone *bone) 
		{
			struct InnerFunc {
				static int getMask(const wstring &s) 
				{
					if (s == L"none") return 0;
					auto m = 0;
					if (s.find(L"x") >= 0) m += 1;
					if (s.find(L"y") >= 0) m += 2;
					if (s.find(L"z") >= 0) m += 3;
					return m;
				}
			};
			if (alias == L"") alias = name;

			// enable / disable
			if (getSettingString(dic, alias + L".enable", L"true") == L"false") return;

			auto g = new PhysicsGroupElementBone();
			g->name = name;
			g->aliasName = alias;
			g->userObject = bone;

			g->getPosition()->mask = InnerFunc::getMask(getSettingString(dic, alias + L".maskpos", L"xyz"));
			g->getScale()->mask = InnerFunc::getMask(getSettingString(dic, alias + L".maskscale", L"xyz"));
			g->getRotate()->mask = InnerFunc::getMask(getSettingString(dic, alias + L".maskrot", L"xyz"));

			g->K = getSettingFloat(dic, 0, alias + L".k", g->K, 0.1, 100 * 100);
			g->Damping = getSettingFloat(dic, 0, alias + L".damping", g->Damping, 0.1, 100 * 100);
			g->Mass = getSettingFloat(dic, 0, alias + L".mass", g->Mass, 0.001, 1000);
			g->LimitVelocity = getSettingFloat(dic, 0, alias + L".limitvelocity", g->LimitVelocity, 1, 0xffff);   // 100 = physics frame
			auto a = (float)getSettingFloat(dic, 0, alias + L".alpha", 1.0, 0.0, +2.0);
			g->getPosition()->alpha = a;
			g->getScale()->alpha = a;
			g->getRotate()->alpha = a;

			physicsGroups.emplace_back(g);
		}
		// apply bones
		void updateWorldMatrix(spine::Bone *bone)
		{
			if (bone == nullptr) return;

			bone->updateWorldTransform();

			auto &children = bone->getChildren();
			for (auto i = 0; i < (int)children.size(); i++) {
				updateWorldMatrix(children[i]);
			}
		}
		float blendRotate(float r1, float r2, float alpha)
		{
			r2 = r2 - r1;
			r2 -= (16384 - (int)(16384.499999999996 - r2 / 360)) * 360;
			r2 = r1 + r2 * alpha;
			r2 -= (16384 - (int)(16384.499999999996 - r2 / 360)) * 360;

			return r2;
		} 
		void applyPhysicsBone(PhysicsGroupSpringBone *g, spine::Skeleton *skeleton)
		{
			auto alpha = (float)g->alpha;
			double rs = 1.0 / antiScaling;

			for(auto &n : g->nodes)
			{
				auto b = static_cast<PhysicsNodeBone*>(n.get());
				if (b->userObject != nullptr)
				{
					auto sb = reinterpret_cast<spine::Bone*>(b->userObject);
					switch (g->mode)
					{
					case PhysicsGroupSpringBoneMode::Bone:
						sb->setRotation(blendRotate(sb->getRotation(), sb->getRotation() + (float)(b->aLocalAngle - b->localAngle) * 360.0f, alpha));
						break;
					case PhysicsGroupSpringBoneMode::StrBone:
						sb->setRotation(blendRotate(sb->getRotation(), sb->getRotation() + (float)(b->aLocalAngle - b->localAngle) * 360.0f, alpha));
						sb->setX(sb->getX() + (float)((b->aLocalPosition.x - b->localPosition.x) * alpha * rs));
						sb->setY(sb->getY() + (float)((b->aLocalPosition.y - b->localPosition.y) * alpha * rs));
						break;
					case PhysicsGroupSpringBoneMode::Point:
					case PhysicsGroupSpringBoneMode::StrPoint:
						sb->setX(sb->getX() + (float)((b->aLocalPosition.x - b->localPosition.x) * alpha * rs));
						sb->setY(sb->getY() + (float)((b->aLocalPosition.y - b->localPosition.y) * alpha * rs));
						break;
					}
				}
			}
		}
		void updateMatrixBone(spine::Bone *bone) {
			if (bone == nullptr) return;

			bone->updateWorldTransform();

			auto &list = bone->getChildren();
			int n = list.size();
			for (auto i = 0; i < n; i++)
			{
				updateMatrixBone(list[i]);
			}
		}
	public:
		void update(double deltatime, double deltaphysics=-1) override
		{
			if (state == nullptr) return;
			if (skeleton == nullptr) return;

			state->update((float)deltatime);
			preparePhysics();				// Prepare physics target bones. Set setup-pose.
			state->apply(*skeleton);
			skeleton->updateWorldTransform();

			// physics
			if (physicsGroups.size() > 0) {
				if (deltaphysics < 0) deltaphysics = deltatime;
				applyPhysics(deltaphysics);
			}
		}
		void addPhysicsForce(const wstring &target, double x, double y, double z) override
		{
			// safe check
			if (state == nullptr) return;
			if (skeleton == nullptr) return;

			if (target == L"none") return;

			PhysicsVector3 v;
			v.setVector(x, y, z);
			for(auto &g : physicsGroups)
			{
				if ((g->aliasName == target) || (target == L"all")) g->addForce(v);
			}
		}
		void setupPhysics(const vector<wstring> &setting) override
		{
			// do not forget to clear PhysicsGroups
			SpineRenderer::setupPhysics(setting);

			if (state == nullptr) return;
			if (skeleton == nullptr) return;

			map<wstring, wstring> settingdic;
			Misc::convertKeyValue(settingdic, setting);

			skeleton->updateWorldTransform();

			auto &bones = skeleton->getBones();
			for (auto i = 0; i < (int)bones.size(); i++) {
				auto b = bones[i];
				auto &spinestr = b->getData().getName();
				auto s = convertSpineToWString(spinestr);

				auto idx = s.find(L"@");
				if (idx == -1) continue;
				if (idx >= s.length()-1) continue;

				// name @ spring,aliasname
				wstring op = L"";
				wstring alias = L"";
				wstring name = s.substr(0, idx);
				auto sl = Misc::divideString(',', s.substr(idx + 1));

				if (sl->size() > 0) op = sl->at(0);
				if (sl->size() > 1) alias = sl->at(1);

				if (op == L"spring") addSpringGroup(settingdic, name, alias, b);
				if (op == L"element") addElementGroup(settingdic, name, alias, b);
				delete sl;
			}
		}
		void preparePhysics() override
		{
			// safe check
			if (state == nullptr) return;
			if (skeleton == nullptr) return;

			for (auto &g : physicsGroups)
			{
				switch (g->groupType)
				{
				case PhysicsGroupType::Spring:
					{
						auto gg = static_cast<PhysicsGroupSpringBone*>(g.get());
						for (auto &n : gg->nodes)
						{
							auto b = static_cast<PhysicsNodeBone*>(n.get());
							if (b->userObject != nullptr)
							{
								auto sb = reinterpret_cast<spine::Bone*>(b->userObject);
								switch (gg->mode)
								{
								case PhysicsGroupSpringBoneMode::Bone:
									sb->setRotation(sb->getData().getRotation());
									break;
								case PhysicsGroupSpringBoneMode::StrBone:
									sb->setRotation(sb->getData().getRotation());
									sb->setX(sb->getData().getX());
									sb->setY(sb->getData().getY());
									break;
								case PhysicsGroupSpringBoneMode::Point:
								case PhysicsGroupSpringBoneMode::StrPoint:
									sb->setX(sb->getData().getX());
									sb->setY(sb->getData().getY());
									break;
								}
							}
						}
					}
					break;
				case PhysicsGroupType::Element:
					{
						auto gg = static_cast<PhysicsGroupElementBone*>(g.get());
						auto sb = reinterpret_cast<spine::Bone*>(gg->userObject);
						if (sb != nullptr) {
							sb->setRotation(sb->getData().getRotation());
							sb->setX(sb->getData().getX());
							sb->setY(sb->getData().getY());
							sb->setScaleX(sb->getData().getScaleX());
							sb->setScaleY(sb->getData().getScaleY());
						}
					}
					break;
				}
			}
		}
		void applyPhysics(double deltatime) override
		{
			// safe check
			if (state == nullptr) return;
			if (skeleton == nullptr) return;

			bool worldupdate = false;
			for (auto &g : physicsGroups)
			{
				switch (g->groupType) {
				case PhysicsGroupType::Spring:
					{
						auto gg = static_cast<PhysicsGroupSpringBone*>(g.get());

						auto m = extractWorldMatrix(reinterpret_cast<spine::Bone*>(gg->rootUserObject));
						Misc::m43PushMatrix(m, transMatrix);
						gg->rootMatrix = m;
						gg->externalMatrix = physicsExternalMatrix;

						gg->update(deltatime);
						gg->apply();

						applyPhysicsBone(gg, skeleton);
						updateMatrixBone(reinterpret_cast<spine::Bone*>(gg->getRootBone()->userObject));
					}
					break;
				case PhysicsGroupType::Element:
					{
						auto gg = static_cast<PhysicsGroupElementBone*>(g.get());

						auto sb = reinterpret_cast<spine::Bone*>(gg->userObject);
						if (sb == nullptr) return;

						gg->getPosition()->Value.setVector(sb->getX(), sb->getY(), 0);
						gg->getScale()->Value.setVector(sb->getScaleX(), sb->getScaleY(), 0);
						gg->getRotate()->Value.setVector(0, 0, sb->getRotation() / 360.0);   // rotate : need 360deg = 1.0 scale

						gg->update(deltatime);
						gg->apply();

						sb->setX((float)gg->getPosition()->aValue.x);
						sb->setY((float)gg->getPosition()->aValue.y);
						sb->setScaleX((float)gg->getScale()->aValue.x);
						sb->setScaleY((float)gg->getScale()->aValue.y);
						sb->setRotation((float)gg->getRotate()->aValue.z * 360.0f);

						worldupdate = true;
					}
					break;
				}
			}

			if (worldupdate) skeleton->updateWorldTransform();
		}
		void appeasePhysics() override
		{
			if (state == nullptr) return;
			if (skeleton == nullptr) return;

			for(auto &g : physicsGroups)
			{
				g->appease();
			}
		}
	};
}

#endif