#pragma once

#include "Base.h"
#include <Resource/Definitions.h>

namespace gorgon {

	class Link;

	static Link *LoadLink(Base *parent, File &file, std::istream &Data, int Size);

	class Link : public Base {
		friend Link *LoadLink(Base *parent, File &file, std::istream &Data, int Size);
	public:

		Link() : target(NULL) {
		}

		Link(Base &target, const std::string &caption="") : target(&target) { this->caption=caption; }

		Link(Base *target, const std::string &caption="") : target(target) { this->caption=caption; }

		//dangerous, use with care
		Link(gge::resource::Base &target, const std::string &caption="") : 
			target(dynamic_cast<Base*>(&target)) { this->caption=caption; }

		Link(gge::resource::Base *target, const std::string &caption="") : target(dynamic_cast<Base*>(target)) { this->caption=caption; }

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

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

			auto targetstart=WriteSectionStart(out, gge::resource::GID::LinkNode_Target);

			if(target)
				target->GetGuid().Save(out);
			else
				gge::utils::SGuid::Empty.Save(out);

			WriteEnd(out, targetstart);

			WriteEnd(out, start);
		}

		bool HasTarget() const {
			return target!=NULL;
		}

		template<class T_>
		T_ &Get() {
			return dynamic_cast<T_&>(*target);
		}

		template<class T_>
		T_ *GetPtr() {
			return dynamic_cast<T_*>(target);
		}

		Base &GetTarget() {
			return *target;
		}

		Base *GetTargetPtr() {
			return target;
		}

		void SetTarget(Base &target) {
			this->target=&target;
		}

		void SetTarget(Base *target) {
			this->target=target;
		}

		void RemoveTarget() {
			this->target=NULL;
		}

		GID::Type GetTargetGID() const {
			if(target)
				return target->GetGID();
			else
				return 0;
		}

		virtual void Resolve(File &file) {
			target=file.FindObject(target_guid);
		}

	protected:
		Base *target;
		gge::utils::SGuid target_guid;
	};

	static Link *LoadLink(Base *parent, File &file, std::istream &in, int Size) {
		Link &link=*new Link;
		if(parent)
			link.SetParent(*parent);
		else
			link.RemoveParent();


		std::vector<namecaption> names;

		LoadEntries(in, Size, file, link, [&](GID::Type &gid, int size) -> bool {
			switch(gid) {
			case gge::resource::GID::LinkNode_Target:
				link.target_guid.Load(in);
				return true;
			}

			return false;
		}, names);

		return &link;
	}

}

