#ifndef __DRAGANDDROP_H__
#define __DRAGANDDROP_H__

#include <wx/dnd.h>
#include <wx/xrc/xmlres.h>
//#include "glwindow.h"
//#include "rendermanager.h"
#include "scene.h"
#include "parameter.h"
#include <vector>
#include <wx/statbmp.h>
#include <wx/stattext.h>
#define TEST_NEW_DRAGDROP 1

namespace VRUT
{
class Camera;
	class SceneNodesDataObject : public wxDataObjectSimple
	{
	public:
		std::vector<NODE_ID> mNodes;
		SCENE_ID mSceneId;

		SceneNodesDataObject(SCENE_ID sceneId=SCENE_ID_NONE) : mSceneId(sceneId)
		{
			wxDataFormat dataFormat(wxT("VRUT_SceneNodes"));
			this->SetFormat(dataFormat);
		}

		void AddNode(NODE_ID id)
		{
			mNodes.push_back(id);
		}

		void SetSceneId(SCENE_ID id)
		{
			mSceneId = id;
		}

		SCENE_ID GetSceneId()
		{
			return mSceneId;
		}

		virtual bool GetDataHere(void *buf) const
		{
			((int *)buf)[0] = (int)mSceneId;
			for (size_t i=0; i < mNodes.size(); i++)
				((int *)buf)[i+1] = (int)mNodes[i];
			return true;
		}

		virtual size_t GetDataSize() const  
		{
			return sizeof(int) + mNodes.size()*sizeof(int);
		}

		virtual bool SetData(size_t _len, const void *buf) 
		{
			size_t len = _len/sizeof(int);
			if (len > 0)
				mSceneId = ((int *)buf)[0];
			mNodes.clear();
			for (size_t i=1; i < len; i++)
				mNodes.push_back(((int *)buf)[i]);
			return true;
		}
	};


	class MaterialDataObject : public wxDataObjectSimple
	{
		MATERIAL_ID material_id;
		SCENE_ID scene_id;

	public:

		MaterialDataObject(MATERIAL_ID material_id=MATERIAL_ID_NONE, SCENE_ID scene_id=SCENE_ID_NONE) : material_id(material_id), scene_id(scene_id)
		{
			wxDataFormat dataFormat(wxT("VRUT_Material"));
			this->SetFormat(dataFormat);
		}

		void SetMaterialId(MATERIAL_ID id)
		{
			material_id = id;
		}

		void SetSceneId(SCENE_ID id)
		{
			scene_id = id;
		}

		MATERIAL_ID GetMaterialId()
		{
			return material_id;
		}

		SCENE_ID GetSceneId()
		{
			return scene_id;
		}

		virtual bool GetDataHere(void *buf) const
		{
			((int *)buf)[0] = (int)material_id;
			((int *)buf)[1] = (int)scene_id;
			return true;
		}

		virtual size_t GetDataSize() const  
		{
			return 2*sizeof(int);
		}

		virtual bool SetData(size_t len, const void *buf) 
		{
			if (len > 0)
				material_id = ((int *)buf)[0];
			if (len > 1)
				scene_id = ((int *)buf)[1];
			return true;
		}
	};

	///GUI item acting like drop source
	class DropSourceControl : public wxWindow
	{
		wxDataObjectSimple *data;
		wxStaticBitmap *bitmap;
		wxStaticText *label;

		void OnLeftDown(wxMouseEvent &/*evt*/)
		{
#ifndef VRUT_HPC
			wxDropSource drop_source(*data, this);
			drop_source.DoDragDrop(wxDrag_AllowMove);
#endif
		}

		///This method must be extended for supporting other formats than MaterialDataObject
		wxDataObjectSimple *CopyData(wxDataObjectSimple *src_data)
		{
			if (!src_data)
				return NULL;
			wxDataObjectSimple *dst_data;

			if (dynamic_cast<MaterialDataObject *>(src_data))
				dst_data = new MaterialDataObject();
			else if (dynamic_cast<SceneNodesDataObject *>(src_data))
				dst_data = new SceneNodesDataObject();
			else
				dst_data = new wxDataObjectSimple();

			size_t size = src_data->GetDataSize();
			void *buffer = malloc(size);

			src_data->GetDataHere(buffer);
			dst_data->SetData(size, buffer);
			dst_data->SetFormat(src_data->GetFormat());

			free(buffer);

			return dst_data;
		}

		void LocalInit(const wxString &textLabel = wxEmptyString, const wxBitmap &bitmapLabel = wxNullBitmap, wxDataObjectSimple *newData = NULL);

	public:

		virtual void SetToolTip(const wxString &tool_tip)
		{
			bitmap->SetToolTip(tool_tip);
		}

		// Default constructor (required for two-step construction when loading from XRC => default contructor -> create)
		DropSourceControl() : wxWindow()
		{
			bitmap = NULL;
			data = NULL;
			label = NULL;
		}

		///DropSourceControl can't take control over the *data and destroys it. It's because *data can be created in other context than the ~DropSourceControl is called.
		///So programmer has to delete *data himself after calling the constructor of DropSourceControl.
		DropSourceControl(wxWindow *parent, wxWindowID id, const wxString &label, const wxBitmap &bitmapLabel, wxDataObjectSimple *data) : wxWindow(parent, id)
		{
			LocalInit(label, bitmapLabel, data);
		}

		bool Create(wxWindow * parent, wxWindowID id, const wxPoint & pos = wxDefaultPosition, const wxSize & size = wxDefaultSize, long style = 0, const wxString & name = wxPanelNameStr);

		void SetData(wxDataObjectSimple *newData)
		{
			SAFE_DELETE(this->data);
			this->data = CopyData(newData);
		}

		void SetTextLabel(const wxString &textLabel)
		{
			label->SetLabel(textLabel);
		}

		void SetBitmapLabel(const wxBitmap &bitmapLabel)
		{
			bitmap->SetBitmap(bitmapLabel);
		}

		~DropSourceControl()
		{
			SAFE_DELETE(this->data);
		}

		DECLARE_DYNAMIC_CLASS(DropSourceControl) 
	};

	class DropSourceControlXmlHandler : public wxXmlResourceHandler
	{
		public:
			// Constructor.
			DropSourceControlXmlHandler()
			{
				AddWindowStyles();
			}

			// Creates the control and returns a pointer to it.
			virtual wxObject *DoCreateResource()
			{
				XRC_MAKE_INSTANCE(control, DropSourceControl)

				control->Create(
					m_parentAsWindow,
					GetID(), 
					GetPosition(),
					GetSize(),
					GetStyle(wxT("style")),
					GetName()
				);

				SetupWindow(control);

				return control;
			}

		// Returns true if we know how to create a control for the given node.
		virtual bool CanHandle(wxXmlNode *node)
		{
			// this function tells XRC system that this handler can parse
			// the <object class="MyControl"> tags
			return IsOfClass(node, wxT("DropSourceControl"));
		}

		// Register with wxWidgets' dynamic class subsystem.
		DECLARE_DYNAMIC_CLASS(DropSourceControlXmlHandler)
	};


#ifndef VRUT_HPC
	///Receiver for Drag&Drop items
	class VRUT_CORE_API DropTarget: public wxDropTarget
	{
	protected:
		wxWindow *target_item;
	private:
		void generateMaterialName(wxString &name, smart_pointer_v2<Scene> &scene);

		NODE_ID selectNodeFromScreen(smart_pointer_v2<Scene> &dst_scene, smart_pointer<Camera> &camera, int scrW, int scrH, int x, int y);

		void setMaterialFromIDs(MATERIAL_ID material_id, SCENE_ID scene_id, NODE_ID dst_node, smart_pointer_v2<Scene> &dst_scene, wxDragResult def);

	public:

		enum DataFormat {
			TEXT = 0x01,
			FILE = 0x02,
			MATERIAL = 0x04,
			SCENE_NODES = 0x08
		};

		bool render;
		bool isGLWindow;

		///constructor
		DropTarget(wxWindow *target_item, int data_format, bool render=true, bool isGLWindow=false) : target_item(target_item), render(render), isGLWindow(isGLWindow)
		{
			wxDataObjectComposite *dataobj = new wxDataObjectComposite();

			if (data_format & TEXT)
				dataobj->Add(new wxTextDataObject);
			if (data_format & FILE)
				dataobj->Add(new wxFileDataObject);
			if (data_format & MATERIAL)
				dataobj->Add(new MaterialDataObject);
			if (data_format & SCENE_NODES)
				dataobj->Add(new SceneNodesDataObject);

			SetDataObject(dataobj);
		}

		virtual wxDragResult OnDragOver(wxCoord x, wxCoord y, wxDragResult def);

		///Process drop functionality
		virtual wxDragResult OnData(wxCoord x, wxCoord y, wxDragResult def);

	};

	class ParamDropTarget: public DropTarget
	{
	protected:
		Parameter::ParameterIdentifier mParamIdentifier;
	public:
		ParamDropTarget(wxWindow *control, int dataFormat, const Parameter::ParameterIdentifier &pi) :  DropTarget(control, dataFormat), mParamIdentifier(pi) {}

		virtual wxDragResult OnDragOver(wxCoord x, wxCoord y, wxDragResult def) {
#ifdef __WXMSW__ //Windows - I don't understand why it not works on Linux...
			if ( !GetData() )
				return wxDragNone;
#endif
			return def;
		}
	};

#endif //VRUT_HPC
}

#endif //__DRAGANDDROP_H__
