#include <pragma/exec_lib.h>
#include <pragma/utility_lib.h>
#include <pragma/intuition_lib.h>
#include <pragma/graphics_lib.h>
#include <pragma/commodities_lib.h>
#include <pragma/gadtools_lib.h>
#include <exec/libraries.h>
#include <clib/alib_protos.h>
#include <ClassLibrary/TimerC.h>
#include <ClassLibrary/SemaphoreC.h>
#include <stdlib.h>
#include <math.h>
//#include <debug.h>


#include "/AudioPluginStructs.h"


static char Name[] = "SampleView";
static char CopyRight[] = "ŠJohnny T Nielsen, draco, 1998";


struct AudioPluginBase {
	struct Library library;
};
#pragma libbase AudioPluginBase;


struct SaveData
{
	struct PluginData plugindata;   //MUST be the first
	ULONG updates;
	BYTE  pixeldist;
	BOOL vu;
	BOOL cont;
};


#define VU_WIDTH 5


struct Library *IntuitionBase = NULL;
struct Library *UtilityBase = NULL;
struct Library *GfxBase = NULL;
struct Library *GadToolsBase = NULL;


struct NewMenu testmenu[] =
{
	{ NM_TITLE, "SampleView", NULL, 0, 0, 0 },
	{ NM_ITEM, "Update", NULL, 0, 0, 0 },
	{ NM_SUB, "5/s", "1", 0, 0, 0 },
	{ NM_SUB, "10/s", "2", 0, 0, 0 },
	{ NM_SUB, "15/s", "3", 0, 0, 0 },
	{ NM_SUB, "20/s", "4", 0, 0, 0 },
	{ NM_SUB, "25/s", "5", 0, 0, 0 },
	{ NM_SUB, "50/s", "6", 0, 0, 0 },
	{ NM_ITEM, "PixelDistance", NULL, 0, 0, 0 },
	{ NM_SUB, "1", NULL, 0, 0, 0 },
	{ NM_SUB, "2", NULL, 0, 0, 0 },
	{ NM_SUB, "3", NULL, 0, 0, 0 },
	{ NM_SUB, "4", NULL, 0, 0, 0 },
	{ NM_SUB, "5", NULL, 0, 0, 0 },
	{ NM_ITEM, "VU-meter", "v", 0, 0, 0 },
	{ NM_ITEM, "Cont. line", "c", 0, 0, 0 },
	{ NM_ITEM, NM_BARLABEL, NULL, 0, 0, 0 },
	{ NM_ITEM, "Hide", "h", 0, 0, 0 },
	{ NM_END, NULL, NULL, 0, 0, 0 }
};


/****************************************************************************************/


//{ "MUSTBEDONE"
class MustBeDone {
public:
	MustBeDone();
};

MustBeDone mustbedone;

MustBeDone::MustBeDone()
{
	Forbid();
	PluginSemaphore *ps = (PluginSemaphore *)FindSemaphore(PLUGIN_SEM_NAME);
	ObtainSemaphore(&ps->Sem);
	Permit();
	ps->IsPlugin = TRUE;
	ps->PluginVer = 0;
	ps->PluginRev = 75;
	ps->PluginName = Name;
	ps->PluginCopyRight = CopyRight;
	ps->PluginFlags = PLUGIN_HAS_WINDOW;
	ReleaseSemaphore(&ps->Sem);
}
//}


//{ "TestData class"
class TestData : SignalSemaphore {
public:
	ULONG PluginSignal;
	struct Task *plugintask;
	BOOL quitasync;
	struct Task *asynctask;

	SaveData savedata;
	ULONG updates;
	BYTE  pixeldist;
	BOOL vu;
	BOOL cont;
	CxObj    *broker;

	Window   *window;       //main window
	RastPort *rp;           //it's rastport
	APTR visualinfo;        //it's visualinfo
	struct Menu *menustrip; //it's menu

	ULONG samplefreq;

	WORD *left, *right;
	ULONG samples;
	ULONG buftime;
	ULONG opsplitning;
	ULONG bufskip;
	ULONG opdwait;
	ULONG bufreached;

	SignalSemaphore sem2;
	WORD *left2, *right2;
	ULONG samples2;
	ULONG buftime2;
	ULONG opsplitning2;
	ULONG bufskip2;
	ULONG opdwait2;
	ULONG bufreached2;

	BOOL updated;

	LONG  vu_decay;
	LONG  vu_gain;
	LONG  vu_old_left;
	LONG  vu_old_right;

	ULONG sig;  //send this signal to asynctask to wake it!
	BOOL show;  //TRUE => sampledisplay running
	BOOL show2;

	//window inner dimensions
	LONG width, height;

	//constructor / destructor
	TestData(CxObj *cx, PluginData *dt);
	~TestData();

	void ClearWindow(BOOL vuclear=TRUE, int channel=3);
};


TestData::TestData(CxObj *cx, PluginData *dt)
 : broker(NULL)
{
	broker = cx;
	window = NULL;
	rp = NULL;
	if(dt) savedata = *(SaveData *)dt;
	else
	{
		savedata.plugindata.Size = sizeof(struct SaveData);
		savedata.plugindata.ShowMain = TRUE;
		savedata.plugindata.MainX = 0;
		savedata.plugindata.MainY = 0;
		savedata.plugindata.MainW = 200;
		savedata.plugindata.MainH = 100;
		savedata.updates = 25L;
		savedata.vu = FALSE;
		savedata.cont = TRUE;
		savedata.pixeldist = 2;
	}

	quitasync = FALSE;
	samplefreq = 0L;
	left = right = NULL;
	samples = 0L;
	buftime = 0L;
	opsplitning = 0L;
	bufskip = 0L;
	bufreached = 0L;
	opdwait = opdwait2 = 40000L;
	show = show2 = FALSE;

	updated = FALSE;

	vu_decay = 32768L*1/4;
	vu_gain  = 3*(32768-vu_decay);
	vu_old_left = vu_old_right = 0L;

	ss_Link.ln_Pri=0;
	ss_Link.ln_Name=NULL;
	::InitSemaphore(this);
	ss_Link.ln_Name = "SAMPLEVIEW";
	::AddSemaphore(this);

	sem2.ss_Link.ln_Pri=0;
	sem2.ss_Link.ln_Name=NULL;
	::InitSemaphore(&sem2);

	PluginSemaphore *ps = (PluginSemaphore *)FindSemaphore(PLUGIN_SEM_NAME);
	ObtainSemaphore(&ps->Sem);
	PluginSignal = ps->PluginSignal;
	ReleaseSemaphore(&ps->Sem);
	plugintask = FindTask(NULL);
}


TestData::~TestData()
{
	Forbid();
	::RemSemaphore(this);
	::ObtainSemaphore(this);
	::ReleaseSemaphore(this);
	ss_Link.ln_Name = NULL;
	Permit();
}


void TestData::ClearWindow(BOOL vuclear, int channel)
{
	if(rp)
	{
		//update display
		LONG width = savedata.plugindata.MainW;
		LONG height = savedata.plugindata.MainH;
		LONG midx = width/2;
		midx-=1;
		LONG midy = height/2;
		LONG channelwidth=width;
		if(right) channelwidth/=2;

		//clear window
		if(savedata.vu)
		{
			channelwidth-=(VU_WIDTH+1);
			if(channel & 1)
			{
				SetAPen(rp, 0);
				if(vuclear)
					RectFill(rp, 1,1, channelwidth+VU_WIDTH, height-2);
				else
					RectFill(rp, 1,1, channelwidth-2, height-2);
				SetAPen(rp, 1); //black

				Move(rp, 1, midy);
				Draw(rp, channelwidth-1, midy); //horisontal midter linie

				Move(rp, channelwidth-1, 1);
				Draw(rp, channelwidth-1, height-2); //vu delimiter linie
			}
			if(right && (channel & 2))
			{
				SetAPen(rp, 0);
				if(vuclear)
					RectFill(rp, midx+1,1, width-1, height-2);
				else
					RectFill(rp, midx+1,1, width-VU_WIDTH-4, height-2);

				SetAPen(rp, 1); //black
				Move(rp, width-VU_WIDTH-2, 1);
				Draw(rp, width-VU_WIDTH-2, height-2); //vu delimiter

				Move(rp, midx, midy);
				Draw(rp, width-VU_WIDTH-2, midy); //horisontal midter line

				Move(rp, midx, 1);
				Draw(rp, midx, height-1);   //kanal skille linie
			}
		}
		else
		{
			if(channel & 1)
			{
				SetAPen(rp, 0);
				RectFill(rp, 1,1, channelwidth-2, height-2);

				SetAPen(rp, 1); //black
				Move(rp, 1, midy);
				Draw(rp, channelwidth-2, midy); //horisontal midter linie
			}
			if(right && (channel & 2))
			{
				SetAPen(rp, 0);
				RectFill(rp, midx+1,1, width-1, height-2);

				SetAPen(rp, 1);
				Move(rp, midx, midy);
				Draw(rp, width-1, midy); //horisontal midter line

				Move(rp, midx, 1);
				Draw(rp, midx, height-1);   //kanal skille linie
			}
		}

		//draw border
		SetAPen(rp, 1); //black
		Move(rp, width-1, 0);
		Draw(rp, width-1, height-1);
		Draw(rp, 0, height-1);
		SetAPen(rp, 2); //white
		Draw(rp, 0, 0);
		Draw(rp, width-1, 0);
	}
}
//}


//{ "ASYNCTASK"
extern "C" void AsyncTask(void)
{
	struct SignalSemaphore *sem = FindSemaphore("SAMPLEVIEW");
	TestData *data = (TestData *)sem;
	ObtainSemaphore(sem);
	data->sig = AllocSignal(-1);
	ReleaseSemaphore(sem);
	TimerC *timer = new TimerC;

	BOOL q=FALSE;
	do
	{
		Wait(1L<<data->sig);
		if(data->show)
		{
			//display running
			while(data->show && !q)
			{
				ObtainSemaphore(sem);
				q = data->quitasync;    //shall I quit?
				timer->Send(0L, data->opdwait);
				//timer->Send(0L, 250000L);
				if(data->rp)
				{
					//yes - the window was open
					data->ClearWindow(FALSE, 1);

					LONG width = data->savedata.plugindata.MainW;
					LONG height = data->savedata.plugindata.MainH;
					LONG midx = width/2;
					LONG midy = height/2;

					//show buffer
					if(data->left)
					{
						if((data->opsplitning > data->bufreached) || (data->opsplitning==0L))
						{
							LONG max = width/data->savedata.pixeldist;
							if(data->right) max /= 2;

							LONG channelwidth = width;
							if(data->right) channelwidth/=2;
							if(data->savedata.vu) channelwidth-=(VU_WIDTH+1);

							LONG step;
							step = channelwidth/max;

							LONG factor = 32768/(height-10);

							LONG x = 1;
							ULONG vu_left = 0;
							ULONG vu_right = 0;
							LONG value;

							LONG skip = 2*data->samplefreq/11025;
							if(skip < 1) skip=1;
							if(max*skip > data->bufskip) max=data->bufskip/skip;

							SetAPen(data->rp, 1);
							for(LONG i=0; i<max; i++)
							{
								value = data->left[data->bufreached*data->bufskip + i*skip];
								vu_left+=abs(value);
								if(i)
								{
									if(data->savedata.cont)
										Draw(data->rp, x+channelwidth*i/max, midy+value/factor);
									else
										WritePixel(data->rp, x+channelwidth*i/max, midy+value/factor);
								}
								else
								{
									Move(data->rp, x+channelwidth*i/max, midy+value/factor);
								}
							}
							if(data->savedata.vu)
							{
								//for(; i<(data->bufskip/data->savedata.pixeldist); i++)
								//{
								//    value = data->left[data->bufreached*data->bufskip + i*skip];
								//    vu_left+=abs(value);
								//}
								//vu_left/=(data->bufskip/data->savedata.pixeldist);
								vu_left/=max;
								vu_left = vu_left*data->vu_gain/32768L+data->vu_decay*data->vu_old_left/32768L;
								data->vu_old_left = vu_left;
								vu_left /= factor;

								SetAPen(data->rp, 0);
								RectFill(data->rp, channelwidth, 1, channelwidth+(VU_WIDTH-1), height-vu_left);
								SetAPen(data->rp, 3);
								RectFill(data->rp, channelwidth, height-vu_left, channelwidth+(VU_WIDTH-1), height-2);
							}
							if(data->right)
							{
								data->ClearWindow(FALSE, 2);
								x = midx;
								SetAPen(data->rp, 1);
								for(i=0; i<max; i++)
								{
									value = data->right[data->bufreached*data->bufskip + i*skip];
									vu_right+=abs(value);
									if(i)
									{
										if(data->savedata.cont)
											Draw(data->rp, x+channelwidth*i/max, midy+value/factor);
										else
											WritePixel(data->rp, x+channelwidth*i/max, midy+value/factor);
									}
									else
									{
										Move(data->rp, x+channelwidth*i/max, midy+value/factor);
									}
								}
								if(data->savedata.vu)
								{
									//for(; i<(data->bufskip/data->savedata.pixeldist); i++)
									//{
									//    value = data->right[data->bufreached*data->bufskip + i*skip];
									//    vu_right+=abs(value);
									//}
									//vu_right/=(data->bufskip/data->savedata.pixeldist);
									vu_right/=max;
									vu_right = vu_right*data->vu_gain/32768L+data->vu_decay*data->vu_old_right/32768L;
									data->vu_old_right = vu_right;
									vu_right /= factor;

									SetAPen(data->rp, 0);
									RectFill(data->rp, width-VU_WIDTH-1, 1, width-2, height-vu_right);
									SetAPen(data->rp, 3);
									RectFill(data->rp, width-VU_WIDTH-1, height-vu_right, width-2, height-2);
								}
							}
							data->bufreached++;
						}
					}
				}
				else data->show=FALSE;
				ReleaseSemaphore(sem);

				//test if new data has arrived!
				ObtainSemaphore(&data->sem2);
				if(data->updated)
				{
					data->left = data->left2;
					data->right = data->right2;
					data->samples = data->samples2;
					data->buftime = data->buftime2;
					data->opsplitning = data->opsplitning2;
					data->bufskip = data->bufskip2;
					data->opdwait = data->opdwait2;
					data->bufreached = data->bufreached2;

					data->updated = FALSE;
				}
				ReleaseSemaphore(&data->sem2);

				if(!q)
				{
					timer->WaitTimer();
					timer->GetTimerMsg();
				}
			}
		}
		else
		{
			ObtainSemaphore(sem);
			q = data->quitasync;    //shall I quit?
			ReleaseSemaphore(sem);
		}
	}while(! q);

	delete timer;
	ObtainSemaphore(sem);
	FreeSignal(data->sig);
	ULONG sig = data->PluginSignal;
	struct Task *task = data->plugintask;
	ReleaseSemaphore(sem);

	//tell main that I've heard the request!
	Signal(task, 1L<<sig);
	Wait(0L);
}
//}


//{ "Quit"
extern "C" void QuitPlugin(register __a0 APTR _data, register __a6 struct Library *base)
{
	TestData *data = (TestData *)_data;
	if(data)
	{
		//get rid of async task
		ObtainSemaphore((SignalSemaphore *)data);
		data->quitasync = TRUE;
		data->show = FALSE;
		Signal(data->asynctask, 1L<<data->sig);
		ReleaseSemaphore((SignalSemaphore *)data);
		Wait(1L<<data->PluginSignal);

		DeleteTask(data->asynctask);

		//now close window
		if(data->window)
		{
			ClearMenuStrip(data->window);
			FreeMenus(data->menustrip);
			FreeVisualInfo(data->visualinfo);
			Forbid();
			data->window->UserPort = NULL;
			CloseWindow(data->window);
			Permit();
		}
		//and delete the data
		delete data;
	}
	if(GadToolsBase) CloseLibrary(GadToolsBase);
	if(UtilityBase) CloseLibrary(UtilityBase);
	if(IntuitionBase) CloseLibrary(IntuitionBase);
	if(GfxBase) CloseLibrary(GfxBase);
}
//}


//{ "InitPlugin"
extern "C" APTR InitPlugin(register __a0 PluginData *d, register __a1 struct MsgPort *p, register __a2 CxObj *b,
	register __a6 struct Library *base)
{
	TestData *data = NULL;
	try {
		IntuitionBase = OpenLibrary("intuition.library", 0);
		GfxBase = OpenLibrary("graphics.library", 0);
		UtilityBase = OpenLibrary("utility.library", 0);
		GadToolsBase = OpenLibrary("gadtools.library", 0);
		data = new TestData(b, d);

		ObtainSemaphore((SignalSemaphore *)data);
		data->asynctask = CreateTask("SampleView.plugin", 0, AsyncTask, 4096L);
		ReleaseSemaphore((SignalSemaphore *)data);
	}
	catch(...) { };
	return (APTR)data;
}
//}


//{ "getpluginData"
extern "C" PluginData *GetPluginData(register __a0 APTR _data, register __a6 Library *base)
{
	TestData *data = (TestData *)_data;
	data->savedata.plugindata.ShowMain = (BOOL)data->window;
	if(data->window)
	{
		data->savedata.plugindata.MainX = data->window->LeftEdge;
		data->savedata.plugindata.MainY = data->window->TopEdge;
	}
	data->savedata.plugindata.Size = sizeof(struct SaveData);
	return (PluginData *)&data->savedata;
}
//}


//{ "InitTransformation"
extern "C" void InitTransformation(register __a0 APTR _data,
	register __a1 char *label,
	register __d0 ULONG duration,
	register __d1 ULONG samplefreq,
	register __d2 ULONG channels,
	register __d3 ULONG bitrate,
	register __d4 ULONG elapsed,
	register __a6 struct Library *base)
{
	TestData *data = (TestData *)_data;
	ObtainSemaphore((SignalSemaphore *)data);
	data->samplefreq = samplefreq;
	data->show = data->show2 = TRUE;
	//dprintf("SAMPLEFREQ: %ld", samplefreq);
	ReleaseSemaphore((SignalSemaphore *)data);
	Signal(data->asynctask, 1L<<data->sig);
}
//}


//{ "Transform"
extern "C" void Transform(register __a0 APTR _data, register __a1 WORD *left, register __a2 WORD *right, register __d0 ULONG samples,
	register __a6 struct Library *base)
{
	//TestData *data = (TestData *)_data;
}
//}


//{ "PluginDisplay"
extern "C" void PluginDisplay(register __a0 APTR _data, register __a1 WORD *left, register __a2 WORD *right, register __d0 ULONG samples,
	register __a6 struct Library *base)
{
	TestData *data = (TestData *)_data;
	//dprintf("SAMPLES   : %ld", samples);
	//ObtainSemaphore((SignalSemaphore *)data);
	if(AttemptSemaphore((SignalSemaphore *)data))
	{
		data->left = left;
		data->right = right;
		data->samples = samples;

		data->buftime = data->samplefreq/samples; //number of buffers this size a second
		data->opsplitning = data->savedata.updates*samples/data->samplefreq; //split each buffer <opsplitning> times
		if(data->opsplitning)
		{
			data->bufskip = samples/data->opsplitning;
			data->opdwait = 1000000L / 25; //(data->samplefreq * data->opsplitning / samples);
		}
		else
		{
			data->bufskip = samples;
			data->opdwait = 1000000L / data->buftime;
		}
		data->bufreached = 0;

		ReleaseSemaphore((SignalSemaphore *)data);
	}
	else
	{
		ObtainSemaphore(&data->sem2);
		data->left2 = left;
		data->right2 = right;
		data->samples2 = samples;

		data->buftime2 = data->samplefreq/samples; //number of buffers this size a second
		data->opsplitning2 = data->savedata.updates*samples/data->samplefreq; //split each buffer <opsplitning> times
		if(data->opsplitning2)
		{
			data->bufskip2 = samples/data->opsplitning2;
			data->opdwait2 = 1000000L / 25L //(data->samplefreq * data->opsplitning2 / samples);
		}
		else
		{
			data->bufskip2 = samples;
			data->opdwait2 = 1000000L / data->buftime2;
		}
		data->bufreached2 = 0;

		data->updated = TRUE;

		ReleaseSemaphore(&data->sem2);
	}
}
//}


//{ "EndTransformation"
extern "C" void EndTransformation(register __a0 APTR _data, register __a6 struct Library *base)
{
	TestData *data = (TestData *)_data;
	ObtainSemaphore((SignalSemaphore *)data);
	data->samplefreq = 0L;
	data->left = NULL;
	data->show = data->show2 = FALSE;
	Signal(data->asynctask, 1L<<data->sig);
	ReleaseSemaphore((SignalSemaphore *)data);
	data->ClearWindow();
}
//}


//{ "HiddenPlugin"
extern "C" BOOL HiddenPlugin(register __a0 APTR _data, register __a6 struct Library *base)
{
	TestData *data = (TestData *)_data;
	if(data->window) return FALSE;
	return TRUE;
}
//}


//{ "ShowPlugin"
extern "C" BOOL ShowPlugin(register __a0 APTR _data, register __a1 MsgPort *port, register __a6 struct Library *base)
{
	TestData *data = (TestData *)_data;
	BOOL res = FALSE;
	ObtainSemaphore((SignalSemaphore *)data);
	if(! data->window)
	{
		data->window = OpenWindowTags(NULL,
			WA_Left, data->savedata.plugindata.MainX,
			WA_Top, data->savedata.plugindata.MainY,
			WA_InnerWidth, data->savedata.plugindata.MainW,
			WA_InnerHeight, data->savedata.plugindata.MainH,
			WA_MinHeight, 25,
			WA_MinWidth, 75,
			WA_MaxHeight, -1,
			WA_MaxWidth, -1,
			WA_Title, "SampleView for TMLG",
			WA_ScreenTitle, "SampleView for TMLG, ŠJohnny T Nielsen, 1998",
			WA_SizeGadget, TRUE,
			WA_DragBar, TRUE,
			WA_DepthGadget, TRUE,
			WA_CloseGadget, TRUE,
			WA_SmartRefresh, TRUE,
			WA_GimmeZeroZero, TRUE,
			WA_IDCMP, NULL,
			TAG_DONE);
		if(data->window)
		{
			data->visualinfo = GetVisualInfo(data->window->WScreen, TAG_END);
			data->menustrip = CreateMenus(testmenu, TAG_END);
			LayoutMenus(data->menustrip, data->visualinfo, TAG_END);
			SetMenuStrip(data->window, data->menustrip);

			data->rp = data->window->RPort;
			data->window->UserPort = port;
			ModifyIDCMP(data->window, IDCMP_CLOSEWINDOW | IDCMP_MENUPICK | IDCMP_NEWSIZE);
			res = TRUE;

			data->ClearWindow();

			data->show = data->show2;
			Signal(data->asynctask, 1L<<data->sig);
		}
	}
	ReleaseSemaphore((SignalSemaphore *)data);
	return res;
}
//}


//{ "hideplugin"
extern "C" void HidePlugin(register __a0 APTR _data, register __a6 struct Library *base)
{
	TestData *data = (TestData *)_data;
	ObtainSemaphore((SignalSemaphore *)data);
	if(data->window)
	{
		//stop display
		data->show2 = data->show;
		data->show = FALSE;
		Signal(data->asynctask, 1L<<data->sig);
		//close window
		ClearMenuStrip(data->window);
		FreeMenus(data->menustrip);
		FreeVisualInfo(data->visualinfo);
		Forbid();
		data->window->UserPort = NULL;
		CloseWindow(data->window);
		Permit();
		data->window = NULL;
		data->rp = NULL;
	}
	ReleaseSemaphore((SignalSemaphore *)data);
}
//}


//{ "AboutPlugin"
extern "C" BOOL AboutPlugin(register __a0 APTR _data, register __a1 MsgPort *port, register __a6 struct Library *base)
{
	TestData *data = (TestData *)_data;
	return FALSE;
}
//}


//{ "PluginPrefs"
extern "C" BOOL PluginPrefs(register __a0 APTR _data, register __a1 MsgPort *port, register __a6 struct Library *base)
{
	TestData *data = (TestData *)_data;
	return FALSE;
}
//}


//{ "PluginHandleKey"
extern "C" BOOL PluginHandleKey(register __a0 APTR _data, register __d0 ULONG key, register __a6 struct Library *base)
{
	TestData *data = (TestData *)_data;
	return FALSE;
}
//}


//{ "HandleWindowMsg"
extern "C" BOOL HandleWindowMsg(register __a0 APTR _data, register __a1 IntuiMessage **msg, register __a6 struct Library *base)
{
	TestData *data = (TestData *)_data;
	if((*msg)->IDCMPWindow == data->window)
	{
		switch((*msg)->Class)
		{
			case IDCMP_NEWSIZE:
				data->savedata.plugindata.MainX = data->window->LeftEdge;
				data->savedata.plugindata.MainY = data->window->TopEdge;
				data->savedata.plugindata.MainW = data->window->Width - data->window->BorderLeft - data->window->BorderRight;
				data->savedata.plugindata.MainH = data->window->Height - data->window->BorderTop - data->window->BorderBottom;
				data->ClearWindow();
				break;
			case IDCMP_CLOSEWINDOW:
				ReplyMsg((Message *)(*msg));
				(*msg) = NULL;
				//close window
				if(data->window)
				{
					data->show2 = data->show;
					data->show = FALSE;
					Signal(data->asynctask, 1L<<data->sig);

					ObtainSemaphore((SignalSemaphore *)data);
					ClearMenuStrip(data->window);
					FreeMenus(data->menustrip);
					FreeVisualInfo(data->visualinfo);
					Forbid();
					data->window->UserPort = NULL;
					CloseWindow(data->window);
					Permit();
					data->window = NULL;
					data->rp = NULL;
					ReleaseSemaphore((SignalSemaphore *)data);
				}
				break;
			case IDCMP_MENUPICK:
				switch((*msg)->Code)
				{
					// updates
					case 0x0000:    // 5/s
						data->savedata.updates = 5;
						break;
					case 0x0800:    // 10/s
						data->savedata.updates = 10;
						break;
					case 0x1000:    // 15/s
						data->savedata.updates = 15;
						break;
					case 0x1800:    // 20/s
						data->savedata.updates = 20;
						break;
					case 0x2000:    // 25/s
						data->savedata.updates = 25;
						break;
					case 0x2800:    // 50/s
						data->savedata.updates = 50;
						break;
					// pixel distance
					case 0x0020:    // 1
						data->savedata.pixeldist = 1;
						break;
					case 0x0820:    // 2
						data->savedata.pixeldist = 2;
						break;
					case 0x1020:    // 3
						data->savedata.pixeldist = 3;
						break;
					case 0x1820:    // 4
						data->savedata.pixeldist = 4;
						break;
					case 0x2020:    // 5
						data->savedata.pixeldist = 5;
						break;
					// VU meter
					case 0xF840:
						if(data->savedata.vu) data->savedata.vu = FALSE;
						else data->savedata.vu = TRUE;
						break;
					// cont line
					case 0xF860:
						if(data->savedata.cont) data->savedata.cont = FALSE;
						else data->savedata.cont = TRUE;
						break;
					// hide
					case 0xF8A0:
						HidePlugin(data, base);
						break;
				}
				break;
		}
		//if(*msg)
		//{
		//    ReplyMsg((Message *)(*msg));
		//    (*msg) = NULL;
		//}
		return TRUE;
	}
	return FALSE;
}
//}


//{ "LockPluginGUI"
extern "C" void LockPluginGUI(register __a0 APTR _data, register __a6 struct Library *base)
{
	TestData *data = (TestData *)_data;
	if(data->window)
	{
		SetWindowPointer(data->window,
			WA_BusyPointer, TRUE,
			WA_PointerDelay, TRUE,
			TAG_DONE);
	}
}
//}


//{ "UnLockPluginGUI"
extern "C" void UnLockPluginGUI(register __a0 APTR _data, register __a6 struct Library *base)
{
	TestData *data = (TestData *)_data;
	if(data->window)
	{
		SetWindowPointer(data->window,
			TAG_DONE);
	}
}
//}


//{ "PluginPriority"
extern "C" void PluginPriority(register __a0 APTR _data, register __d0 LONG pri, register __a6 struct Library *base)
{
	TestData *data = (TestData *)_data;
	SetTaskPri(data->asynctask, pri);
}
//}

