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

#include <intuition/intuition.h>
#include <dos/dos.h>
#include <libraries/screennotify.h>
#include <workbench/workbench.h>
#include <workbench/startup.h>


#include <proto/intuition.h>
#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/guigfx.h>
#include <proto/graphics.h>
#include <proto/screennotify.h>
#include <proto/icon.h>

#include "flynnapp.h"
#include "global.h"
#include "defs.h"
#include "subtask.h"
#include "flynnpic.h"
#include "tools.h"



void inputloop (struct FlynnApp *flynnapp);
struct FlynnApp *CreateFlynnApp(void);
void DeleteFlynnApp(struct FlynnApp *app);
void ReadConfig(struct FlynnApp *app);
void WriteConfig(struct FlynnApp *app);




int flynn_idle[] = {0,0,5,0,0,10,0,0,0,0,5,5,0,0,0,5,0,0,0,10,0,0,10,10,0,-1};
int flynn_hate[] = {15, -1};
int flynn_evil[] = {20, -1};
int flynn_dead[] = {25, -1};
int flynn_god[] = {26, -1};
int *flynn_anim[] = {flynn_idle, flynn_evil, flynn_hate, flynn_dead, flynn_god, NULL};




LONG __saveds idlefunc(APTR subtask, BYTE abortsignal)
{
	struct idledata *data;
	ULONG abortsignals = 1 << abortsignal, signals;
	struct Task *mytask = FindTask(NULL);
	
	data = ObtainData(subtask);

	data->count = 0;
	
	do
	{
		LONG newpri = ((drand48() * 2.0 - 1.0) * (drand48() * 2.0 - 1.0)) * 7.0;
		SetTaskPri(mytask, newpri); 
		Delay(4);
		data->count++;
		signals = SetSignal(0, abortsignals);
	
	} while (!(signals & abortsignals));
	
	
	ReleaseData(subtask);

	return 0;
}






static int updateflynn(struct FlynnApp *app)
{
	int index;	
	static int lastcount = 0;
	float oldhealth, newhealth;
	static int hatecount = 0;

	oldhealth = app->flynnhealth;

	app->flynnhealth -= 0.20;
	app->flynnhealth += (app->idledata.count - lastcount) / 21.0;

	if (app->flynnhealth < 0.01) app->flynnhealth = 0.01;
	if (app->flynnhealth > 1.0) app->flynnhealth = 1.0;

	newhealth = app->flynnhealth;
	lastcount = app->idledata.count;


	if (newhealth - oldhealth < -0.20)
	{
		app->flynnanimptr = NULL;
		app->flynnmode = FLYNN_HATE;
		hatecount = 6;
	}
	
	if (app->flynnmode == FLYNN_HATE)
	{
		if (--hatecount <= 0)
		{
			app->flynnanimptr = NULL;
			app->flynnmode = FLYNN_IDLE;
		}
	}
	



	if (app->flynnanimptr)
	{
		app->flynnanimptr++;
		if (*app->flynnanimptr == -1)
		{
			app->flynnanimptr = NULL;	
		}
	}

	if (app->flynnanimptr == NULL)
	{
		app->flynnanimptr = flynn_anim[app->flynnmode];
	}



	index = *app->flynnanimptr + (1.0 - app->flynnhealth) * 5;


	return index;
}



void main(void)
{
	if (InitGlobal())
	{
		struct FlynnApp *app;
	
		if (app = CreateFlynnApp())
		{
			int oldpri = SetTaskPri(FindTask(NULL), 2);
			srand(time(NULL));
			inputloop(app);
			SetTaskPri(FindTask(NULL), oldpri);

			DeleteFlynnApp(app);
		}

		CloseGlobal();
	}
}




void inputloop (struct FlynnApp *app)
{
	BOOL finish = FALSE;
	ULONG signals;
	ULONG idcmpsignals;
	ULONG scrnotifysignals = 0;
	struct IntuiMessage *imsg;
	struct IntuiMessage myimsg;
	int last_index = -1, new_index;
	BOOL refresh = TRUE;
	int writeconfig = 0;
	APTR dh = NULL;

	
	if (ScreenNotifyBase && app->scrmsgport && app->scrnotifyhandle)
	{
		scrnotifysignals = (1 << app->scrmsgport->mp_SigBit);
	}


	do
	{
		new_index = updateflynn(app);

		if (!dh && app->window)
		{

			ClearWindow(app->window);
			dh = ObtainDrawHandle(app->psm, app->window->window->RPort,
				app->window->screen->ViewPort.ColorMap, 
				GGFX_DitherMode, DITHERMODE_NONE,
				GGFX_AutoDither, FALSE, TAG_DONE);
		}
		
		if (dh && app->window)
		{
			if (new_index != last_index || refresh)
			{
				DrawPicture(dh, app->flynnpicture,
					app->window->innerleft + BORDERX,
					app->window->innertop + BORDERY,
					GGFX_SourceWidth, FLYNNWIDTH, GGFX_SourceX, 0,
					GGFX_SourceHeight, FLYNNHEIGHT, 
					GGFX_SourceY, new_index * FLYNNHEIGHT,
						GGFX_DestWidth, app->window->innerwidth - BORDERX*2,
					GGFX_DestHeight, app->window->innerheight - BORDERY*2,
					TAG_DONE);
				last_index = new_index;
			}
		}
	

		if (refresh)
		{
			refresh = FALSE;
		}
		else
		{
			Delay( (ULONG)((drand48() - 0.5) * 7.0 + 25.0));
		}

		idcmpsignals = app->window ? app->window->idcmpSignal : 0;

		signals = SetSignal(0, SIGBREAKF_CTRL_C | idcmpsignals | scrnotifysignals);

	

		if ((signals & idcmpsignals) && app->window)
		{
			while (imsg = (struct IntuiMessage *) GetMsg(app->window->window->UserPort))
			{
				memcpy(&myimsg, imsg, sizeof(struct IntuiMessage));
				ReplyMsg((struct Message *) imsg);

				switch (myimsg.Class)
				{
					case ACTIVEWINDOW:
					case INACTIVEWINDOW:
						writeconfig = 10;
						break;
						
					case MOUSEBUTTONS:
						app->flynnhealth -= 0.2;
						break;

					case NEWSIZE:
						ClearWindow(app->window);
						UpdateWindowParameters(app->window);
						app->window->sizegadget.LeftEdge = app->window->window->Width-SIZESIZE;
						app->window->sizegadget.TopEdge = app->window->window->Height-SIZESIZE;
						app->window->draggadget.Width = app->window->window->Width;
						app->window->draggadget.Height = app->window->window->Height;
						refresh = TRUE;
						writeconfig = 10;
						break;

					case VANILLAKEY:
						switch (myimsg.Code)
						{
							case 27:
								finish = TRUE;
								break;
						}
				}

			}
		}

		if (signals & scrnotifysignals)
		{
			struct ScreenNotifyMessage *snm;

			while (snm = (struct ScreenNotifyMessage *) GetMsg(app->scrmsgport))
			{
				if (snm->snm_Type == SCREENNOTIFY_TYPE_WORKBENCH)
				{
					if (snm->snm_Value == FALSE)
					{
						if (app->window)
						{
							app->winx = app->window->window->LeftEdge;
							app->winy = app->window->window->TopEdge;
							app->winwidth = app->window->window->Width;
							app->winheight = app->window->window->Height;
							
							ReleaseDrawHandle(dh);
							dh = NULL;
							DeleteMVWindow(app->window);
							app->window = NULL;
						}
					}
					else
					{
						if (!app->window)
						{
							if (!(app->window = CreateMVWindow(app->winx, app->winy, app->winwidth, app->winheight)))
							{
								finish = TRUE;
							}
						}
					}
				}
				ReplyMsg((struct Message *) snm);
			}
		}

		if (writeconfig)
		{
			if (--writeconfig == 0)
			{
				WriteConfig(app);
			}
		}

		if (signals & SIGBREAKF_CTRL_C)
		{
			finish = TRUE;
		}
		
	} while (!finish);


	ReleaseDrawHandle(dh);

}





struct FlynnApp *CreateFlynnApp(void)
{
	struct FlynnApp *app;
	BOOL success = FALSE;

	
	if (app = Malloclear(sizeof(struct FlynnApp)))
	{
		app->winx = -1;		
		app->winy = -1;		
		app->winwidth = APPWIDTH;
		app->winheight = APPHEIGHT;
		
		if (_WBenchMsg)
		{
			if (NameFromLock(GetProgramDir(), app->filenamebuffer, MAXNAMELEN))
			{
				if (AddPart(app->filenamebuffer, _WBenchMsg->sm_ArgList[0].wa_Name, MAXNAMELEN))
				{
					app->progname = StrDup(app->filenamebuffer);
				}
			}
		}
		else
		{
			if (GetProgramName(app->filenamebuffer, MAXNAMELEN))
			{
				char *s;
				if (s = StrDup(app->filenamebuffer))
				{
					if (NameFromLock(GetProgramDir(), app->filenamebuffer, MAXNAMELEN))
					{
						if (AddPart(app->filenamebuffer, s, MAXNAMELEN))
						{
							app->progname = StrDup(app->filenamebuffer);
						}
					}
					Free(s);
				}
			}
		}

		ReadConfig(app);
		
		if (app->subtask = SubTask(idlefunc, &app->idledata, 1000, 0, "flynn idletask (%x)", NULL, FALSE))
		{
			app->window = CreateMVWindow(app->winx, app->winy, app->winwidth, app->winheight);

		//	app->flynnpicture = LoadPicture("flynn.picture", TAG_DONE);
			
			app->flynnpicture = MakePicture(flynndata, 24, 864, 
				GGFX_NumColors, 256, GGFX_Palette, flynnpalette, TAG_DONE);
			
			
			app->psm = CreatePenShareMap(TAG_DONE);
			
			app->flynnhealth = 1.0;
			app->flynnmode = FLYNN_IDLE;
			app->flynnanimptr = NULL;
			
			if (app->window && app->flynnpicture && app->psm)
			{
				if (ScreenNotifyBase)
				{
					if (app->scrmsgport = CreateMsgPort())
					{
						app->scrnotifyhandle = AddWorkbenchClient(app->scrmsgport, 0);
					}
				}
			
				GetPictureAttrs(app->flynnpicture,
					PICATTR_Width, &app->flynnwidth,
					PICATTR_Height, &app->flynnheight,
					TAG_DONE);
		
				AddPicture(app->psm, app->flynnpicture, TAG_DONE);
		
		
		
		
				success = TRUE;		
			}		

		}
		
	}


	if (!success)
	{
		DeleteFlynnApp(app);
		app = NULL;
	}
	
	return app;
}



void DeleteFlynnApp(struct FlynnApp *app)
{
	if (app)
	{
		if (app->window)
		{
			WriteConfig(app);
		}

		Free(app->progname);

		if (ScreenNotifyBase)
		{
			if (app->scrnotifyhandle)
			{
				while (!RemWorkbenchClient(app->scrnotifyhandle))
				{
					Delay(10);
				}
			}
			DeleteMsgPort(app->scrmsgport);
		}
	
		if (app->subtask)
		{
			AbortSubTask(app->subtask);
			CloseSubTask(app->subtask);
		}

		DeletePenShareMap(app->psm);
		DeletePicture(app->flynnpicture);
		DeleteMVWindow(app->window);
		Free(app);	
	}

}





void ReadConfig(struct FlynnApp *app)
{
	if (app->progname)
	{
		struct DiskObject *dobj;

		if (dobj = GetDiskObject(app->progname))
		{
			char **tt = dobj->do_ToolTypes;
			char *p;
			
			if (p = FindToolType(tt, "WINLEFT"))
			{
				app->winx = atoi(p);
			}
	
			if (p = FindToolType(tt, "WINTOP"))
			{
				app->winy = atoi(p);
			}
	
			if (p = FindToolType(tt, "WINWIDTH"))
			{
				app->winwidth = atoi(p);
			}
	
			if (p = FindToolType(tt, "WINHEIGHT"))
			{
				app->winheight = atoi(p);
			}
		
			FreeDiskObject(dobj);
		}

	}
}


void SetTTEntry(struct List *ttlist, char *name, int value)
{
	BOOL found = FALSE;
	char buffer[100];
	struct Node *node, *nextnode;
	int len = strlen(name), dlen;
	char *newstring, *disabled;

	sprintf(buffer, "%s=%ld", name, value);
	newstring = StrDup(buffer);
	sprintf(buffer, "(%s", name);
	disabled = StrDup(buffer);
	
	if (newstring && disabled)
	{
		dlen = strlen(disabled);
		node = ttlist->lh_Head;
		while (nextnode = node->ln_Succ)
		{
			if (strncmp(name, node->ln_Name, len) == 0)
			{
				found = TRUE;
			}
			else if (strncmp(disabled, node->ln_Name, dlen) == 0)
			{
				found = TRUE;			
			}

			if (found)
			{
				Free(node->ln_Name);
				node->ln_Name = newstring;
				newstring = NULL;
				break;
			}
			
			node = nextnode;
		}

		if (!found)
		{
			struct Node *newnode;
			if (newnode = CreateNode(newstring))
			{
				AddTail(ttlist, newnode);
			}		
		}
	}

	Free(newstring);
	Free(disabled);

}


void WriteConfig(struct FlynnApp *app)
{
	if (app->progname && app->window)
	{
		struct DiskObject *dobj;
		
		dobj = GetDiskObject(app->progname);

		if (!dobj)
		{
			dobj = GetDefDiskObject(WBTOOL);
		}
		
		if (dobj)
		{
			struct List *ttlist;
			if (ttlist = CreateListFromArray(dobj->do_ToolTypes))
			{
				char **newtt;

				SetTTEntry(ttlist, "WINLEFT", app->window->window->LeftEdge);
				SetTTEntry(ttlist, "WINTOP", app->window->window->TopEdge);
				SetTTEntry(ttlist, "WINWIDTH", app->window->window->Width);
				SetTTEntry(ttlist, "WINHEIGHT", app->window->window->Height);
				
				if (newtt = CreateStringArrayFromList(ttlist))
				{
					dobj->do_ToolTypes = newtt;
					PutDiskObject(app->progname, dobj);
					FreeStringArray(newtt);
				}

				DeleteList(ttlist);
			}	
			FreeDiskObject(dobj);
		}
	}
}
