/////////////////////////////////////////////////////////////
// CINEMA SDK : MENU PLUGINS															 //
/////////////////////////////////////////////////////////////
// VERSION    : CINEMA 4D																	 //
/////////////////////////////////////////////////////////////
// (c) 1989-2002 MAXON Computer GmbH, all rights reserved	 //
/////////////////////////////////////////////////////////////

// example code for a menu/manager plugin

// be sure to use a unique ID obtained from www.plugincafe.com
#define ID_ASYNCTEST 1000955

#include "c4d.h"
#include "gradientuserarea.h"
#include "c4d_symbols.h"

enum
{
	GADGET_ADDROW = 5000,
	GADGET_SUBROW,
	GROUP_DYNAMIC,
	GROUP_SCROLL,

	GADGET_DRAG = 6000,

	_dummy
};

class SDKGradientArea : public GeUserArea
{
	public:
		SDKGradientGadget ggtmp;
		SDKGradient	grad[MAXGRADIENT];
		LONG				count,interpolation,type;

		SDKGradientArea(void);

		virtual Bool Init(void);
		virtual Bool GetMinSize(LONG &w,LONG &h);
		virtual void Sized(LONG w,LONG h);
		virtual void Draw(LONG x1,LONG y1,LONG x2,LONG y2);
		virtual Bool InputEvent(const BaseContainer &msg);
};

SDKGradientArea::SDKGradientArea(void)
{
	LONG i;
	for (i=0; i<MAXGRADIENT; i++)
		grad[i].id=i;

	grad[0].col = Vector(1.0,1.0,0.0);
	grad[1].col = Vector(1.0,0.0,0.0);
	grad[0].pos = 0.0;
	grad[1].pos = 1.0;

	count=2;
	interpolation=4;
	type=0;
}

Bool SDKGradientArea::Init(void)
{
	ggtmp.Init(this,grad,&count,&interpolation,MAXGRADIENT);
	return TRUE;
}

Bool SDKGradientArea::GetMinSize(LONG &w,LONG &h)
{
	w = 100;
	h = 200;
	return TRUE;
}

void SDKGradientArea::Sized(LONG w,LONG h)
{
	ggtmp.InitDim(w,h);
}

void SDKGradientArea::Draw(LONG x1,LONG y1,LONG x2,LONG y2)
{
	if (!ggtmp.col) return;
	LONG w = ggtmp.col->GetBw();
	LONG h = ggtmp.col->GetBh();
	DrawBitmap(ggtmp.col,0,0,w,h,0,0,w,h,0);
}

Bool SDKGradientArea::InputEvent(const BaseContainer &msg)
{
	LONG dev = msg.GetLong(BFM_INPUT_DEVICE);
	LONG chn = msg.GetLong(BFM_INPUT_CHANNEL);
	if (dev==BFM_INPUT_MOUSE)
	{
		BaseContainer action(BFM_ACTION);
		action.SetLong(BFM_ACTION_ID,GetId());
		action.SetLong(BFM_ACTION_VALUE,0);

		if (chn==BFM_INPUT_MOUSELEFT)
		{
			LONG mxn,myn;
			LONG mx = msg.GetLong(BFM_INPUT_X);
			LONG my = msg.GetLong(BFM_INPUT_Y);
			Bool dc = msg.GetBool(BFM_INPUT_DOUBLECLICK);
			Global2Local(&mx,&my);

			if (ggtmp.MouseDown(mx,my,dc))
			{
				BaseContainer z;
				while (GetInputState(BFM_INPUT_MOUSE,BFM_INPUT_MOUSELEFT,z))
				{
					if (z.GetLong(BFM_INPUT_VALUE)==0) break;

					mxn = z.GetLong(BFM_INPUT_X);
					myn = z.GetLong(BFM_INPUT_Y);
					Global2Local(&mxn,&myn);

					mx=mxn; my=myn;
					ggtmp.MouseDrag(mx,my);
					Redraw();
					action.SetLong(BFM_ACTION_INDRAG,TRUE);
					SendParentMessage(action);
				}
			}
			Redraw();

			action.SetLong(BFM_ACTION_INDRAG,FALSE);
			SendParentMessage(action);
		}
		else if (chn==BFM_INPUT_MOUSEWHEEL)
		{
			Real per;
			if (ggtmp.GetPosition(&per))
			{
				per+=msg.GetLong(BFM_INPUT_VALUE)/120.0*0.01;
				per=FCut(per,0.0,1.0);
				ggtmp.SetPosition(per);
				Redraw();
				SendParentMessage(action);
			}
		}
		return TRUE;
	}
	return FALSE;
}

class AsyncDialog : public GeDialog
{
	private:
		LONG rows;
		String array_drag[100];
		BaseContainer links;

		void DoEnable(void);
		Bool CheckDropArea(LONG id,const BaseContainer &msg);
		void CreateDynamicGroup(void);
		void ReLayout(void);
		String GetStaticText(LONG i);

		SDKGradientArea sg;
		C4DGadget				*gradientarea;

	public:
		AsyncDialog(void);
		virtual Bool CreateLayout(void);
		virtual Bool InitValues(void);
		virtual Bool Command(LONG id,const BaseContainer &msg);
		virtual LONG Message(const BaseContainer &msg,BaseContainer &result);
		virtual Bool CoreMessage  (LONG id,const BaseContainer &msg);
};

enum
{
	IDC_OFFSET		= 1001,
	IDC_ACCESS		= 1002,
	IDC_GRADTEST	= 1003,
	IDC_XPOSITION	= 1004,
	IDC_XINTERPOL	= 1005
};

AsyncDialog::AsyncDialog(void)
{
	gradientarea=NULL;
	rows = 1;
}

Bool AsyncDialog::CreateLayout(void)
{
	// first call the parent instance
	Bool res = GeDialog::CreateLayout();

	SetTitle("GuiDemo C++");

	GroupBegin(0,BFH_SCALEFIT,5,0,"",0);
	{
		GroupBorderSpace(4,4,4,4);
		AddButton(GADGET_ADDROW,BFH_FIT,0,0,"add row");
		AddButton(GADGET_SUBROW,BFH_FIT,0,0,"sub row");
	}
	GroupEnd();

	GroupBegin(0,BFH_SCALEFIT,2,0,"",0);
	{
		GroupBegin(0,BFV_SCALEFIT|BFH_SCALEFIT,0,1,"Drop objects, tags, materials here",0);
		{
			GroupBorder(BORDER_GROUP_IN|BORDER_WITH_TITLE);
			GroupBorderSpace(4,4,4,4);

			ScrollGroupBegin(GROUP_SCROLL,BFH_SCALEFIT|BFV_SCALEFIT,SCROLLGROUP_VERT);
			{
				GroupBegin(GROUP_DYNAMIC,BFV_TOP|BFH_SCALEFIT,3,0,"",0);
				{
					CreateDynamicGroup();
				}
				GroupEnd();
			}
			GroupEnd();
		}
		GroupEnd();

		GroupBegin(0,BFV_SCALEFIT|BFH_SCALEFIT,0,2,"",0);
		{
			gradientarea = AddUserArea(IDC_GRADTEST,BFH_SCALEFIT);	
			if (gradientarea) AttachUserArea(sg,gradientarea);

			GroupBegin(0,BFH_LEFT,2,0,"",0);
			{
				AddStaticText(0,BFH_LEFT,0,0,GeLoadString(IDS_INTERPOLATION),0);
				AddComboBox(IDC_XINTERPOL, BFH_SCALEFIT);
					AddChild(IDC_XINTERPOL, 0, GeLoadString(IDS_NONE));
					AddChild(IDC_XINTERPOL, 1, GeLoadString(IDS_LINEAR));
					AddChild(IDC_XINTERPOL, 2, GeLoadString(IDS_EXPUP));
					AddChild(IDC_XINTERPOL, 3, GeLoadString(IDS_EXPDOWN));
					AddChild(IDC_XINTERPOL, 4, GeLoadString(IDS_SMOOTH));

				AddStaticText(0,BFH_LEFT,0,0,GeLoadString(IDS_POSITION),0);
				AddEditNumberArrows(IDC_XPOSITION,BFH_LEFT);
			}
			GroupEnd();
		}
		GroupEnd();
	}
	GroupEnd();

	MenuFlushAll();	
		MenuSubBegin("Menu1");
			MenuAddString(GADGET_ADDROW,"add row");
			MenuAddString(GADGET_SUBROW,"sub row");
			MenuAddSeparator();
			MenuSubBegin("SubMenu1");
				MenuAddCommand(1000959); // atom object
				MenuAddCommand(1000960); // rounded tube object
				MenuAddCommand(1000961); // spherize object
				MenuSubBegin("SubMenu2");
					MenuAddCommand(1000962); // double circle object
					MenuAddCommand(1000963); // triangulate object
				MenuSubEnd();
			MenuSubEnd();
		MenuSubEnd();
	MenuFinished();

	return res;
}

void AsyncDialog::DoEnable(void)
{
	Real pos=0.0;
	Bool ok=sg.ggtmp.GetPosition(&pos);
	Enable(IDC_XPOSITION,ok);
	Enable(IDC_XINTERPOL,ok);
}

void AsyncDialog::ReLayout(void)
{
	LayoutFlushGroup(GROUP_DYNAMIC);
	CreateDynamicGroup();
	LayoutChanged(GROUP_DYNAMIC);
}

Bool AsyncDialog::InitValues(void)
{
	// first call the parent instance
	if (!GeDialog::InitValues()) return FALSE;

	SetLong(IDC_OFFSET,100,0,100,1);
	SetBool(IDC_ACCESS,TRUE);

	Real pos=0.0;
	sg.ggtmp.GetPosition(&pos);

	SetPercent(IDC_XPOSITION,pos);
	SetLong(IDC_XINTERPOL,sg.interpolation);

	DoEnable();

	return TRUE;
}

Bool AsyncDialog::CheckDropArea(LONG id,const BaseContainer &msg)
{
	LONG x,y,w,h,dx,dy;
	GetDragPosition(msg,&dx,&dy);
	GetItemDim(id,&x,&y,&w,&h);
	return dy>y && dy<y+h;
}

void AsyncDialog::CreateDynamicGroup(void)
{
	LONG i;
	for (i=0;i<rows;i++)
	{
		AddCheckbox(0,BFH_LEFT,0,0,"Rows "+LongToString(i+1));
		AddStaticText(GADGET_DRAG+i,BFH_SCALEFIT,260,0,GetStaticText(i),BORDER_THIN_IN);

		AddEditSlider(0,BFH_SCALEFIT,0,0);
	}
}

Bool AsyncDialog::Command(LONG id,const BaseContainer &msg)
{
	switch (id)
	{
		case GADGET_ADDROW:
			if (rows<100) rows++;
			ReLayout();
			break;

		case GADGET_SUBROW:
			if (rows>1)
			{
				rows--;
				ReLayout();
			}
			break;

		case IDC_GRADTEST:
			InitValues();
			break;

		case IDC_XPOSITION:
			sg.ggtmp.SetPosition(msg.GetReal(BFM_ACTION_VALUE));
			sg.Redraw();
			break;

		case IDC_XINTERPOL:
			sg.interpolation = msg.GetLong(BFM_ACTION_VALUE);
			sg.ggtmp.CalcImage();
			sg.Redraw();
			break;
	}
	return TRUE;
}

static String GenText(BaseList2D *bl)
{
	String str;
	if (bl->IsInstanceOf(Obase))
		str = "BaseObject";
	else if (bl->IsInstanceOf(Tbase))
		str = "BaseTag";
	else if (bl->IsInstanceOf(Mbase))
		str = "BaseMaterial";
	else if (bl->IsInstanceOf(KEbase))
		str = "BaseKey";
	else if (bl->IsInstanceOf(SEbase))
		str = "BaseSequence";
	else if (bl->IsInstanceOf(TEbase))
		str = "BaseTrack";
	else if (bl->IsInstanceOf(GVbase))
		str = "BaseNode";
	else
		return "Unknown object";

	return str+" "+bl->GetName()+" ("+LongToString(bl->GetType())+")";
}

String AsyncDialog::GetStaticText(LONG i)
{
	BaseList2D *bl = links.GetLink(i,GetActiveDocument());
	if (!bl) return String();
	return String("Dropped ")+GenText(bl);
}

Bool AsyncDialog::CoreMessage(LONG id,const BaseContainer &msg)
{
	switch (id)
	{
		case EVMSG_CHANGE:
			if (CheckCoreMessage(msg))
			{
				LONG i;
				for (i=0;i<rows;i++)
				{
					SetString(GADGET_DRAG+i,GetStaticText(i));
				}
			}
			break;
	}
	return GeDialog::CoreMessage(id,msg);
}

LONG AsyncDialog::Message(const BaseContainer &msg,BaseContainer &result)
{
	switch (msg.GetId())
	{
		case BFM_DRAGRECEIVE:
			{
				String prefix = "Dragging ";
				LONG i,id=-1;
				if (msg.GetLong(BFM_DRAG_FINISHED)) prefix = "Dropped ";

				if (CheckDropArea(GROUP_SCROLL,msg))
				{
					for (i=0;i<rows;i++)
					{
						if (CheckDropArea(GADGET_DRAG+i,msg)) { id = i; break; }
					}
				}
				if (id!=-1)
				{
					if (msg.GetLong(BFM_DRAG_LOST))
					{
						for (i=0;i<rows;i++)
						{
							SetString(GADGET_DRAG+i,GetStaticText(i));
						}
					}
					else
					{
						String string,str;
						LONG type = 0;
						void *object = NULL;
						BaseList2D *bl = NULL;
						
						GetDragObject(msg,&type,&object);

						if (type==DRAGTYPE_ATOMARRAY && ((AtomArray*)object)->GetCount()==1 && ((AtomArray*)object)->GetIndex(0))
						{
							Atom *at= (Atom*)((AtomArray*)object)->GetIndex(0);
							if (at->IsInstanceOf(Obase))
							{
								bl  = (BaseList2D*)at;
								str = "BaseObject";
							}
							else if (at->IsInstanceOf(Tbase))
							{
								bl  = (BaseList2D*)at;
								str = "BaseTag";
							}
							else if (at->IsInstanceOf(Mbase))
							{
								bl  = (BaseList2D*)at;
								str = "BaseMaterial";
							}
							else if (at->IsInstanceOf(KEbase))
							{
								bl  = (BaseList2D*)at;
								str = "BaseKey";
							}
							else if (at->IsInstanceOf(SEbase))
							{
								bl  = (BaseList2D*)at;
								str = "BaseSequence";
							}
							else if (at->IsInstanceOf(TEbase))
							{
								bl  = (BaseList2D*)at;
								str = "BaseTrack";
							}
						}

						if (bl)
							string = prefix+str+" "+bl->GetName()+" ("+LongToString(bl->GetType())+")";
						else
							string = prefix+"unknown object";

						if (msg.GetLong(BFM_DRAG_FINISHED))
							links.SetLink(id,bl);

						for (i=0;i<rows;i++)
							array_drag[i] = GetStaticText(i);
						array_drag[id] = string;

						for (i=0;i<rows;i++)
							SetString(GADGET_DRAG+i,array_drag[i]);

						return SetDragDestination(MOUSE_POINT_HAND);
					}
				}
			}
			break;
	}
	return GeDialog::Message(msg,result);
}

class AsyncTest : public CommandData
{
	private:
		AsyncDialog dlg;
	public:
		virtual Bool Execute(BaseDocument *doc);
		virtual LONG GetState(BaseDocument *doc);
		virtual Bool RestoreLayout(void *secret);
};

LONG AsyncTest::GetState(BaseDocument *doc)
{
	return CMD_ENABLED;
}

Bool AsyncTest::Execute(BaseDocument *doc)
{
	return dlg.Open(TRUE,ID_ASYNCTEST,-1,-1);
}

Bool AsyncTest::RestoreLayout(void *secret)
{
	return dlg.RestoreLayout(ID_ASYNCTEST,0,secret);
}

Bool RegisterAsyncTest(void)
{
	// decide by name if the plugin shall be registered - just for user convenience
	String name=GeLoadString(IDS_ASYNCTEST); if (!name.Content()) return TRUE;
	return RegisterCommandPlugin(ID_ASYNCTEST,name,0,NULL,"C++ SDK Menu Test Plugin",gNew AsyncTest);
}

