/*
**	$VER: XModule 3.9 (16.8.97) Copyright (C) 1993,94,95,96 Bernardo Innocenti
**
**	This source code is provided "AS-IS", without warranties of any kind and
**	it is subject to change without notice.  All usage is at your own risk.
**	No liability or responsibility is assumed by the author.
**
**	Use 4 chars wide TABs to read this file
*/

#include <exec/execbase.h>
#include <exec/lists.h>
#include <exec/memory.h>
#include <dos/dos.h>
#include <dos/dostags.h>
#include <libraries/gadtools.h>
#include <utility/utility.h>
#include <workbench/startup.h>

#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/intuition.h>
#include <proto/icon.h>
#include <proto/utility.h>
#include <proto/xmodule.h>


#include "XModulePriv.h"
#include "Gui.h"



/* Local functions prototypes */

static LONG GetShellArgs (void);
static LONG GetWBArgs (void);
static void DisposeArgs (void);
static void HandleFrom (void);
static void SetupHooks (void);
static void Cleanup (LONG err);
static LONG Setup (void);



/* Memory pool for use by main program only */
XDEF void *Pool = NULL;



/* Version tag */
XDEF const UBYTE Version[] = "$VER: " VSTR " " BUILDMODE " " XMODULECOPY;
XDEF const UBYTE BaseName[] = BASENAME;
XDEF const UBYTE PrgName[] = PRGNAME;



/* Library bases */
XDEF struct IntuitionBase	*IntuitionBase	= NULL;
XDEF struct GfxBase			*GfxBase		= NULL;
XDEF struct Library			*LayersBase		= NULL;
XDEF struct UtilityBase		*UtilityBase	= NULL;
XDEF struct Library			*GadToolsBase	= NULL;
XDEF struct Library			*DiskfontBase	= NULL;
XDEF struct Library			*AslBase		= NULL;
XDEF struct Library			*IFFParseBase	= NULL;
XDEF struct Library			*WorkbenchBase	= NULL;
XDEF struct Library			*IconBase		= NULL;
XDEF struct ReqToolsBase	*ReqToolsBase	= NULL;
XDEF struct Library			*CxBase			= NULL;
XDEF struct Library			*KeymapBase		= NULL;
XDEF struct XModuleBase		*XModuleBase	= NULL;



/* This structure holds data to open required libraries automatically. */

struct OpenLibs
{
	struct Library **Base;
	UBYTE *Name;
	LONG Version;
};


#ifdef OS30_ONLY
 #define OSLIBVER 39
#else
 #define OSLIBVER 37
#endif /* !OS30_ONLY */

static const struct OpenLibs openlibs[] =
{
	{ (struct Library **)&IntuitionBase,	"intuition.library",	OSLIBVER },
	{ (struct Library **)&GfxBase,			"graphics.library",		OSLIBVER },
	{ (struct Library **)&LayersBase,		"layers.library",		OSLIBVER },
	{ (struct Library **)&UtilityBase,		"utility.library",		OSLIBVER },
	{ (struct Library **)&GadToolsBase,		"gadtools.library",		OSLIBVER },
	{ (struct Library **)&KeymapBase,		"keymap.library",		37 },
	{ (struct Library **)&IFFParseBase,		"iffparse.library",		37 },
	{ (struct Library **)&WorkbenchBase,	"workbench.library",	37 },
	{ (struct Library **)&IconBase,			"icon.library",			37 },
	{ (struct Library **)&DiskfontBase,		"diskfont.library",		0 },

	{ NULL }
};



/* Used for argument parsing.
 * NOTE: All fields must be 4 bytes long.
 */
static struct
{
	STRPTR	*From,
			 PubScreen,
			 PortName,
			 Settings;
	LONG	 CxPopup;		/* Set to TRUE by default. */
	STRPTR	 CxPopKey;
	LONG	*CxPriority,
			*IconX,
			*IconY;
	STRPTR	 IconName;
} XMArgs; /* egcs doesn't like this: = { 0 }; */

static struct RDArgs *RDArgs = NULL;

#define ARGS_TEMPLATE	"FROM/M,PUBSCREEN/K,PORTNAME/K,SETTINGS/K,CX_POPUP/T,CX_POPKEY/K,CX_PRIORITY/K/N,ICONXPOS/K/N,ICONYPOS/K/N,ICONNAME/K"



extern LONG STDARGS __main (void)

/* XModule main entry point.  Get arguments from CLI/Workbench,
 * setup environment, open required files and call main event
 * handling loop.
 */
{
	LONG err;

	DB(kprintf ("\n"));
	DB(kprintf ((STRPTR)(Version + 6)));
	DB(kprintf ("\n*** Starting debug session -- Good luck!\n\n"));


	if (!(err = Setup())) /* Setup environment */
		err = HandleGui();

	Cleanup (err);

	return err;
}	/* End main() */



static LONG GetShellArgs (void)

/* Parse command line arguments */
{
	if (!(RDArgs = ReadArgs (ARGS_TEMPLATE, (LONG *)&XMArgs, NULL)))
		return IoErr();

	return RETURN_OK;

} /* End GetShellArgs() */



static LONG GetWBArgs (void)

/* Parse Workbench arguments */
{
	struct DiskObject *dobj;
	STRPTR val;
	UWORD i;


	/* Get Multiselect args.
	 * Create a NULL-terminated array of STRPTRs
	 * in the same way ReadArgs() would have done.
	 */
	if (WBenchMsg->sm_NumArgs > 1)
		if (!(XMArgs.From = AllocVec (WBenchMsg->sm_NumArgs * sizeof (STRPTR), MEMF_CLEAR)))
			return RETURN_FAIL;

	for (i = 1; i < WBenchMsg->sm_NumArgs; i++)
	{
		UBYTE buf[PATHNAME_MAX];

		if (NameFromLock (WBenchMsg->sm_ArgList[i].wa_Lock, buf, PATHNAME_MAX))
			if (AddPart (buf, WBenchMsg->sm_ArgList[i].wa_Name, PATHNAME_MAX))
			{
				if (XMArgs.From[i-1] = AllocVec (strlen (buf), MEMF_ANY))
					strcpy (XMArgs.From[i-1], buf);
				else
					break;
			}
	}


	/* Get ToolTypes */

	if (!(dobj = GetProgramIcon()))
		return RETURN_FAIL;

	if (val = FindToolType (dobj->do_ToolTypes, "PUBSCREEN"))
		if (XMArgs.PubScreen = AllocVec (strlen (val), MEMF_ANY))
			strcpy (XMArgs.PubScreen, val);

	if (val = FindToolType (dobj->do_ToolTypes, "PORTNAME"))
		if (XMArgs.PortName = AllocVec (strlen (val), MEMF_ANY))
			strcpy (XMArgs.PortName, val);

	if (val = FindToolType (dobj->do_ToolTypes, "SETTINGS"))
		if (XMArgs.Settings = AllocVec (strlen (val), MEMF_ANY))
			strcpy (XMArgs.Settings, val);

	if (val = FindToolType (dobj->do_ToolTypes, "CX_POPUP"))
		XMArgs.CxPopup = MatchToolValue (val, "YES");

	if (val = FindToolType (dobj->do_ToolTypes, "CX_POPKEY"))
		if (XMArgs.CxPopKey = AllocVec (strlen (val), MEMF_ANY))
			strcpy (XMArgs.CxPopKey, val);

	if (val = FindToolType (dobj->do_ToolTypes, "CX_PRIORITY"))
		if (XMArgs.CxPriority = AllocVec (sizeof (LONG), MEMF_ANY))
			StrToLong (val, XMArgs.CxPriority);

	if (val = FindToolType (dobj->do_ToolTypes, "ICONXPOS"))
		if (XMArgs.IconX = AllocVec (sizeof (LONG), MEMF_ANY))
			StrToLong (val, XMArgs.IconX);

	if (val = FindToolType (dobj->do_ToolTypes, "ICONYPOS"))
		if (XMArgs.IconY = AllocVec (sizeof (LONG), MEMF_ANY))
			StrToLong (val, XMArgs.IconY);

	if (val = FindToolType (dobj->do_ToolTypes, "ICONNAME"))
		if (XMArgs.IconName = AllocVec (strlen (val), MEMF_ANY))
			strcpy (XMArgs.IconName, val);

	FreeDiskObject (dobj);

	return RETURN_OK;
} /* End GetWBArgs() */



static void DisposeArgs (void)
{
	if (RDArgs)
	{
		FreeArgs (RDArgs);
		RDArgs = NULL;
	}
	else	/* Workbench */
	{
		/* NULL is a valid parameter for FreeVec() */
		FreeVec (XMArgs.IconName);
		FreeVec (XMArgs.IconY);
		FreeVec (XMArgs.IconX);
		FreeVec (XMArgs.CxPriority);
		FreeVec (XMArgs.CxPopKey);
		FreeVec (XMArgs.Settings);
		FreeVec (XMArgs.PortName);
		FreeVec (XMArgs.PubScreen);

		if (XMArgs.From)
		{
			STRPTR *tmp = XMArgs.From;

			while (*tmp)
			{
				FreeVec (*tmp);
				tmp++;
			}

			FreeVec (XMArgs.From);
		}
	}

	memset (&XMArgs, 0, sizeof (XMArgs));
}



static void HandleFrom (void)
{
	if (XMArgs.From)
	{
		STRPTR				*name = XMArgs.From;
		struct AnchorPath	*ap;
		LONG err;

		if (ap = AllocMem (sizeof (struct AnchorPath) + PATHNAME_MAX, MEMF_CLEAR))
		{
			OpenProgressWindow ();

			ap->ap_BreakBits = SIGBREAKF_CTRL_C;
			ap->ap_Strlen = PATHNAME_MAX;

			while (*name)
			{
				err = MatchFirst (*name, ap);

				while (!err)
				{
					xmLoadModule (ap->ap_Buf,
						XMSNG_AddToList, TRUE,
						TAG_DONE);

					err = MatchNext (ap);
				}

				if (err != ERROR_NO_MORE_ENTRIES)
				{
					UBYTE buf[FAULT_MAX];

					Fault (err, NULL, buf, FAULT_MAX);
					ShowMessage (MSG_ERR_LOAD, *name, buf);
				}

				MatchEnd (ap);
				name++;
			}

			CloseProgressWindow();

			FreeMem (ap, sizeof (struct AnchorPath) + PATHNAME_MAX);
		}
		else LastErr = ERROR_NO_FREE_STORE;
	}
}



static void SetupHooks (void)
{
	struct Library			*XMHookBase;
	struct FileInfoBlock	*fib;
	BPTR					 lock;
	UBYTE					 libpath[PATHNAME_MAX];

	/* Built-in hooks */
	AddXModuleHooks ();
	AddTrackerHooks ();


	if (fib = (struct FileInfoBlock *)AllocDosObject (DOS_FIB, NULL))
	{
		if (lock = Lock (DEF_HOOKSDIR, ACCESS_READ))
			if (Examine (lock, fib))
				while (ExNext (lock, fib))
				{
					strcpy (libpath, DEF_HOOKSDIR);
					AddPart (libpath, fib->fib_FileName, PATHNAME_MAX);

					if (XMHookBase = OpenLibrary (libpath, 0))
					{
						#if defined(__SASC)
							#pragma libcall XMHookBase SetupXMHook 24 801
							void SetupXMHook (struct XModuleBase *);
						#elif defined(__GNUC__)
							#define SetupXMHook(xmbase) \
								LP1NR(0x24, SetupXMHook, struct XModuleBase *, xmbase, a0, \
								, XMHookBase)
						#else
							#error Define SetupXMHook() library call for your compiler
						#endif

						SetupXMHook (XModuleBase);
						CloseLibrary (XMHookBase);
					}
				}

		FreeDosObject (DOS_FIB, fib);
	}


	/* Set default saver */

	if (!IsListEmpty ((struct List *)&XModuleBase->xm_Savers))
		XModuleBase->xm_DefaultSaver = (struct XMHook *)XModuleBase->xm_Savers.mlh_Head;
}



static LONG Setup (void)
{
	LONG err;
	ULONG i;

	SetProgramName (PrgName);

	/* Initialize view lists */
	NEWLIST (&WindowList);
	NEWLIST (&LogList);
	NEWLIST (&PatternsList);
	NEWLIST (&SequenceList);
	NEWLIST (&InstrList);

	/* Install graphics function replacements */
	/*	InstallGfxFunctions();	*/	/* These are currently never used */

	/* Initialize ScrInfo structure */
	strcpy (ScrInfo.PubScreenName, BaseName);

	/* Initialize PubPort name */
	strcpy (PubPortName, BaseName);

	/* Initialize PubPort name */
	strcpy (IconName, PrgName);


	/* Open required libraries */
	for (i = 0 ; openlibs[i].Base ; i++)
		if (!(*(openlibs[i].Base) = MyOpenLibrary (openlibs[i].Name, openlibs[i].Version)))
			if (openlibs[i].Version) return RETURN_FAIL;

#ifndef OS30_ONLY
	if (UtilityBase->lib_Version >= 39)
#endif	/* !OS30_ONLY */
		UniqueID = GetUniqueID();	/* Get ID for HelpGroup and other jobs */

	/* Get startup arguments */
	XMArgs.CxPopup = TRUE;

	if (WBenchMsg)
		err = GetWBArgs();
	else
		err = GetShellArgs();

	if (err) return err;

	SetupLocale();

	/* Create XModule library */
	if (err = MakeXModuleLibrary ())
		return err;

	Pool = XModuleBase->xm_Pool;


	/* Try to load XModule preferences */
	if (XMArgs.Settings)
	{
		if (LoadPrefs (XMArgs.Settings))
		{
			UBYTE buf[FAULT_MAX];

			Fault (IoErr(), NULL, buf, FAULT_MAX);
			ShowMessage (MSG_ERR_LOAD, XMArgs.Settings, buf);
		}
	}
	else
	{
		if (LoadPrefs ("PROGDIR:" PRGNAME ".prefs"))
			LoadPrefs ("ENV:" PRGNAME ".prefs");
	}


	/* Use startup Arguments */

	if (XMArgs.PubScreen)
		strncpy (ScrInfo.PubScreenName, XMArgs.PubScreen, 31);

	if (XMArgs.PortName)
		strncpy (PubPortName, XMArgs.PortName, 15);

	CxPopup = XMArgs.CxPopup;

	if (XMArgs.CxPopKey)
		strncpy (CxPopKey, XMArgs.CxPopKey, 31);

	if (XMArgs.CxPriority)
		CxPri = *XMArgs.CxPriority;

	if (XMArgs.IconX)
		IconX = *XMArgs.IconX;

	if (XMArgs.IconY)
		IconY = *XMArgs.IconY;

	if (XMArgs.IconName)
		strncpy (IconName, XMArgs.IconName, 15);

	/* Setup FileRequesters if LoadPrefs() hasn't already done it */
	if (!AslBase && !ReqToolsBase)
		if (err = SetupRequesters())
			return err;

	/* Setup App Message Port */
	SetupApp();

	/* Setup Rexx Host */
	CreateRexxPort();

	/* Setup Commodity object */
	SetupCx();

	/* Add internal hooks and load external ones */
	SetupHooks();

	/* Allocate a new SongInfo structure */
	if (!XModuleBase->xm_CurrentSong)
	{
		struct SongInfo *si;

		if (si = xmCreateSong (
			SNGA_ReadyToUse,	TRUE,
			XMSNG_AddToList,	-1,
			XMSNG_Active,		TRUE,
			TAG_DONE))
			ReleaseSemaphore (&si->Lock);
		else
			return ERROR_NO_FREE_STORE;
	}

	/* Open screen and ToolBox window */
	if (CxPopup)
		if (err = SetupScreen())
			return err;


	/* Load modules requested with Shell/Workbench startup arguments */

	HandleFrom();

	DisposeArgs();

	return 0;
}



static void Cleanup (LONG err)

/* Cleanup routine.  Display error message, free all resources & exit */
{
	ULONG i;

	if (err > 100)
		PrintFault (err, PrgName);

	/* Free all allocated resources */

	CleanupAudio();

	DisposeArgs();	/* Just to be sure */

	FreeFReq();
	CloseDownScreen();

	if (Pool)
	{
		FreeVecPooled (Pool, ScreenAttr.ta_Name);
		FreeVecPooled (Pool, WindowAttr.ta_Name);
		FreeVecPooled (Pool, ListAttr.ta_Name);
		FreeVecPooled (Pool, EditorAttr.ta_Name);
	}

	/* Dispose XModule library */
	DisposeXModuleLibrary ();

	/* Remove AppIcons/AppWindows Port */
	CleanupApp();

	/* Remove Commodity Broker */
	CleanupCx();

	/* Remove ARexx port */
	DeleteRexxPort();

	CleanupLocale();

	/* Close all libraries */
	for (i = 0 ; openlibs[i].Base ; i++)
		/* starting with V36, NULL is a valid parameter for CloseLibrary(). */
		CloseLibrary (*(openlibs[i].Base));
}



/*!*/
/* Emergency patch: */

LONG SampleWinTags[1] = { 0 };
void UpdateSampleMenu	(void) {}

