/* Application.c */

#include "Constants.h"
#include "Locale.h"

#include <exec/types.h>
#include <exec/memory.h>
#include <exec/libraries.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <intuition/gadgetclass.h>
#include <libraries/gadtools.h>
#include <libraries/commodities.h>
#include <libraries/asl.h>
#include <devices/scsidisk.h>
#include <devices/cd.h>
#include <graphics/rastport.h>
#include <pragma/screennotify_lib.h>
#include "AsmLib/CD-ROM.h"
#include "Structures/Application.h"

#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/commodities_protos.h>
#include <clib/intuition_protos.h>
#include <clib/gadtools_protos.h>
#include <clib/icon_protos.h>
#include <clib/wb_protos.h>
#include <string.h>
#include <stdio.h>
#include "ACDPlay_protos.h"

extern char	*ls[ANZ_MSG];
extern char	 PROG_NAME[];
extern char	*PROG_FULLNAME;

struct Application *CreateApplication(char *progname)
{
	struct Application *app	= NULL;

	/* Speicher für Applicationstruktur besorgen */
	if (app = GetVecA(sizeof(struct Application), MEMF_PUBLIC | MEMF_CLEAR))
	{
		app->progname = progname;
		app->orgprwin = ((struct Process *)FindTask(NULL))->pr_WindowPtr;

		if (app->cdstr = GetVecA(sizeof(struct CDStruct), MEMF_PUBLIC | MEMF_CLEAR))
		{
			app->edit_pos = -1;
			ReadArguments(app);
			ReadConfig(app, "ENV:acdplay.cfg");
			app->arexxdir = GetDirContents("PROGDIR:ARexx");
			app->diskobj = NULL;

			if (!FindARexxPortA())	/* Falls ACDPlay noch nicht läuft */
			{
				if (app->arexx_mp = OpenARexxPortA())
				{
					if (OpenCDStruct(app->cdstr))
					{
						if (app->scroller = GetVecA(sizeof(struct Scroll), MEMF_CLEAR | MEMF_PUBLIC))
						{
							int i;

							app->appwin[WINDOW_PUBSCREEN].minwidth = MINWIDTH_LISTVIEWWIN;
							app->magicwbmode = (app->flag_tool & FLAG_TOOL_MWB) ? TRUE : FALSE;

							/* ASL-Requester-Werte eintragen */
							for (i = 0; i < ANZ_ASL; i++)
							{
								app->appasl[i].leftedge	= 100;
								app->appasl[i].topedge	= 50;
								app->appasl[i].width	= 200;
								app->appasl[i].height	= 200;
								app->appasl[i].noicons	= TRUE;
							}
							/* spezifische ASL-Werte */
							app->appasl[ASL_AREXX].titletext	= ls[MSG_ASL_AREXX_HAIL];
							strcpy(app->appasl[ASL_AREXX].dirname, "ARexx/");
							app->appasl[ASL_SAVEAUDIO].titletext	= ls[MSG_ASL_SAVEAUDIO_HAIL];

							UpdateCDTitles(app);	/* CD-Titel setzen */
							ProgramLoad(app);	/* Programm laden (nicht mehr in
										   UpdateCDTitles enthalten!) */
							if (CreateCommodity(app))
								return (app);
							else
								ShowMsgA(ls[MSG_ERROR_TITLE], ls[MSG_NO_CX], NULL);
						}
					}
				}
			}
		}
	}

	DeleteCxObjAll(app->broker);
	DeleteMsgPort(app->broker_mp);
	FreeVec(app->scroller);
	FreeList(app->arexxdir);
	CloseARexxPortA(app->arexx_mp);
	FreeCDStruct(app->cdstr);
	FreeVec(app);
	return (NULL);
}

void DeleteApplication(struct Application *app)
{
	CloseApplication(app);
	FreeAppItem(app);

	/* ScreenNotify-Port, falls vorhanden, entfernen */
	if (app->scrnotify_mp)
	{
		/* Solange versuchen den Screennotifyclient zu schließen, bis es klappt */
		if (app->snhandle_wbench)
			while (!RemWorkbenchClient(app->snhandle_wbench))
				Delay(10);

		Forbid();
		FlushMessagesA(app->scrnotify_mp);
		DeleteMsgPort(app->scrnotify_mp);
		Permit();
	}

	/* Muß leider hier plaziert werden, da die Listen in CreateApplication() */
	/* erstellt wurden: */
	FreeList(app->cdstr->playlist[PLAYLIST_CD]);
	FreeList(app->cdstr->playlist[PLAYLIST_PROGRAM]);
	FreeList(app->arexxdir);

	/* Commodities: Broker löschen, MsgPort leeren und freigeben */
	DeleteCxObjAll(app->broker);
	Forbid();							/* Sicher ist sicher :-) */
	FlushMessagesA(app->broker_mp);
	DeleteMsgPort(app->broker_mp);
	Permit();

	FreeVec(app->scroller);

	FreeCDStruct(app->cdstr);
	CloseARexxPortA(app->arexx_mp);
	FreeVec(app);
}

BOOL OpenApplication(struct Application *app, BOOL activate)
{
	BOOL success = TRUE;

	if (!app->screen)
	{
		success = FALSE;

		app->screen = LockPubScreen(app->pubscreenname);	/* Es wird zuerst versucht, */
		if (!app->screen)									/* den gewünschten PubScreen */
			app->screen = LockPubScreen(NULL);				/* zu locken. */
		if (!app->screen)									/* Bei Scheitern wird es mit dem */
		{													/* DefaultPubScreen versucht, */
			ShowMsgA(ls[MSG_ERROR_TITLE], ls[MSG_NO_PUBSCREEN], NULL);	/* wenn das nicht klappt, */
			return (success);								/* steigt ACDPlay aus */
		}

		if (activate)
			ScreenToFront(app->screen);

		if (app->screen->Flags & WBENCHSCREEN)
			app->Flags |= APPF_WORKBENCH;
		else
			app->Flags &= ~APPF_WORKBENCH;

		if (app->visualinfo = GetVisualInfo(app->screen, TAG_END))
		{
			WORD win;
			app->font = app->screen->Font;

			success = TRUE;

			if (!MakeMenus(app)) return (FALSE);

			/* Message-Port erstellen, der als shared-IDCMP-Port benutzt wird */
			if (app->idcmp = CreateMsgPort())
			{
				for (win = ANZ_WINDOWS - 1; win >= 0; win--)
					if (app->appwin[win].winopen)
						if (!(OpenAppWin(app, win, activate))) success = FALSE;
			}

			/* Damit die Application nicht mehr durch OpenWorkBench() geöffnet werden kann */
			app->Flags &= ~APPF_SNCLOSED;
		}
		else ShowMsgA(ls[MSG_ERROR_TITLE], ls[MSG_NO_VISUALINFO], app->screen->Title);
	}
	else
		if (activate)
		{
			ScreenToFront(app->screen);		/* Screen wird *immer* nach vorne geholt */
			WindowToFront(app->appwin[WINDOW_MAIN].window);
			ActivateWindow(app->appwin[WINDOW_MAIN].window);
		}

	return(success);
}

void CloseApplication(Application *app)
{
	if (app->screen)
	{
		WORD win;

		for (win = ANZ_WINDOWS - 1; win >= 0; win--)
			if (app->appwin[win].winopen) CloseAppWin(app, win, FALSE);

		/* DeleteMsgPort(), FreeMenus() und FreeVisualInfo() testen
		   selbstständig auf NULL-Pointer */

		DeleteMsgPort(app->idcmp);
		app->idcmp = NULL;

		FreeMenus(app->menustrip);
		app->menustrip = NULL;

		FreeVisualInfo(app->visualinfo);
		app->visualinfo = NULL;

		if (app->screen) {
			UnlockPubScreen(NULL, app->screen);
			app->screen = NULL;
		}
	}
}

void Iconify(struct Application *app)
{
	CloseApplication(app);

	if (app->hidemode != HIDEMODE_NONE)
		CreateAppItem(app);
}

void UnIconify(struct Application *app)
{
	if (app->hidemode != HIDEMODE_NONE)
		FreeAppItem(app);

	OpenApplication(app, TRUE);
}

BOOL OpenCDStruct(struct CDStruct *cd)
{
	BOOL success = FALSE;

	if (cd->cdx = CDOpenDeviceA(cd->device, cd->unit, cd->lun))
	{
		/* Wenn geöffnetes Gerät kein CD-ROM-Laufwerk ist */
		if (cd->cdx->cdx_DeviceType != DEVTYPE_CDROM)
		{
			struct EasyStruct req={	sizeof(struct EasyStruct),	/* es_StructSize */
									0,							/* es_Flags */
									ls[MSG_WARNING_TITLE],		/* es_Title */
									ls[MSG_BAD_SCSIDEVICE],		/* es_TextFormat */
									ls[MSG_YESNO_REQ] };		/* es_GadFormat */

			if (!EasyRequestArgs(NULL, &req, NULL, &(ULONG)cd->unit))
				return (success);
		}

		cd->num_track 	= cd->cdx->cdx_TOCData->TrackNum;
		cd->cur_track	= cd->cdx->cdx_CurrentTrack;
		cd->active	= cd->cdx->cdx_Active;
		cd->cdnameptr	= cd->cdname;
		cd->artistptr	= cd->artistname;

		cd->fileformat	= FORMAT_CDR;
		cd->freq	= FREQ_44100;

		success = TRUE;
	}

	return (success);
}

void FreeCDStruct(CDStruct *cd)
{
	int i;

	if (cd->cdx)
		CDCloseDeviceA(cd->cdx);

	/* Tracknamen freigeben */
	for (i = 0; cd->tracknames[i]; i++)
		FreeVec(cd->tracknames[i]);
	FreeVec(cd->tracknames);

	FreeVec(cd);
}

void CreateAppItem(struct Application *app)
{
	if (app->appitem_mp = CreateMsgPort())
		switch (app->hidemode)
		{
			case HIDEMODE_APPICON:
				if (!app->diskobj)
					if (app->diskobj = GetDiskObject(app->progname))
					{
						/* Das muß wohl so sein */
						app->diskobj->do_Type = NULL;
						app->appitem = (APTR)AddAppIcon(0L, 0L, PROG_NAME, app->appitem_mp, NULL, app->diskobj, TAG_DONE);
					}
				break;

			case HIDEMODE_APPMENUITEM:
				app->appitem = (APTR)AddAppMenuItem(0L, 0L, PROG_NAME, app->appitem_mp, TAG_DONE);
				break;
		}
}

void FreeAppItem(struct Application *app)
{
	if (app->appitem)
	{
		switch (app->hidemode)
		{
			case HIDEMODE_APPICON:
				RemoveAppIcon((struct AppIcon *)app->appitem);
				break;
			case HIDEMODE_APPMENUITEM:
				RemoveAppMenuItem((struct AppMenuItem *)app->appitem);
				break;
		}
		app->appitem = NULL;
	}

	FlushMessagesA(app->appitem_mp);
	DeleteMsgPort(app->appitem_mp);
	app->appitem_mp = NULL;

	if (app->diskobj)
	{
		FreeDiskObject(app->diskobj);
		app->diskobj = NULL;
	}
}

BOOL CreateCommodity(struct Application *app)
{
	/* Erstellen des Commodities */
	if (app->broker_mp = CreateMsgPort())
	{
		struct NewBroker newbroker = {
				NB_VERSION,					/* nb_Version - Version of the NewBroker structure */
				PROG_NAME,					/* nb_Name - Name of the commodity */
				PROG_FULLNAME,				/* nb_Title */
				ls[PROG_DESCR],				/* nb_Decr - Description */
				NBU_UNIQUE,					/* nb_Unique */
				COF_SHOW_HIDE,				/* nb_Flags */
				app->cx_priority,			/* nb_Pri - Priority */
				app->broker_mp,				/* nb_Port - MsgPort CX talks to */
				0 };						/* nb_ReservedChannel */

		if (app->broker = CxBroker(&newbroker, NULL))
		{
			if (app->filter_popkey = CxFilter(app->cx_popkey))
			{
				AttachCxObj(app->broker, app->filter_popkey);
				if (app->sender_popkey = CxSender(app->broker_mp, EVENT_HOTKEYPRESSED))
				{
					AttachCxObj(app->filter_popkey, app->sender_popkey);
					if (app->translate_popkey = CxTranslate(NULL))
					{
						AttachCxObj(app->filter_popkey, app->translate_popkey);

						if (app->filter_timer = CxFilter("timer -caps -alt -control -lcommand -rcommand -numericpad -rbutton -leftbutton -midbutton"))
						{
							AttachCxObj(app->broker, app->filter_timer);
							if (app->sender_timer = CxSender(app->broker_mp, EVENT_TIMER))
							{
								AttachCxObj(app->filter_timer, app->sender_timer);
								ActivateCxObj(app->broker, 1L);

								if (ScreenNotifyBase)
								{
									if (app->scrnotify_mp = CreateMsgPort())
										app->snhandle_wbench = AddWorkbenchClient(app->scrnotify_mp, 1);
									else
										ShowMsgA(ls[MSG_ERROR_TITLE], ls[MSG_NO_MSGPORT], NULL);
								}

								return (TRUE);
							}
						}
					}
				}
			}
		}
		DeleteMsgPort(app->broker_mp);
	}
	else ShowMsgA(ls[MSG_ERROR_TITLE], ls[MSG_NO_MSGPORT], NULL);

	return (FALSE);
}