/* main.c
Originaly written by Anders Melchiorsen, modified by Roland Florac
SPrintf routine slightly modified (caused the CPU 040 060 to crash)
Modified from the original version for the ARexx interface */

#include "FetchRefs.h"
#include <libraries/locale.h>
#include <clib/locale_protos.h>
#include <pragmas/locale_pragmas.h>

static void __regargs LoadDataFragmented (BPTR file);
static __regargs void ReadWBArgs (struct WBArg * wb_arg);

#define VERSION_CATALOGUE 1

const UBYTE Vers_Tag[] = "$VER: FetchRefs " VERSION DATE;

struct Library * IntuitionBase;
struct Library * RexxSysBase, * LocaleBase;
APTR catalogue;

/* ReadArgs stuff */
UBYTE Template[] = "FILES/M,PORTNAME";
UBYTE CLI_Help[] = "\n"
	"FetchRefs [[FILES] <wildcard> [...]] [PORTNAME <name>]\n"
	"\n"
	"- FILES are index files generated by GenerateIndex\n"
	"- Default ARexx port name is FETCHREFS\n"
	"- Please read the guide for further information\n"
	"\n";

struct RDArgs *Args;
struct {
    STRPTR  (*Files)[];
    STRPTR  Portname;
} StartupArgs;

/* Other variables */
struct List FileList;
APTR FilePool;
long WorkBench = 0;	/* Signale que le programme a été démarré du WorkBench */

/* Localized strings */
char * texts[] = {
    "AutoDoc/include file look-up utility",
    "You need triton.library V%ld+!", "Error opening ARexx port",
    "There is a problem with this tool type:\n%s\n(%s [#%ld])",
    "Warning: no match for pattern '%s'", "Removing FetchRefs...",
    "FetchRefs " VERSION " by Anders Melchiorsen & Roland Florac", "Okay",
    "Select reference",
    "Search _pattern", "Fetch reference", "_List file", "Cancel",
    "Could not open guide", "Could not open amigaguide.library v34+",
    "FetchRefs: Reference not found", "FetchRefs: Aborted!"
};

char * __regargs GetString (long indice)
{   STRPTR def = texts[indice];
    return (catalogue ? GetCatalogStr (catalogue, indice, def) : def);
}

void ouverture_port_ARexx (void)
{   Forbid ();
    if (FindPort ("FETCHREFS") == 0)
	rexxPort = CreatePort ("FETCHREFS", 0);
    Permit ();
}

static void fermeture_port_ARexx (void)
{   if (rexxPort)
    {	RemPort (rexxPort);
	DeletePort (rexxPort);
	CloseLibrary (RexxSysBase);
    }
}

main (LONG argc, STRPTR argv)
{
    STRPTR port;
    struct WBStartup * argmsg;		struct WBArg * wb_arg;
    struct MsgPort *otherport;

    if (LocaleBase = OpenLibrary ("locale.library", 38))
	catalogue = OpenCatalog (NULL, "FetchRefs.catalog", OC_Version, VERSION_CATALOGUE, TAG_DONE);

    IntuitionBase = OldOpenLibrary ("intuition.library");
    if (IntuitionBase == 0)
	CloseAll (0);

    /* We need Triton */
    if (! TR_OpenTriton (TRITON14VERSION,
	    TRCA_Name,	    "FetchRefs",
	    TRCA_LongName,  "FetchRefs",
	    TRCA_Info,	    GetString (TEXTE_RECHERCHE),
	    TRCA_Version,   VERSION,
	    TRCA_Release,   RELEASE,
	    TRCA_Date,	    DATE,
	    TAG_END))
    {
	PostMessage (GetString(TEXTE_TRITON), TRITON14VERSION);
	CloseAll (0);
    }

    /* Initialize list */
    NewList (&FileList);

    if (!(FilePool = LibCreatePool (MEMF_CLEAR, 8 * 1024, 8 * 1024)))
	CloseAll (ERROR_NO_FREE_STORE);

    /* Allocate memory for argument parsing */
    if (!(Args = AllocDosObject (DOS_RDARGS, NULL)))
	CloseAll (ERROR_NO_FREE_STORE);

    /* Convert tool types to something that ReadArgs() understands if we are
     * run by Workbench.
     */
    if (argc == 0)
    {	argmsg = (struct WBStartup *) argv;
	wb_arg = argmsg->sm_ArgList ;
	WorkBench = 0;
	ReadWBArgs (wb_arg);
    }

    /* Activate extended help */
    Args->RDA_ExtHelp = CLI_Help;

    /* Parse arguments from either the Shell or Workbench */
    if (!(ReadArgs (Template, (LONG *)&StartupArgs, Args)))
	CloseAll (IoErr());

    /* Do not bother to start if the user pressed CTRL-C during load
     * time or during a 'FetchRefs ?' help session.
     */
    if (CheckSignal (SIGBREAKF_CTRL_C))
	CloseAll (0);

    /* Try to allocate an ARexx port. Default port name is FETCHREFS.
     * We quit both copies of FetchRefs if our port name is used.
     */
    port = StartupArgs.Portname;

    if (!port)
	port = "FETCHREFS";

    if (otherport = FindPort (port))
    {	Signal (otherport->mp_SigTask, SIGBREAKF_CTRL_C);

	CloseAll (ERROR_RUNTWICE);
    }

    /* Open ARexx port */
    ouverture_port_ARexx ();
    if (! rexxPort)
    {
	PostMessage (GetString (TEXTE_AREXX_PORT));
	CloseAll (0);
    }

    /* Expand each wildcard (which can also be just a file) and read refs */
    if (StartupArgs.Files)
	ReadWild (StartupArgs.Files);

    /* Install Commodities Broker */
    InstallCx (port);

    /* Activate ARexx handler and quit when it returns */
    MessageLoop ();
    CloseAll (0);
}

/* ReadWBArgs() - convert tool types to ReadArgs() string */
static __regargs void ReadWBArgs (struct WBArg * wb_arg)
{
    struct DiskObject *diskobj;
    STRPTR ToolTypeArgs;

    if (!(ToolTypeArgs = AllocVec (1024, NULL)))
	CloseAll (ERROR_NO_FREE_STORE);

    CurrentDir (wb_arg->wa_Lock);
    if (diskobj = GetDiskObject (wb_arg->wa_Name))
    {
	int err;

	if (err = ToolTypesToReadArgs (diskobj->do_ToolTypes, Template, ToolTypeArgs, 1))
	{
	    UBYTE errortxt[80];

	    Fault (err & 0xFFFF, NULL, errortxt, 80);

	    PostMessage(
		GetString (TEXTE_TOOLTYPE),
		diskobj->do_ToolTypes[err >> 16],
		errortxt,
		err & 0xFFFF);

	    FreeDiskObject (diskobj);
	    CloseAll (0);
	}

	Args->RDA_Source.CS_Buffer = ToolTypeArgs;
	Args->RDA_Source.CS_Length = strlen(ToolTypeArgs);
	Args->RDA_Flags = RDAF_NOPROMPT;

	FreeDiskObject (diskobj);
    }
}

/* ReadWild() - read all files that match argument
 * The argument is a pointer to an NULL terminated array of STRPTR (as
 * returned by ReadArgs("ARG/M"); ). */
void __regargs ReadWild (STRPTR (*patharrayptr)[])
{
    struct AnchorPath *fanchor;
    LONG cnt;
    STRPTR path;

    if (!(fanchor = AllocVec (sizeof(struct AnchorPath), MEMF_CLEAR)))
	return;

    for (cnt = 0; path = (*patharrayptr)[cnt]; cnt++)
    {
	if (MatchFirst (path, fanchor) == 0)
	{
	    do
	    {
		BPTR olddir;

		olddir = CurrentDir (fanchor->ap_Current->an_Lock);
		ReadRefs (fanchor->ap_Info.fib_FileName, fanchor->ap_Info.fib_Size);
		CurrentDir (olddir);
	    }
	    while (MatchNext(fanchor) == 0);
	}
	else
	    PostMessage (GetString (TEXTE_PATTERN), path);

	MatchEnd (fanchor);
    }
    FreeVec (fanchor);
}

/* ReadRefs() - Read all references in 'filename' (with length 'filesize') */
void __regargs ReadRefs (STRPTR filename, LONG filesize)
{
    BPTR file;

    /* First ensure that we can really open the file */
    if (!(file = Open (filename, MODE_OLDFILE)))
	return;

    /* If we cannot load the file fast then try the slower routine */
    if (!LoadData (file, filesize))
	LoadDataFragmented (file);

    Close (file);
}

/* LoadData() - fast loader using large memory chunk */
BOOL __regargs LoadData (BPTR file, LONG filesize)
{
    struct FileEntry *fileentry;
    char *gotto, *buffer;

    if (!(buffer = LibAllocPooled (FilePool, filesize)))
	return FALSE;

    Read (file, buffer, filesize);

    gotto = buffer;
    buffer += filesize;

    while (gotto < buffer)
    {
	LONG size;

	/* The size is saved in the .ln_Succ field of the Node */
	size = (LONG)(((struct Node *)gotto)->ln_Succ);

	if (((struct Node *) gotto)->ln_Type == 1)
	{
	    fileentry = (struct FileEntry *) gotto;
	    AddTail (&FileList, &fileentry->node);
	    NewList (&fileentry->RefsList);
	    fileentry->node.ln_Name = fileentry->Name;
	}
	else if (((struct Node *)gotto)->ln_Type == 2)
	{
	    struct RefsEntry *refsentry;
	    refsentry = (struct RefsEntry *) gotto;
	    AddTail (&fileentry->RefsList, &refsentry->node);
	    refsentry->node.ln_Name = fileentry->Name;
	}
	gotto += size;
    }

    return TRUE;
}

/* LoadDataFragmented() - Load into fragmented memory. A bit slower. */
static void __regargs LoadDataFragmented (BPTR file)
{
    char *buffer;
    struct FileEntry *fileentry;
    struct Node tmpnode;

    while (Read (file, &tmpnode, sizeof(struct Node)))
    {
	/* The size of the node is saved in the .ln_Succ filed */
	if (!(buffer = LibAllocPooled (FilePool, (LONG)tmpnode.ln_Succ)))
	    break;

	/* Fill in the node with information from the file */
	// fread (buffer + sizeof(struct Node), (LONG)tmpnode.ln_Succ - sizeof(struct Node), 1, file);
	Read (file, buffer + sizeof(struct Node), (LONG)tmpnode.ln_Succ - sizeof(struct Node));

	if (tmpnode.ln_Type == 1)
	{
	    /* A new file starts here */
	    fileentry = ((struct FileEntry *)buffer);

	    AddTail (&FileList, &fileentry->node);
	    NewList (&fileentry->RefsList);
	    fileentry->node.ln_Name = fileentry->Name;
	}
	else if (tmpnode.ln_Type == 2)
	{
	    struct RefsEntry *refsentry = ((struct RefsEntry *)buffer);

	    /* Another reference in the current file */
	    AddTail (&fileentry->RefsList, &refsentry->node);
	    refsentry->node.ln_Name = fileentry->Name;
	}
    }
}

/* FreeRefs() - Release all files from memory and re-initialize list */
void FreeRefs ()
{
    NewList (&FileList);
    LibDeletePool (FilePool);

    if (!(FilePool = LibCreatePool (MEMF_CLEAR, 8 * 1024, 8 * 1024)))
	CloseAll (ERROR_NO_FREE_STORE);
}

struct TagItem TellWeStopOutline[] =
{
    WindowID (0xC0DEDBAD),
	WindowPosition (TRWP_CENTERDISPLAY),
	WindowTitle ("FetchRefs " VERSION),
	WindowBackfillReq,
	WindowFlags (TRWF_NOACTIVATE|TRWF_NOESCCLOSE|TRWF_NOSIZEGADGET|TRWF_NOZIPGADGET|TRWF_NOCLOSEGADGET),

    HorizGroupA,
	Space,
	VertGroupA,
	    Space,
	    GroupBox, ObjectBackfillB,
	    HorizGroupA,
		Space,
		VertGroupA,
		    Space,
		    TextN (0),
		    Space,
		EndGroup,
		Space,
	    EndGroup,
	    Space,
	EndGroup,
	Space,
    EndGroup,

EndProject
};

/****** triton.lib/TR_CloseTriton ******
*   NAME
*	TR_CloseTriton -- Closes Triton easily.
*
*   SYNOPSIS
*	TR_CloseTriton()
*
*	VOID TR_CloseTriton(VOID);
*
*   FUNCTION
*	Closes the application created by OpenTriton()
*	and closes triton.library.
*
*   SEE ALSO
*	TR_OpenTriton()
******/

REGS void TR_CloseTriton (void)
{
    if (__Triton_Support_App)
    {
	TR_DeleteApp (__Triton_Support_App);
	__Triton_Support_App = NULL;
    }

    if (TritonBase)
    {
	CloseLibrary (TritonBase);
	TritonBase = NULL;
    }
}

/* CloseAll() - Clean up and possibly print an error report */
void CloseAll (LONG error, ...)
{
    LibDeletePool (FilePool);

    if (WorkBench)
	FreeVec (Args->RDA_Source.CS_Buffer);

    if (Args)
    {
	FreeArgs (Args);
	FreeDosObject (DOS_RDARGS, Args);
    }

    if (error)
    {
	/* Print an error message, either standard or custom... */

	if (error > ERROR_SPECIALMAX)
	{
	    UBYTE errtxt[80];

	    Fault (error, "FetchRefs", errtxt, 80);
	    PostMessage (errtxt);
	}
	else if (error != ERROR_RUNTWICE)
	{
	    va_list args;

	    va_start (args, error);
	    PostMessage (*(STRPTR *)args);
	    va_end (args);
	}
    }
    else
    {
	/* Tell that we are stopping by popping a window for a second */

	struct TR_Project *stopprj;
	TellWeStopOutline[16].ti_Data = (ULONG) GetString (TEXTE_REMOVE);
	if (stopprj = TR_OpenProject (Application, TellWeStopOutline))
	{
	    Delay (TICKS_PER_SECOND);
	    TR_CloseProject (stopprj);
	}
    }

    if (IntuitionBase)
	CloseLibrary (IntuitionBase);

    if (LocaleBase)
    {	CloseCatalog (catalogue);
	CloseLibrary (LocaleBase);
    }

    TR_CloseTriton();
    RemoveCx();
    fermeture_port_ARexx();
    _exit((error) && (error != ERROR_RUNTWICE)  ? 5 : 0);
}

/* PostMessage() - printf() like requester function. Uses Shell if available */
void PostMessage (STRPTR fmt, ...)
{
    static struct EasyStruct msgreq = {
	sizeof(struct EasyStruct),
	0,
	0,
	NULL,
	0
    };

    va_list args;

    msgreq.es_Title = GetString (TEXTE_VERSION);
    msgreq.es_GadgetFormat = GetString (TEXTE_OK);
    msgreq.es_TextFormat = fmt;

    va_start (args, fmt);

    if (WorkBench)
	EasyRequestArgs (NULL, &msgreq, NULL, args);
    else
    {
	VPrintf (fmt, args);
	PutStr ("\n");
    }

    va_end (args);
}

/* SPrintf() - Low-cost sprintf routine using RawDoFmt()-formatting */
void SPrintf (UBYTE *buf, UBYTE *fmt, ...)
{
    /* The "\x16\xC0\x4E\x75" part is really code that gets executed
     * for each character output:
     *	move.b	d0,(a3)+
     *	rts
     * DATA (move_rts) MUST BE STATIC DO BE USED WITH CACHE PROCESSORS !
     */
    static __aligned char move_rts[4] = { '\x16', '\xC0', '\x4E', '\x75' };
    va_list args;

    va_start (args, fmt);
    RawDoFmt (fmt, args, (void *)move_rts, buf);
    va_end (args);
}

/* Triton support functions */
struct Library *TritonBase;
struct TR_App *__Triton_Support_App;

/****** triton.lib/TR_OpenTriton ******
*   NAME
*	TR_OpenTriton -- Opens Triton ready to use.
*
*   SYNOPSIS
*	success = TR_OpenTriton(version, tag1,...)
*	D0
*
*	BOOL TR_OpenTriton(ULONG, ULONG,...);
*
*   FUNCTION
*	Opens triton.library with the specified minimum
*	version and creates an application.
*	The supplied tags are passed as a taglist to
*	TR_CreateApp().
*
*   RESULT
*	success - Was everything opened successful?
*
*   SEE ALSO
*	TR_CloseTriton(), TR_CreateApp()
******/

BOOL TR_OpenTriton (ULONG version, ULONG taglist,...)
{
    if (!(TritonBase = OpenLibrary (TRITONNAME, version)))
	return FALSE;
    if (!(__Triton_Support_App = TR_CreateApp ((struct TagItem *)&taglist)))
	return FALSE;

    return TRUE;
}

/* Triton stack arguments stubs */
struct TR_Project * TR_OpenProjectTags (struct TR_App *app, ULONG taglist, ...)
{
    return TR_OpenProject (app, (struct TagItem *)&taglist);
}

ULONG TR_EasyRequestTags (struct TR_App *app, STRPTR bodyfmt, STRPTR gadfmt, ULONG taglist, ...)
{
    return TR_EasyRequest (app, bodyfmt, gadfmt, (struct TagItem *)&taglist);
}

ULONG TR_AutoRequestTags (struct TR_App *app, struct TR_Project *lockproject, ULONG taglist, ...)
{
    return TR_AutoRequest (app, lockproject, (struct TagItem *)&taglist);
}
