#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 "/AudioPluginStructs.h"


static char Name[] = "SampleView";
static char CopyRight[] = "ŠJohnny T Nielsen, draco, 1998";
static const long VERSION = 1L;
static const long REVISION = 0L;


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, "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 = VERSION;
	ps->PluginRev = REVISION;
	ps->PluginName = Name;
	ps->PluginCopyRight = CopyRight;
	ps->PluginFlags = PLUGIN_HAS_WINDOW;
	ReleaseSemaphore(&ps->Sem);
}
//}


//{ "TestData class"
class TestData : SignalSemaphore {
public:
	SaveData savedata;
	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

	BOOL stereo;
	ULONG samplefreq;

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

	ULONG sig;  //send this signal to asynctask to wake it!

	enum {
		NONE,
		SHOW,
		STOP,
		CLEAR,
		QUIT,
		LOAD
	} async_cmd;

	//window inner dimensions
	LONG width, height;

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

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


//{ "constructor"
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.vu = FALSE;
		savedata.cont = TRUE;
		savedata.pixeldist = 2;
	}

	samplefreq = 0L;
	stereo = 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);
}
//}


void TestData::ClearWindow(BOOL vuclear, int channel)
{
	ObtainSemaphore((SignalSemaphore *)this);
	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(stereo) 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(stereo && (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(stereo && (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);
	}
	ReleaseSemaphore((SignalSemaphore *)this);
}
//}


//{ "Quit"
extern "C" void QuitPlugin(register __a0 APTR _data, register __a6 struct Library *base)
{
	TestData *data = (TestData *)_data;
	if(data)
	{
		//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;
	IntuitionBase = OpenLibrary("intuition.library", 0);
	GfxBase = OpenLibrary("graphics.library", 0);
	UtilityBase = OpenLibrary("utility.library", 0);
	GadToolsBase = OpenLibrary("gadtools.library", 0);
	try {
		data = new TestData(b, d);
	}
	catch(...) {
		CloseLibrary(GadToolsBase);
		CloseLibrary(UtilityBase);
		CloseLibrary(GfxBase);
		CloseLibrary(IntuitionBase);
	};
	return (APTR)data;
}
//}


//{ "getpluginData"
extern "C" PluginData *GetPluginData(register __a0 APTR _data, register __a6 Library *base)
{
	TestData *data = (TestData *)_data;
	ObtainSemaphore((SignalSemaphore *)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);
	ReleaseSemaphore((SignalSemaphore *)data);
	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->stereo = (channels == 2)? TRUE : FALSE;
	ReleaseSemaphore((SignalSemaphore *)data);
}
//}


//{ "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;
	ObtainSemaphore((SignalSemaphore *)data);
	if(data->rp) {
		//update...
		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
		LONG max = width/data->savedata.pixeldist;
		LONG channelwidth = width;
		if(data->stereo) {
			max /= 2;
			channelwidth/=2;
		}
		if(data->savedata.vu) channelwidth-=(VU_WIDTH+1);

		long factor = 65535L/(height-4L);
		while(factor*(height-4L) < 65535L) factor++;
		long factor2 = factor/2L;

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

		SetAPen(data->rp, 1);
		for(LONG i=0; i<max; i++)
		{
			value = left[i];
			vu_left+=abs(value);
			value/=factor;
			if(i)
			{
				if(data->savedata.cont)
					Draw(data->rp, x+channelwidth*i/max, midy+value);
				else
					WritePixel(data->rp, x+channelwidth*i/max, midy+value);
			}
			else
			{
				Move(data->rp, x+channelwidth*i/max, midy+value);
			}
		}
		if(data->savedata.vu)
		{
			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 /= factor2;

			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->stereo)
		{
			data->ClearWindow(FALSE, 2);
			x = midx;
			SetAPen(data->rp, 1);
			for(i=0; i<max; i++)
			{
				value = right[i];
				vu_right+=abs(value);
				value/=factor;
				if(i)
				{
					if(data->savedata.cont)
						Draw(data->rp, x+channelwidth*i/max, midy+value);
					else
						WritePixel(data->rp, x+channelwidth*i/max, midy+value);
				}
				else
				{
					Move(data->rp, x+channelwidth*i/max, midy+value);
				}
			}
			if(data->savedata.vu)
			{
				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 /= factor2;

				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);
			}
		}
	}
	ReleaseSemaphore((SignalSemaphore *)data);
}
//}


//{ "EndTransformation"
extern "C" void EndTransformation(register __a0 APTR _data, register __a6 struct Library *base)
{
	//TestData *data = (TestData *)_data;
}
//}


//{ "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();
		}
	}
	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)
	{
		//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;
}
//}


//{ "HandlePluginWindowMsg"
extern "C" BOOL HandlePluginWindowMsg(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:
				ObtainSemaphore((SignalSemaphore *)data);
				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;
				ReleaseSemaphore((SignalSemaphore *)data);
				data->ClearWindow();
				break;
			case IDCMP_CLOSEWINDOW:
				ReplyMsg((Message *)(*msg));
				(*msg) = NULL;
				//close window
				ObtainSemaphore((SignalSemaphore *)data);
				if(data->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);
				break;
			case IDCMP_MENUPICK:
				switch((*msg)->Code)
				{
					// pixel distance
					case 0x0000:    // 1
						data->savedata.pixeldist = 1;
						break;
					case 0x0800:    // 2
						data->savedata.pixeldist = 2;
						break;
					case 0x1000:    // 3
						data->savedata.pixeldist = 3;
						break;
					case 0x1800:    // 4
						data->savedata.pixeldist = 4;
						break;
					case 0x2000:    // 5
						data->savedata.pixeldist = 5;
						break;
					// VU meter
					case 0xF820:
						if(data->savedata.vu) data->savedata.vu = FALSE;
						else data->savedata.vu = TRUE;

						data->ClearWindow();
						break;
					// cont line
					case 0xF840:
						if(data->savedata.cont) data->savedata.cont = FALSE;
						else data->savedata.cont = TRUE;
						break;
					// hide
					case 0xF880:
						HidePlugin(data, base);
						break;
				}
				break;
		}
		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;
}
//}


extern "C" void PluginClear(register __a0 APTR _data, register __a6 struct Library *base)
{
	TestData *data = (TestData *)_data;
	data->ClearWindow();
}

