/* -------------------------------------------------------------------------- *\
   MUIPREVIEW.CPP
   Copyright © 1997 by Jarno van der Linden
   jarno@kcbbs.gen.nz

   Preview MUI class

   This program is Freeware, and all usual Freeware rules apply.

   09 Aug 1997: Project started
\* -------------------------------------------------------------------------- */

/* -------------------------------- Includes -------------------------------- */
#include <exec/types.h>

#include <libraries/mui.h>
#include <utility/utility.h>

#include <proto/muimaster.h>
#include <proto/intuition.h>
#include <proto/utility.h>
#include <clib/alib_protos.h>

#include <math.h>

#include "MUIpreview.h"

#include "syn_cam.h"

/* ------------------------------ Definitions ------------------------------- */

/* --------------------------------- Macros --------------------------------- */

/* -------------------------------- Typedefs -------------------------------- */

/* ------------------------------ Proto Types ------------------------------- */
__saveds ULONG PreviewNew(struct IClass *cl,Object *obj,Msg msg);
__saveds ULONG PreviewSetup(struct IClass *cl,Object *obj,struct MUIP_HandleInput *msg);
__saveds ULONG PreviewCleanup(struct IClass *cl,Object *obj,struct MUIP_HandleInput *msg);
__saveds ULONG PreviewSet(struct IClass *cl,Object *obj,Msg msg,BOOL nosuper);
__saveds ULONG PreviewGet(struct IClass *cl,Object *obj,Msg msg);
__saveds ULONG PreviewAskMinMax(struct IClass *cl,Object *obj,struct MUIP_AskMinMax *msg);
__saveds ULONG PreviewDraw(struct IClass *cl,Object *obj,struct MUIP_Draw *msg);
__saveds ULONG PreviewHandleInput(struct IClass *cl,Object *obj,struct MUIP_HandleInput *msg);
__saveds ULONG PreviewUpdate(struct IClass *cl,Object *obj);
__saveds __asm ULONG PreviewDispatcher(register __a0 struct IClass *cl,register __a2 Object *obj,register __a1 Msg msg);

/* -------------------------------- Structs --------------------------------- */
struct PreviewData
{
	ULONG mode;
	SynCamera *camera;
	Environ *environ;
	int sx,sy,x,y;
	BOOL moving;
	int level;
};

/* -------------------------------- Globals --------------------------------- */
struct MUI_CustomClass *previewClass = NULL;

/* ---------------------------------- Code ---------------------------------- */
__saveds ULONG PreviewNew(struct IClass *cl,Object *obj,Msg msg)
{
	struct PreviewData *data;

	if(!(obj = (Object *)DoSuperMethodA(cl,obj,msg)))
		return 0;

	data = (PreviewData *)INST_DATA(cl,obj);
	data->mode = PREVIEW_STATIC | PREVIEW_MOVEFOCUS | PREVIEW_MOVEX | PREVIEW_MOVEY;
	data->camera = NULL;
	data->environ = NULL;
	data->moving = FALSE;
	data->level = 0;

	PreviewSet(cl,obj,msg,TRUE);

	return((ULONG)obj);
}


__saveds ULONG PreviewSetup(struct IClass *cl,Object *obj,struct MUIP_HandleInput *msg)
{
	if(!(DoSuperMethodA(cl,obj,(Msg)msg)))
		return(FALSE);

	MUI_RequestIDCMP(obj,IDCMP_MOUSEBUTTONS);

	return(TRUE);
}


__saveds ULONG PreviewCleanup(struct IClass *cl,Object *obj,struct MUIP_HandleInput *msg)
{
	MUI_RejectIDCMP(obj,IDCMP_MOUSEBUTTONS);
	return(DoSuperMethodA(cl,obj,(Msg)msg));
}


__saveds ULONG PreviewSet(struct IClass *cl,Object *obj,Msg msg,BOOL nosuper)
{
	struct PreviewData *data = (struct PreviewData *)INST_DATA(cl,obj);
	struct TagItem *tags,*tag;

	for (tags=((struct opSet *)msg)->ops_AttrList;tag=NextTagItem(&tags);)
	{
		switch (tag->ti_Tag)
		{
			case MUIA_Preview_Mode:
					data->mode = (ULONG)tag->ti_Data;
				break;
			case MUIA_Preview_Mode_Add:
					data->mode |= (ULONG)tag->ti_Data;
				break;
			case MUIA_Preview_Mode_Remove:
					data->mode &= ~(ULONG)tag->ti_Data;
				break;
			case MUIA_Preview_Mode_Toggle:
					data->mode ^= (ULONG)tag->ti_Data;
				break;
			case MUIA_Preview_Camera:
				if (tag->ti_Data)
				{
					data->camera = (SynCamera *)tag->ti_Data;
				}
				break;
			case MUIA_Preview_Environ:
				if (tag->ti_Data)
				{
					data->environ = (Environ *)tag->ti_Data;
				}
				break;
			case MUIA_Preview_Level:
					data->level = (int)tag->ti_Data;
				break;
		}
	}

	return(nosuper ? 0 : DoSuperMethodA(cl,obj,msg));
}


__saveds ULONG PreviewGet(struct IClass *cl,Object *obj,Msg msg)
{
	struct PreviewData *data = (struct PreviewData *)INST_DATA(cl,obj);
	ULONG *store = ((struct opGet *)msg)->opg_Storage;

	switch (((struct opGet *)msg)->opg_AttrID)
	{
		case MUIA_Preview_Mode   : *store = (ULONG)data->mode; return(TRUE);
		case MUIA_Preview_Camera : *store = (ULONG)data->camera; return(TRUE);
		case MUIA_Preview_Environ: *store = (ULONG)data->environ; return(TRUE);
		case MUIA_Preview_Level  : *store = (ULONG)data->level; return(TRUE);
	}

	return(DoSuperMethodA(cl,obj,msg));
}


__saveds ULONG PreviewAskMinMax(struct IClass *cl,Object *obj,struct MUIP_AskMinMax *msg)
{
	DoSuperMethodA(cl,obj,(Msg)msg);

	msg->MinMaxInfo->MinWidth  += 300;
	msg->MinMaxInfo->DefWidth  += 320;
	msg->MinMaxInfo->MaxWidth  += 500;

	msg->MinMaxInfo->MinHeight += 300;
	msg->MinMaxInfo->DefHeight += 320;
	msg->MinMaxInfo->MaxHeight += 500;

	return(0);
}


__saveds ULONG PreviewDraw(struct IClass *cl,Object *obj,struct MUIP_Draw *msg)
{
	struct PreviewData *data = (struct PreviewData *)INST_DATA(cl,obj);
	WinMetaFile *winmetafile;

	DoSuperMethodA(cl,obj,(Msg)msg);

	if (!(msg->flags & MADF_DRAWOBJECT))
		return(0);

	winmetafile = new WinMetaFile(_window(obj),_dri(obj),_mleft(obj),_top(obj)+1);
	if(data->moving)
		data->camera->BoxPreview(data->environ, winmetafile, data->level);
	else
		data->camera->Preview(data->environ, winmetafile);
	delete winmetafile;

	return(0);
}


__saveds void MoveCamera(struct IClass *cl,Object *obj)
{
	struct PreviewData *data = (struct PreviewData *)INST_DATA(cl,obj);
	double nx,ny,r;

	nx = -((double)(data->x))/_mwidth(obj);
	ny = -((double)(data->y))/_mheight(obj);
	r = sqrt(nx*nx+ny*ny);

	if(data->mode & PREVIEW_MOVEX)
	{
		if((data->mode & PREVIEW_MOVEEYE) && (data->mode & PREVIEW_MOVEFOCUS))
		{
			data->camera->Pan(nx*10.0,0.0);
		}
		else if(data->mode & PREVIEW_MOVEEYE)
		{
			data->camera->Orbit(nx*2.0*PI,0.0);
		}
		else if(data->mode & PREVIEW_MOVEFOCUS)
		{
			data->camera->Rotate(nx*2.0*PI,0.0);
		}
	}
	if(data->mode & PREVIEW_MOVEY)
	{
		if((data->mode & PREVIEW_MOVEEYE) && (data->mode & PREVIEW_MOVEFOCUS))
		{
			data->camera->Pan(0.0,ny*10.0);
		}
		else if(data->mode & PREVIEW_MOVEEYE)
		{
			data->camera->Orbit(0.0,ny*2.0*PI);
		}
		else if(data->mode & PREVIEW_MOVEFOCUS)
		{
			data->camera->Rotate(0.0,ny*2.0*PI);
		}
	}
	if(data->mode & PREVIEW_MOVEZ)
	{
		if((data->mode & PREVIEW_MOVEEYE) && (data->mode & PREVIEW_MOVEFOCUS))
		{
			data->camera->Dolly(ny*10.0);
		}
		else if(data->mode & PREVIEW_MOVEEYE)
		{
			data->camera->DollyEyeFocusPosn(ny*10.0, 0.0);
		}
		else if(data->mode & PREVIEW_MOVEFOCUS)
		{
			data->camera->DollyEyeFocusPosn(0.0, ny*10.0);
		}
	}

	PreviewUpdate(cl,obj);
}


__saveds ULONG PreviewHandleInput(struct IClass *cl,Object *obj,struct MUIP_HandleInput *msg)
{
	#define _between(a,x,b) ((x)>=(a) && (x)<=(b))
	#define _isinobject(x,y) (_between(_mleft(obj),(x),_mright(obj)) && _between(_mtop(obj),(y),_mbottom(obj)))

	struct PreviewData *data = (struct PreviewData *)INST_DATA(cl,obj);

	if(msg->imsg)
	{
		switch(msg->imsg->Class)
		{
			case IDCMP_MOUSEBUTTONS:
				if(msg->imsg->Code == SELECTDOWN)
				{
					if(_isinobject(msg->imsg->MouseX,msg->imsg->MouseY))
					{
						data->sx = msg->imsg->MouseX;
						data->sy = msg->imsg->MouseY;
						data->moving = TRUE;
						PreviewUpdate(cl,obj);
						MUI_RequestIDCMP(obj,IDCMP_MOUSEMOVE | IDCMP_DELTAMOVE);
					}
				}
				else
				{
					MUI_RejectIDCMP(obj,IDCMP_MOUSEMOVE | IDCMP_DELTAMOVE);
					if(data->moving == TRUE)
					{
						data->moving = FALSE;
						PreviewUpdate(cl,obj);
					}
				}
				break;
			case IDCMP_MOUSEMOVE:
				data->x = msg->imsg->MouseX;
				data->y = msg->imsg->MouseY;
				MoveCamera(cl,obj);
				break;
		}
	}

	return(DoSuperMethodA(cl,obj,(Msg)msg));
}


__saveds ULONG PreviewUpdate(struct IClass *cl,Object *obj)
{
	MUI_Redraw(obj,MADF_DRAWOBJECT);

	return(0);
}


__saveds __asm ULONG PreviewDispatcher(register __a0 struct IClass *cl,register __a2 Object *obj,register __a1 Msg msg)
{
	switch (msg->MethodID)
	{
		case OM_NEW             : return(PreviewNew        (cl,obj,msg));
		case MUIM_Setup         : return(PreviewSetup      (cl,obj,(MUIP_HandleInput *)msg));
		case MUIM_Cleanup       : return(PreviewCleanup    (cl,obj,(MUIP_HandleInput *)msg));
		case OM_SET             : return(PreviewSet        (cl,obj,msg,FALSE));
		case OM_GET             : return(PreviewGet        (cl,obj,msg));
		case MUIM_AskMinMax     : return(PreviewAskMinMax  (cl,obj,(MUIP_AskMinMax *)msg));
		case MUIM_Draw          : return(PreviewDraw       (cl,obj,(MUIP_Draw *)msg));
		case MUIM_HandleInput   : return(PreviewHandleInput(cl,obj,(MUIP_HandleInput *)msg));
		case MUIM_Preview_Update: return(PreviewUpdate     (cl,obj));
	}

	return(DoSuperMethodA(cl,obj,msg));
}


struct MUI_CustomClass *InitPreviewClass(void)
{
	previewClass = MUI_CreateCustomClass(NULL,MUIC_Area,NULL,sizeof(struct PreviewData),PreviewDispatcher);

	return previewClass;
}


void DeletePreviewClass(void)
{
	if(previewClass) MUI_DeleteCustomClass(previewClass);
	previewClass = NULL;
}
