/* Small Plugin 3 ©2001 Jaca/Dreamolers
** Clever Amigants Polish Society
** jacadcaps.tripod.com jacadcaps@poczta.onet.pl
** Plugin looks best on title bar! it's NOT transparent (it only takes the back
** color of 0,0 window pixel!!!
*/

/*                                 Based on                                  */
/*****************************************************************************/
/*                         AmigaAMP Plugin skeleton v1.2                     */
/*                     Written December 1998  by Thomas Wenzel               */
/*               Adapted to work with VBCC by Thomas Jensen Jan 1999         */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <math.h>
#include <linkerfunc.h>

#include <exec/types.h>
#include <exec/memory.h>
#include <exec/nodes.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/intuition.h>
#include <proto/graphics.h>
#include <proto/cybergraphics.h>
#include <proto/icon.h>
#include <workbench/startup.h>

#include <dos/dostags.h>
#include <dos/dosextens.h>
#include <graphics/gfxbase.h>
#include <exec/tasks.h>
#include <intuition/screens.h>
#include <graphics/rastport.h>

#include <cybergraphics/cybergraphics.h>
#include <wbstartup.h>

#include "trackinfo.h"

#define VERSTR "$VER: SmallPlugin3 1.2 (18.06.01) ©2001 Jaca/Dreamolers-CAPS"

static struct WBStartup *wbmsg;
struct GfxBase *GfxBase=0;
struct Library *CyberGfxBase=0;
ULONG scrx=425,scry=2,realdepth;
static struct Screen *wbscr=0;

ULONG *background=0,*mixbackground=0,*renderarray=0;
UBYTE fadespeed=4,framespeed=0,randomfreq=20,rfreq=0;
struct Window *wind=0;
BOOL fromwb=FALSE,randomdirection=FALSE;
BYTE direction=3,udirection=1;

int main(int a,char**b);
BOOL PluginInit(void);
void PluginExit(void);
void PluginLoop(void);
void DoLines(void);
void MoveLine(UBYTE yposition);
void FillLine(UBYTE yposition);

void ShowRequester(char *Text, char *Button);

/***************************************************************************/
/* This is the global variables section. Don't change anything here unless */
/* you know what you're doing!                                             */
/***************************************************************************/

BYTE             PluginSignal;
ULONG            PluginMask;
BOOL             Accepted;
struct Process   *PluginTask;
struct MsgPort   *PluginMP;
struct MsgPort   *PluginRP;
BYTE             InfoSignal; // v1.2
ULONG            InfoMask;   // v1.2
struct TrackInfo *tinfo;     // v1.2

UWORD	*PluginRawL;
UWORD	*PluginRawR;
UWORD	*SpecRawL;
UWORD	*SpecRawR;

struct PluginMessage {
	struct Message msg;
	ULONG          PluginMask;
	struct Process *PluginTask;
	UWORD          **SpecRawL;
	UWORD          **SpecRawR;
	BOOL           Accepted;

	/* All data beyond this point is new for v1.2.                         */
	/* AmigaAMP v2.5 and up will detect this new data and act accordingly. */
	/* Older versions of AmigaAMP will simply ignore it.                   */

	ULONG            InfoMask;
	struct TrackInfo **tinfo;
	ULONG            reserved;
};

/***************************************************************************/
/* This is the main part. Again, don't change anything here if you haven't */
/* got a very good reason to do so!                                        */
/***************************************************************************/

int main(int a,char**b) {
	struct PluginMessage *PluginMsg;
	struct PluginMessage *ReplyMsg;

	/* (TJ) added for VBCC compatibility */
	#ifndef __SASC
	IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library", 36);
	if(IntuitionBase == NULL) {
		printf("Unable to open intuition.library V36+ (OS 2.1 required)\n");
		exit(5);
	}
	#endif

	/* Allocate all user resources */
	if(PluginInit()) {
		/* Check if a plugin capable instance of AmigaAMP is running */
		if(PluginMP=FindPort("AmigaAMP plugin port"))	{
			/* Allocate a sigbit for receiving signals FROM AmigaAMP */
			PluginTask   = (struct Process *)FindTask(NULL);
			PluginSignal = AllocSignal(-1);
			InfoSignal   = AllocSignal(-1);              // v1.2
			if(PluginSignal != -1 && InfoSignal != -1) { // v1.2
				InfoMask   = 1L << InfoSignal;             // v1.2
				PluginMask = 1L << PluginSignal;

				/* Allocate a message and reply port for sending messages TO AmigaAMP */
				PluginMsg=AllocVec(sizeof(struct PluginMessage), MEMF_PUBLIC|MEMF_CLEAR);
				PluginRP=CreatePort(0,0);

				/* Tell AmigaAMP all the details it needs to know */
				PluginMsg->msg.mn_Node.ln_Type = NT_MESSAGE;
				PluginMsg->msg.mn_Length       = sizeof(struct PluginMessage);
				PluginMsg->msg.mn_ReplyPort    = PluginRP;
				PluginMsg->PluginMask          = PluginMask;
				PluginMsg->PluginTask          = PluginTask;
				PluginMsg->SpecRawL            = &SpecRawL;
				PluginMsg->SpecRawR            = &SpecRawR;
				PluginMsg->InfoMask            = InfoMask;  // v1.2
				PluginMsg->tinfo               = &tinfo;    // v1.2
				PluginMsg->reserved            = 0;         // v1.2
				PutMsg(PluginMP, (struct Message *)PluginMsg);
				/* Wait for a reply */
				WaitPort(PluginRP);
				/* Let's see if AmigaAMP accepted our registration attempt */
				if(ReplyMsg = (struct PluginMessage *)GetMsg(PluginRP)) Accepted=ReplyMsg->Accepted;
				else Accepted=FALSE;

				if(Accepted) {
					/* If it did, start the plugin loop */
					PluginLoop();

					/* Tell AmigaAMP that this plugin is going down */
					PluginMsg->PluginMask          = 0;		/* (TJ) changed from NULL for VBCC compatibility */
					PluginMsg->PluginTask          = NULL;
					PluginMsg->SpecRawL            = NULL;
					PluginMsg->SpecRawR            = NULL;
					PluginMsg->InfoMask            = 0;     // v1.2
					PluginMsg->tinfo               = NULL;  // v1.2
					PluginMsg->reserved            = 0;     // v1.2
					PutMsg(PluginMP, (struct Message *)PluginMsg);
					/* Wait for confirmation before going on! */
					WaitPort(PluginRP);
					GetMsg(PluginRP);
					/* Now that AmigaAMP knows that we're gone, we can quit */
				}
				else {
					/* If AmigaAMP didn't accept us, tell the user about it */
					ShowRequester("Plugin rejected by AmigaAMP!\nPerhaps there's another one running.", "Abort");
				}

				/* Free all resources */
				FreeVec(PluginMsg);
				DeletePort(PluginRP);
				if(PluginSignal != -1) FreeSignal(PluginSignal);
				if(InfoSignal   != -1) FreeSignal(InfoSignal);   // v1.2
			}
			else {
				ShowRequester("Signal allocation failure!", "Abort");
			}
		}
		else {
			ShowRequester("Could not find message port!\nAmigaAMP probably not running.", "Abort");
		}
	}
	else {
		ShowRequester("Plugin initialisation failed!", "Ok");
	}
	/* Free all user resources */
	PluginExit();

	/* added for VBCC compatibility */
	#ifndef __SASC
	if(IntuitionBase) CloseLibrary((struct Library *)IntuitionBase);
	#endif
	return(0);
}

/*****************************************************************************/
/* Ok, now for the individual plugin sourcecode.                             */
/* Just like before, we start with the global variables section              */
/*****************************************************************************/



/****************************************************************************/
/* This function will be called once when the plugin is started. You should */
/* allocate all needed resources here. Return TRUE if all went well.        */
/****************************************************************************/

BOOL PluginInit(void) {
	/* Initialise everything here. Return TRUE if all went well, FALSE otherwise */
BOOL rc=TRUE;
static struct Library *IconBase;
static struct DiskObject *icon;
static struct Process *prog;
static struct Screen scr;
static struct RastPort *rp;
static struct CommandLineInterface *cli;
static struct DrawInfo *di=0;
char *val,*name;

	prog = (struct Process *)FindTask(0);
	cli = BADDR(prog->pr_CLI);
	name = BADDR(cli->cli_CommandName);
	name++;

	if ((IconBase = OpenLibrary("icon.library",0)))
	{
		if (fromwb)
		{
			CurrentDir(wbmsg->sm_ArgList[0].wa_Lock);
			icon = GetDiskObject(wbmsg->sm_ArgList[0].wa_Name);
		} else {
		icon = GetDiskObject(name)}
		if (icon)
		{
			if ((val = FindToolType((STRPTR *) icon->do_ToolTypes,"XPOS")))
			{
				scrx = strtol(val,0,0);
			}
			if ((val = FindToolType((STRPTR *) icon->do_ToolTypes,"YPOS")))
			{
				scry = strtol(val,0,0);
			}
			
			if ((val = FindToolType((STRPTR *) icon->do_ToolTypes,"FADESPEED")))
			{
				fadespeed = strtol(val,0,0);
				if (fadespeed < 1) fadespeed=1;
				if (fadespeed > 10) fadespeed=10;
			}

			if ((val = FindToolType((STRPTR *) icon->do_ToolTypes,"FRAMESKIP")))
			{
				framespeed = strtol(val,0,0);
				if (framespeed < 0) framespeed=0;
				if (framespeed > 6) framespeed=6;
		
			}

			if ((val = FindToolType((STRPTR *) icon->do_ToolTypes,"YDIRECTION")))
			{
				udirection = strtol(val,0,0);
				if (udirection > 1) udirection=1;
				if (udirection < -1) udirection=-1;
			}

			if ((val = FindToolType((STRPTR *) icon->do_ToolTypes,"XDIRECTION")))
			{
				direction = (strtol(val,0,0)*3);
				if (direction > 3) direction=3;
				if (direction < -3) direction=-3;

			}

			if ((val = FindToolType((STRPTR *) icon->do_ToolTypes,"RANDOMXDIRECTION")))
			{
				randomdirection = TRUE;
				udirection=1;
			}

			if ((val = FindToolType((STRPTR *) icon->do_ToolTypes,"RANDOMFREQ")))
			{
				randomfreq = strtol(val,0,0);
				if (randomfreq <0) randomfreq=0;
				if (randomfreq >50) randomfreq=50;
			}

			FreeDiskObject(icon);
		}
	}

	if (IconBase) CloseLibrary(IconBase);

	if (!(GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",0))) rc=FALSE;
	if (!(CyberGfxBase = (struct Library *)OpenLibrary("cybergraphics.library",41))) rc=FALSE;

	if (!(background = AllocVec(100*10*3,MEMF_PUBLIC+MEMF_CLEAR))) rc=FALSE;
	if (!(mixbackground = AllocVec(100*10*3,MEMF_PUBLIC+MEMF_CLEAR))) rc=FALSE;
	if (!(renderarray = AllocVec(100*10*3,MEMF_PUBLIC+MEMF_CLEAR))) rc=FALSE;

	if (rc == FALSE) return(FALSE); //MUST BE PLACED HERE!

	Delay(5); //wait 1/10 sec for quit of last plugin :)

	if ((wbscr=LockPubScreen(0)))
	{
		rp = &wbscr->RastPort;

		ReadPixelArray(background,0,0,300,rp,
		scrx,scry,100,10,RECTFMT_RGB); //get background

	realdepth=0;
		
		if (IsCyberModeID(GetVPModeID(&wbscr->ViewPort)))
		{
			realdepth = GetBitMapAttr(wbscr->RastPort.BitMap, BMA_DEPTH );
		}
	
		if (realdepth>8)
		{
			if (!(wind = OpenWindowTags(0,WA_Left,scrx,
												WA_Top,scry,
												WA_Width,100,WA_Height,10,
												WA_SizeGadget,FALSE,WA_DragBar,FALSE,
												WA_DepthGadget,FALSE,WA_CloseGadget,FALSE,
												WA_Backdrop,FALSE,WA_Activate,FALSE,
												WA_RMBTrap,TRUE,WA_PubScreen,wbscr,
												WA_Flags,WFLG_BORDERLESS,WA_IDCMP,IDCMP_VANILLAKEY,
												WA_ScreenTitle,VERSTR,
												TAG_DONE))) rc=FALSE;
		} else {rc=FALSE}
	} else {
		rc=FALSE;
	}

	if (rc)
	{
		WritePixelArray(background,0,0,300,wind->RPort,0,0,100,10,RECTFMT_RGB);
		CopyMem(background,renderarray,3*10*100);
		CopyMem(background,mixbackground,3*10*100);
		direction=((RangeRand(3)-1)*3);
	}
	
	return rc;
}

/******************************************************************************/
/* This function will be called when the plugin is shut down. You should free */
/* all previously allocated resources here.                                   */
/******************************************************************************/

void PluginExit(void) {
	/* Free everything you've allocated before */
if (wind) CloseWindow(wind);
if (renderarray) FreeVec(renderarray);
if (background) FreeVec(background);
if (mixbackground) FreeVec(mixbackground);
if (CyberGfxBase) CloseLibrary(CyberGfxBase);
if (GfxBase) CloseLibrary((struct Library *)GfxBase);
if (wbscr)		UnlockPubScreen(0,wbscr);
}

/*******************************************************************************/
/* This is the main Plugin Loop. It will receive a signal matching PluginMask  */
/* each time new spectral data is ready. The data is stored in two arrays,     */
/* UWORD SpecRawL[512] and UWORD SpecRawR[512]. The scale is logarithmic, i.e. */
/* 0 means below -96dB, 65535 means 0dB.                                       */
/* No matter how long it takes until your plugin actually processes the data,  */
/* the memory referenced by the array pointers always remains valid!           */
/*                                                                             */
/* Your plugin loop MUST quit when it receives SIGBREAKF_CTRL_C. If you've     */
/* opened a window you should react to the close gadget as well. For full      */
/* screen plugins I strongly recommend checking the ESC key.                   */
/*******************************************************************************/

void PluginLoop(void) {
	ULONG Signals,windowsig;
	struct IntuiMessage *msg;
	char title[200];UBYTE frame=0;
	
	windowsig = 1L << wind->UserPort->mp_SigBit;

	strcpy(title,VERSTR);

	for(;;) {

		/* Wait until there's something to do */
		Signals=Wait(SIGBREAKF_CTRL_C | PluginMask | windowsig); // v1.2

		/* Break received -> quit at once! */
		if(Signals & SIGBREAKF_CTRL_C) break;


		if(Signals & windowsig);
		{
			msg = (struct IntuiMessage *)GetMsg(wind->UserPort);
			if (msg)
			{
				if (msg->Class = IDCMP_VANILLAKEY)
				{
					if (msg->Code == 27)
					{
						ReplyMsg(&msg->ExecMessage);
						break;
					}
				}
				ReplyMsg(&msg->ExecMessage);
			}
		}

		/* New data has arrived! */
		if(Signals & PluginMask) {
			/*********************************************************/
			/* Visualise SpecRawL[0..511] and SpecRawR[0..512] here! */
			/*********************************************************/

				if (frame == framespeed)
				{
					CopyMem(renderarray,mixbackground,3000);
					if (randomdirection)
					{
						if (rfreq == randomfreq)
						{
							direction=((RangeRand(3)-1)*3);
							rfreq=0;
						}else{
						rfreq++}
					}
					DoLines();
					WritePixelArray(renderarray,0,0,300,wind->RPort,0,0,100,10,RECTFMT_RGB);
					frame=0;
				}else{
				frame++}
					
			}

		/* New track info has arrived! */
//		if(Signals & InfoMask) {
			/**********************************************************************/
			/* If you want to display anything from struct TrackInfo, do it here! */
			/**********************************************************************/
//				sprintf(title,"ABC-%s",&tinfo->TrackInfoText);
//				SetWindowTitles(wind,0,title);
//		}
	}
}


void ShowRequester(char *Text, char *Button) {
	struct EasyStruct Req;

  Req.es_Title        = "AmigaAMP Plugin";
  Req.es_TextFormat   = (UBYTE*)Text;
  Req.es_GadgetFormat = (UBYTE*)Button;
	EasyRequestArgs(NULL, &Req, NULL, NULL);
}

void wbmain(struct WBStartup *msg)
{
	wbmsg = msg;
	if (wbmsg) fromwb = TRUE;
	main(0,0);
}
/* (TJ) added to avoid VBCC linker error */
#ifndef __SASC
struct IntuitionBase *IntuitionBase=NULL;
#endif

void DoLines(void)
{
				MoveLine(0);
				MoveLine(1);
				MoveLine(2);
				MoveLine(3);
				MoveLine(4);
				MoveLine(5);
				MoveLine(6);
				MoveLine(7);
				MoveLine(8);
				MoveLine(9);
				FillLine(0);
				FillLine(1);
				FillLine(2);
				FillLine(3);
				FillLine(4);
				FillLine(5);
				FillLine(6);
				FillLine(7);
				FillLine(8);
				FillLine(9);


}

void MoveLine(UBYTE yposition)
{
UBYTE *triplet,*triplet2,*triplet3,xcnt;
BYTE cval;

	triplet = (UBYTE *)renderarray + (300*yposition);
	triplet2= (UBYTE *)mixbackground + (300*(yposition+1)) + direction;
	switch (yposition)
	{
		case 0:
			triplet2= (UBYTE *)mixbackground + 300;break;
		case 1:
			triplet2= (UBYTE *)mixbackground + (300*(yposition+udirection)) +direction;break;
		case 2:
			triplet2= (UBYTE *)mixbackground + (300*(yposition+udirection));break;
		case 3:
			triplet2= (UBYTE *)mixbackground + (300*(yposition+udirection)) +direction;break;
		case 4:
			triplet2= (UBYTE *)mixbackground + (300*(yposition+udirection));break;
		case 5:
			triplet2= (UBYTE *)mixbackground + (300*(yposition+udirection)) +direction;break;
		case 6:
			triplet2= (UBYTE *)mixbackground + (300*(yposition+udirection));break;
		case 7:
			triplet2= (UBYTE *)mixbackground + (300*(yposition+udirection)) +direction;break;
		case 8:
			triplet2= (UBYTE *)mixbackground + (300*(yposition+udirection));break;
		case 9:
			triplet2= (UBYTE *)mixbackground + (300*yposition);break;
		
	}
	triplet3= (UBYTE *)background;

	for (xcnt = 0; xcnt < 100; xcnt++)
	{
		switch (direction)
		{
			case -3:
				if (xcnt == 0)
				{
					triplet2+=3;
					cval = (((triplet3[0] - triplet2[0])/fadespeed)+1);
					if ((cval+triplet2[0]) <= triplet3[0]) {triplet[0]=triplet2[0]+cval}else{triplet[0]=triplet3[0]};
					cval = (((triplet3[1] - triplet2[1])/fadespeed)+1);
					if ((cval+triplet2[1]) <= triplet3[1]) {triplet[1]=triplet2[1]+cval}else{triplet[1]=triplet3[1]};
					cval = (((triplet3[2] - triplet2[2])/fadespeed)+1);
					if ((cval+triplet2[2]) <= triplet3[2]) {triplet[2]=triplet2[2]+cval}else{triplet[2]=triplet3[2]};
					triplet+=3;//triplet3+=3;
				} else {
					cval = (((triplet3[0] - triplet2[0])/fadespeed)+1);
					if ((cval+triplet2[0]) <= triplet3[0]) {triplet[0]=triplet2[0]+cval}else{triplet[0]=triplet3[0]};
					cval = (((triplet3[1] - triplet2[1])/fadespeed)+1);
					if ((cval+triplet2[1]) <= triplet3[1]) {triplet[1]=triplet2[1]+cval}else{triplet[1]=triplet3[1]};
					cval = (((triplet3[2] - triplet2[2])/fadespeed)+1);
					if ((cval+triplet2[2]) <= triplet3[2]) {triplet[2]=triplet2[2]+cval}else{triplet[2]=triplet3[2]};
					triplet+=3;triplet2+=3;//triplet3+=3;
				}
				break;
			case 0:
					cval = (((triplet3[0] - triplet2[0])/fadespeed)+1);
					if ((cval+triplet2[0]) <= triplet3[0]) {triplet[0]=triplet2[0]+cval}else{triplet[0]=triplet3[0]};
					cval = (((triplet3[1] - triplet2[1])/fadespeed)+1);
					if ((cval+triplet2[1]) <= triplet3[1]) {triplet[1]=triplet2[1]+cval}else{triplet[1]=triplet3[1]};
					cval = (((triplet3[2] - triplet2[2])/fadespeed)+1);
					if ((cval+triplet2[2]) <= triplet3[2]) {triplet[2]=triplet2[2]+cval}else{triplet[2]=triplet3[2]};
					triplet+=3;triplet2+=3;//triplet3+=3;
				break;
			case 3:
				if (xcnt == 99)
				{
					triplet2-=3;
					cval = (((triplet3[0] - triplet2[0])/fadespeed)+1);
					if ((cval+triplet2[0]) <= triplet3[0]) {triplet[0]=triplet2[0]+cval}else{triplet[0]=triplet3[0]};
					cval = (((triplet3[1] - triplet2[1])/fadespeed)+1);
					if ((cval+triplet2[1]) <= triplet3[1]) {triplet[1]=triplet2[1]+cval}else{triplet[1]=triplet3[1]};
					cval = (((triplet3[2] - triplet2[2])/fadespeed)+1);
					if ((cval+triplet2[2]) <= triplet3[2]) {triplet[2]=triplet2[2]+cval}else{triplet[2]=triplet3[2]};
				}else{
					cval = (((triplet3[0] - triplet2[0])/fadespeed)+1);
					if ((cval+triplet2[0]) <= triplet3[0]) {triplet[0]=triplet2[0]+cval}else{triplet[0]=triplet3[0]};
					cval = (((triplet3[1] - triplet2[1])/fadespeed)+1);
					if ((cval+triplet2[1]) <= triplet3[1]) {triplet[1]=triplet2[1]+cval}else{triplet[1]=triplet3[1]};
					cval = (((triplet3[2] - triplet2[2])/fadespeed)+1);
					if ((cval+triplet2[2]) <= triplet3[2]) {triplet[2]=triplet2[2]+cval}else{triplet[2]=triplet3[2]};
					triplet+=3;triplet2+=3;//triplet3+=3;
				}
				break;
		}
	}
}

void FillLine(UBYTE yposition)
{
ULONG level;
UBYTE *triplet,*triplet2,xcnt;
UBYTE red=0xFF,green=0xFF;
	triplet = (UBYTE *)renderarray + (300*yposition);
	triplet2 = (UBYTE *)background + (300*yposition);
	if (yposition > 0)
	{level = 50000-(yposition*5000);}else{level=50000};

	switch (yposition)
	{
		case 1:
			green=0xE0;red=0xF0;break;
		case 2:
			green=0xD0;red=0xF0;break;
		case 3:
			green=0x60;red=0xF0;break;
		case 4:
			green=0x40;red=0xEA;break;
		case 5:
			green=0x20;red=0xD8;break;
		case 6:
			green=0x0;red=0xC6;break;
		case 7:
			red=0xB4;break;
		case 8:
			red=0xA2;break;
		case 9:
			red=0x90;break;
	}
	if (yposition > 6) green=0;

	for (xcnt = 0; xcnt < 100; xcnt++)
	{
		if (level <= ((SpecRawL[xcnt*5] + SpecRawR[xcnt*5])/2))
		{
			triplet[0]=red;	//mix color with back
			triplet[1]=green;
			triplet[2]=0;
		}
			triplet+=3;triplet2+=3;
			if (level>200) level-=200 ;
	}
}

