#pragma once

#include "../Base.h"
#include <Resource/Animation.h>
#include "../Generic/Image.h"

namespace gorgon {

	class Animation;

	Animation *LoadAnimation(Base *parent, File &File, std::istream &Data, int Size);
	void LoadAnimationEx(Animation &anim, Base *parent, File &File, std::istream &Data, int Size, std::function<bool(std::istream&, GID::Type, int)> loadextra=std::function<bool(std::istream&, GID::Type, int)>());

	class Animation : public gge::resource::Animation, public Base {
		friend void LoadAnimationEx(Animation &anim, gorgon::Base *parent, File &File, std::istream &Data, int Size, std::function<bool(std::istream&, GID::Type, int)> loadextra);
	public:

		void Add(Image &image, unsigned duration=42) {
			gge::resource::AnimationFrame f(duration, GetDuration(), &image);

			Frames.push_back(f);
			TotalLength+=duration;
			FrameCount++;
			gorgon::Base::Subitems.Add(image);
		}
		void Add(Image *image, unsigned duration=42) {
			if(image) Add(*image, duration);
		}

		void Insert(Image &image, int before, unsigned duration=42) {
			if(before<0) before=0;
			if(before>=FrameCount) {
				Add(image, duration);
				return;
			}

			gge::resource::AnimationFrame f(duration, GetDuration(), &image);

			Frames.push_back(f);
			for(int i=Frames.size()-1;i>before;i--) {
				Frames[i]=Frames[i-1];
			}
			Frames[before]=f;

			TotalLength+=duration;
			FrameCount++;
			gorgon::Base::Subitems.Insert(image, before);

			recalculate();
		}
		void Insert(Image *image, int before, unsigned duration=42) {
			if(image) Insert(*image, before, duration);
		}

		void Remove(int location) {
			if(location<0 || location>=FrameCount)
				return;

			for(int i=location;i<FrameCount-1;i++)
				Frames[i]=Frames[i+1];

			Frames.pop_back();

			gorgon::Base::Subitems.Remove(location);
			FrameCount--;
			recalculate();
		}

		void Delete(int location) {
			if(location<0 || location>=FrameCount)
				return;

			Image &img=dynamic_cast<Image&>(gorgon::Base::Subitems[location]);
			Remove(location);
			delete &img;
		}

		void Move(int from, int to) {
			if(from<0 || from>=FrameCount)
				return;
			if(to<0 || to>FrameCount)
				return;

			gge::resource::AnimationFrame f=Frames[from];

			Remove(from);

			Insert(dynamic_cast<Image*>(f.Image), to, f.Duration);
		}

		void SetDuration(int location, unsigned duration) {
			if(location<0 || location>=FrameCount)
				return;

			Frames[location].Duration=duration;
			recalculate();
		}

		void Clear() {
			Frames.clear();
			gorgon::Base::Subitems.Clear();
			FrameCount=0;
			TotalLength=0;
		}

		void Destroy() {
			Frames.clear();
			gorgon::Base::Subitems.Destroy();
			FrameCount=0;
			TotalLength=0;
		}

		virtual void Save(std::ostream &out){
			Save(out, gge::resource::GID::Animation, std::function<void(std::ostream &)>());
		}

		virtual void Save(std::ostream &out, GID::Type mygid, std::function<void(std::ostream &)> saveextra) {
			auto start=WriteObjectStart(out, *this, mygid);

			WriteNames(out, *this, gge::resource::GID::Animation_Names, gge::resource::GID::Animation_Name);

			AdditionalSave(out);

			WriteSubItems(out, *this);

			auto timestart=WriteSectionStart(out, gge::resource::GID::Animation_Durations);
			for(auto frame=Frames.begin();frame!=Frames.end();++frame) {
				WriteTo(out, frame->Duration);
			}
			WriteEnd(out, timestart);

			if(saveextra)
				saveextra(out);

			WriteEnd(out, start);
		}

		virtual GID::Type GetGID() const {
			return gge::resource::Animation::GetGID();
		}

		const std::vector<gge::resource::AnimationFrame> &GetFrames() const {
			return Frames;
		}

	protected:
		void recalculate() {
			unsigned t=0;
			for(auto frame=Frames.begin();frame!=Frames.end();++frame) {
				frame->Start=t;				
				t+=frame->Duration;
			}
			TotalLength=t;
		}

		virtual void AdditionalSave(std::ostream &out) {}
		virtual bool AdditionalLoad(GID::Type gid, std::istream &in, int size) {
			return false;
		}
	};

}