#include "Animation.h"
#include <Resource/DataArray.h>
#include <utility>
#include <Gorgon/Game/Animation.h>
#include <Graphics/Canvas.h>

namespace assets
{
	gorgon::File gfile;

	static gge::graphics::RGBint bodycolors[]={ 
		0xfff3c3a3,
		0xfff3e3c3,
		0xff604224,
		0xffeede84,
		0xfff0e8d8,
	};
	static gge::graphics::RGBint eyecolors[]={ 
		0xff493929,
		0xffb0c084,
	};
	static gge::graphics::RGBint haircolors[]={ 
		0xff221009,
		0xffd7a443,
		0xffa77423,
		0xffffeeb0,
	};

	std::map<std::string, assets::AnimationProvider*> AnimationProvider::list;

	Animation::Animation()
	{
		for(int i = 0; i < 4; ++i)
		{
			directions[i] = NULL;
		}
	}

	//clips a value between 0-255 to prevent overflowing
	unsigned char clip(double v) {
		return (unsigned char)std::min(std::max(v+0.5,0.),255.);
	}

	//Switches body color, both skin and eye. If your model is different than base, you may need to change base colors. 
	//Notice that this will work for blue or green eyes, otherwise it would be harder to detect eyes
	template<class C_>
	void BodyColorSwitch(C_ &canvas, gge::graphics::RGBint bodytarget, gge::graphics::RGBint eyetarget, gge::graphics::RGBint bodybase=0xfff3c3a3, gge::graphics::RGBint eyebase=0xff50d4ec) {
		//extract base color components
		double cr=(double)bodytarget.r/bodybase.r,cg=(double)bodytarget.g/bodybase.g,cb=(double)bodytarget.b/bodybase.b;

		//same for the eyes
		double ecr=(double)eyetarget.r/eyebase.r,ecg=(double)eyetarget.g/eyebase.g,ecb=(double)eyetarget.b/eyebase.b;

		//for all pixels, determined by GetHeight and GetWidth functions, change them as you need
		for(int y=0;y<canvas.GetHeight();y++) {
			for(int x=0;x<canvas.GetWidth();x++) {
				//In skin color r>>g and r>>b
				if(canvas(x,y).r>canvas(x,y).g+4 && canvas(x,y).r>canvas(x,y).b+5) {
					canvas(x,y).r=clip(cr*canvas(x,y).r);
					canvas(x,y).g=clip(cg*canvas(x,y).g);
					canvas(x,y).b=clip(cb*canvas(x,y).b);
				}
				//if g>>>r or b>>>r we probably have an eye color
				else if(canvas(x,y).r+10<canvas(x,y).g || canvas(x,y).r+10<canvas(x,y).b) {
					canvas(x,y).r=clip(ecr*canvas(x,y).r);
					canvas(x,y).g=clip(ecg*canvas(x,y).g);
					canvas(x,y).b=clip(ecb*canvas(x,y).b);
				}
				//rest is the white parts of the eyes
			}
		}
	}

	//This function switches the hair color (can be another part as which will totally be recolored), as before base color is important
	//it is designed for the blonde hair wulax had provided
	template<class C_>
	void HairColorSwitch(C_ &canvas, gge::graphics::RGBint target, gge::graphics::RGBint base=0xffa77423) {
		//same as body color switch except without restrictions
		double cr=(double)target.r/base.r,cg=(double)target.g/base.g,cb=(double)target.b/base.b;

		for(int y=0;y<canvas.GetHeight();y++) {
			for(int x=0;x<canvas.GetWidth();x++) {
				canvas(x,y).r=clip(cr*canvas(x,y).r);
				canvas(x,y).g=clip(cg*canvas(x,y).g);
				canvas(x,y).b=clip(cb*canvas(x,y).b);
			}
		}
	}

	void AnimationProvider::RecolorAll() {
		return;

		std::map<std::string, assets::AnimationProvider*> supported;

		for(auto it=list.begin();it!=list.end();++it) {
			if(it->second->type==BaseModel)
				supported[it->first]=it->second;
			else if(it->second->type==Hair)
				supported[it->first]=it->second;
		}

		for(auto it=supported.begin();it!=supported.end();++it) {
			if(it->second->type==BaseModel) {
				for(int i=0;i<5;i++) {
					for(int j=0;j<2;j++) {
						std::string id=it->first+"_"+std::string(1, (char)i+'a')+std::string(1, (char)j+'a');
						list[id]=&it->second->Recolor(id, bodycolors[i], eyecolors[j]);
					}
				}
			}
			else if(it->second->type==Hair) {
				for(int i=0;i<4;i++) {
					std::string id=it->first+"_"+std::string(1, (char)i+'a');
					list[id]=&it->second->Recolor(id, haircolors[i]);
				}
			}
		}
	}

	AnimationProvider &AnimationProvider::Recolor(std::string newid, gge::graphics::RGBint firstcolor, gge::graphics::RGBint secondcolor) {
		AnimationProvider &animp=*new AnimationProvider;
		animp.ID=newid;
		animp.type=type;
		animp.baseColor=firstcolor;
		animp.secondColor=secondcolor;
		animp.attachmentPoint=attachmentPoint;
		
		for(auto it=animations.begin();it!=animations.end();++it) {
			Animation *anim=new Animation;
			
			animp.animations[it->first]=anim;
			for(int i=0;i<4;i++) {
				if(it->second->directions[i]) {
					gorgon::Animation *animation=new gorgon::Animation;
					anim->directions[i]=animation;

					for(int f=0;f<it->second->directions[i]->GetFrameCount();f++) {
						gge::resource::Image &img=dynamic_cast<gge::resource::Image&>(it->second->directions[i]->Subitems[f]);

						graphics::Canvas &c=*new graphics::Canvas(img);

						if(type==BaseModel)
							BodyColorSwitch(c, firstcolor, secondcolor, baseColor, secondColor);
						else
							HairColorSwitch(c, firstcolor, baseColor);


						gorgon::Image &im=*new gorgon::Image(c);
						animation->Add(im, it->second->directions[i]->GetDuration(f));
						im.Prepare(gge::Main, gfile);

						delete &c;
					}
				}
			}
		}

		return animp;
	}

	
	void AnimationProvider::load( gge::resource::Folder &folder )
	{
		int mainFolderIndex = 0;
		int dataArrayIndex = 0;

		// first element is always a DataArray which contains animation info
		gge::resource::DataArray &dataArray = folder.Get<gge::resource::DataArray>(mainFolderIndex++);

		// since first element is DataArray of animation info 
		// folder.getCount() - 1 will give us number of different animations
		int numOfAnimations = folder.GetCount() - 1;

		ID				= dataArray.getString(dataArrayIndex++);
		attachmentPoint = dataArray.getPoint(dataArrayIndex++);
		type			= (ObjectType)dataArray.getInt(dataArrayIndex++);
		baseColor		= dataArray.getInt(dataArrayIndex++);
		secondColor		= dataArray.getInt(dataArrayIndex++);

		//!Should check if the animation is base or hair, if it is it should try to create color variations

		// iterate all animation folders
		for(int i = 0; i < numOfAnimations; ++i)
		{
			int animationFolderIndex = 0;

			// get next animation folder
			gge::resource::Folder &animationFolder = folder.Get<gge::resource::Folder>(mainFolderIndex++);

			// create a new directional animation object
			Animation *animation = new Animation;

			// get animation name from animation Info DataArray
			std::string animationName = dataArray.getString(dataArrayIndex++);

			// get all directional(4 directions) animations in a given animation folder
			for(int j = 0; j < 4; ++j)
			{
				animation->directions[j] = &animationFolder.Get<gge::resource::Animation>(animationFolderIndex++);
			}

			// insert them into animations map.
			// each element of animations map holds animation name
			// and directional animations associated with that particular animation
			animations.insert(std::pair<std::string, Animation*>(animationName, animation));
		}


	}


}

