#pragma once

#include <Resource/DataArray.h>
#include "../Font.h"

namespace gorgon {

	////Link data
	class LinkData : public gge::resource::IData {
	public:
		////02030C02h (Basic, Data resource, Data types, integer)
		virtual int GetGID() const { return gge::resource::GID::Data_Link; }
		int getObjectGID() { if(value) return value->GetGID(); return 0; }
		gorgon::Base *value;
		gge::utils::SGuid guid;

		LinkData(gge::utils::SGuid guid) : value(NULL) { this->guid=guid; }
		LinkData(gorgon::Base *obj) : value(obj) { }
		LinkData(gorgon::Base &obj) : value(&obj) { }
		operator gorgon::Base *() { return value; }
		operator gorgon::Base &() { if(!value) throw std::runtime_error("Target is not set"); return *value; }
		gorgon::Base &Get() { if(!value) throw std::runtime_error("Target is not set"); return *value; }

		//after loading completes this will be called manually
		void Prepare(gorgon::File &File) {
			value=File.Root().FindObject(guid);
		}
	};

	//Font data
	class FontData : public gge::resource::IData {
	public:
		////03300C01h (Gaming, FontTheme, Data types, font)
		virtual int GetGID() const { return 0x03300C01; }
		gge::Font value;
		gorgon::FontInitiator initiator;

		FontData() { }
		FontData(const gge::Font &font) { value=font; }
		FontData(const gorgon::FontInitiator &font) { initiator=font; }
		operator gge::Font() { return value; }
		void Prepare(File &File) {
			value=(gge::Font)initiator;
		}
	};


	class DataArray;

	DataArray *LoadData(Base *parent, File &File, std::istream &Data, int Size);
	class DataArray : public gge::resource::DataArray, public Base {
	protected:
		template<class T_, class D_>
		void savethis(std::ostream &out, gge::resource::IData &data) {
			D_ &d=dynamic_cast<D_&>(data);
			auto start=WriteSectionStart(out, d.GetGID());
			WriteTo(out, d.operator T_());
			WriteEnd(out, start);
		}
	public:
		gge::resource::IData &Add(gge::resource::IData &item) {
			Data.Add(item);

			return item;
		}

		using gge::resource::DataArray::Add;

		LinkData &Add(gorgon::Base &obj) {
			auto &l=*new LinkData(obj);

			Data.Add(l);

			return l;
		}

		LinkData &Add(gorgon::Base *obj) {
			auto &l=*new LinkData(obj);

			Data.Add(l);

			return l;
		}

		template<class T_>
		T_ &Get(int index) {
			return dynamic_cast<T_&>(Data[index]);
		}

		template<class T_>
		T_ &Get(const std::string index) {
			for(auto it=Data.First();it.IsValid();it.Next()) {
				if(it->name==index)
					return dynamic_cast<T_&>(*it);
			}

			throw std::runtime_error("Index not found");
		}

		void Remove(int ind) {
			gorgon::Base::Subitems.Remove(ind);
			Data.Remove(ind);
		}

		void Delete(int ind) {
			gorgon::Base::Subitems.Remove(ind);
			Data.Delete(ind);
		}

		void RemoveAll() {
			while(Data.GetCount()) {
				Remove(0);
			}
		}

		void DeleteAll() {
			while(Data.GetCount()) {
				Remove(0);
			}
		}

		virtual void Save(std::ostream &out) {
			auto start=WriteObjectStart(out, *this);

			//names
			auto namesstart=WriteSectionStart(out, gge::resource::GID::Data_Names);
			for(auto it=Data.First();it.IsValid();it.Next()) {
				auto start=WriteSectionStart(out, gge::resource::GID::Data_Name);
				WriteTo(out, it->name);
				WriteEnd(out, start);
			}
			WriteEnd(out, namesstart);

			//data
			for(auto it=Data.First();it.IsValid();it.Next()) {
				switch(it->GetGID()) {
				case gge::resource::GID::Data_Int:
					savethis<int, gge::resource::IntegerData>(out, *it);
					break;
				case gge::resource::GID::Data_Color:
					savethis<gge::graphics::RGBint, gge::resource::ColorData>(out, *it);
					break;
				case gge::resource::GID::Data_Float:
					savethis<float, gge::resource::FloatData>(out, *it);
					break;
				case gge::resource::GID::Data_Text:
					savethis<const char*, gge::resource::StringData>(out, *it);
					break;
				case gge::resource::GID::Data_Point:
					savethis<gge::utils::Point, gge::resource::PointData>(out, *it);
					break;
				case gge::resource::GID::Data_Size:
					savethis<gge::utils::Size, gge::resource::SizeData>(out, *it);
					break;
				case gge::resource::GID::Data_Rect:
					savethis<gge::utils::Rectangle, gge::resource::RectangleData>(out, *it);
					break;
				case gge::resource::GID::Data_Bounds:
					savethis<gge::utils::Bounds, gge::resource::BoundsData>(out, *it);
					break;
				case gge::resource::GID::Data_Margins:
					savethis<gge::utils::Margins, gge::resource::MarginsData>(out, *it);
					break;
				case gge::resource::GID::Data_Font:
					{
						gge::resource::FontData &f=dynamic_cast<gge::resource::FontData &>(*it);
	
						auto start=WriteSectionStart(out, gge::resource::GID::Data_Font);

						SaveObject(out, f.value);

						WriteEnd(out, start);
					}

					break;
				case gge::resource::GID::Data_Link:
					{						
						LinkData &l=dynamic_cast<LinkData&>(*it);

						auto start=WriteSectionStart(out, gge::resource::GID::Data_Link);

						if(l.value) {
							l.value->GetGuid().Save(out);
						}
						else {
							gge::utils::SGuid::Empty.Save(out);
						}

						WriteEnd(out, start);
					}

					break;
				}
			}

			WriteEnd(out, start);
		}
		virtual void Resolve(File &file) {
			for(auto data=Data.First();data.IsValid();data.Next()) {
				switch(data->GetGID()) {
				case gge::resource::GID::Data_Link:
					dynamic_cast<LinkData&>(*data).Prepare(file);
					break;
				case gge::resource::GID::Data_Font:
					dynamic_cast<FontData&>(*data).Prepare(file);
					break;
				}
			}
		}
		virtual GID::Type GetGID() const { return gge::resource::DataArray::GetGID(); }

		////Returns resource object from a link
		gorgon::Base &getLink(int Index) { return dynamic_cast<LinkData&>(Data[Index]).Get(); }
		////Returns resource object from a link
		gorgon::Base *getLinkPtr(int Index) { return dynamic_cast<LinkData&>(Data[Index]).value; }
		////Returns if the link is set and safe to read
		bool checkLink(int Index) { return dynamic_cast<LinkData&>(Data[Index]).value!=NULL; }

	};

}