/* -----------------------------------------------------------------------------

 TeX parser ©1996 BURGHARD Eric
    ScanLib ©1996 Dietmar Eiler

 GoldED syntax parser library (uses a syntax cache).
 Highlight TeX instructions, comments, arguments, options, spell errors

 Main code
 -------------------------------------------------------------------------------
*/

/// "Includes"

#include "defs.h"
#define CATCOMP_NUMBERS
#include "locale.h"
#include "funcs.h"
#include "private.h"
#include "debug.h"

///
/// "Definitions"

// Set by tag.a
struct Library *SysBase, *DOSBase, *LocaleBase, *RexxSysBase, *LibBase;
BPTR SegList;
struct LocaleInfo li;

///
/// "Library functions"

/* ------------------------------- MountScanner --------------------------------

 Called by the editor before first usage of a scanner. Return a description of
 our abilities.

*/

LibCall struct ParserData *
MountScanner(void)
{
    static struct ParserData parserData;

    // syntax elements understood by parser
    static UBYTE *levelNames[CHKNBR+1];

    static UBYTE *example[] = { "\\documentclass[10pt]{article}",
                                "\\def\\foo=\"8A",
                                "\\def\\dim=6,3 cm",
                                "\\let\\bar='74 %useless comment",
                                "\\def\\mx2=$x^2$",
                                "\\def\\un=$U_n$",
                                "\\def\\catcode\\'\\=1",
                                "\\def\\pr@tected#1=#1\\relax",
                                "\\begin{document}",
                                "{\\large\\sl GoldED~Editor\\\\is powerfull !}",
                                "\\end{document}",
                                NULL};

    // color suggestions

    static ULONG levelColors[CHKNBR] = {
        MAKE_RGB4(0,  0,  0),
        MAKE_RGB4(0,  0,  0),
        MAKE_RGB4(15, 15, 0),
        MAKE_RGB4(15, 15, 0),
        MAKE_RGB4(0,  0,  15),
        MAKE_RGB4(0, 15, 0),
        MAKE_RGB4(0, 15, 15),
        MAKE_RGB4(0, 15, 15),
        MAKE_RGB4(15, 0, 0),
        MAKE_RGB4(15, 0, 15),
        MAKE_RGB4(15, 15, 15),
        MAKE_RGB4(0, 0, 15),
        MAKE_RGB4(0, 0, 15),
        #ifdef ISPELL
        MAKE_RGB4(0, 0, 15)
        #endif
    };

    #ifdef DEBUG
      DbgPrintf("MountScanner()\n");
    #endif

    if (LocaleBase)

        li.li_Catalog = OpenCatalogA(NULL,"envtex.catalog",NULL);

    levelNames[STDT]=GetString(&li, MSG_STDTX);
    levelNames[INTL]=GetString(&li, MSG_INSTL);
    levelNames[INTS]=GetString(&li, MSG_INSTS);
    levelNames[INTH]=GetString(&li, MSG_INSTH);
    levelNames[GRPE]=GetString(&li, MSG_GROUP);
    levelNames[OPTI]=GetString(&li, MSG_OPTOS);
    levelNames[NBRE]=GetString(&li, MSG_NUMBR);
    levelNames[DIME]=GetString(&li, MSG_DIMEN);
    levelNames[COMM]=GetString(&li, MSG_COMMT);
    levelNames[MATH]=GetString(&li, MSG_MATHM);
    levelNames[MTHS]=GetString(&li, MSG_MATHS);
    levelNames[INSC]=GetString(&li, MSG_INSCR);
    levelNames[MACA]=GetString(&li, MSG_MCARG);
    #ifdef ISPELL
    levelNames[ISPE]=GetString(&li, MSG_ISPEL);
    #endif
    levelNames[CHKNBR]=NULL;

    parserData.pd_Release  = SCANLIBVERSION;
    parserData.pd_Version  = 1;
    parserData.pd_Serial   = 0;
    parserData.pd_Info     = GetString(&li, MSG_PARSR);
    parserData.pd_Levels   = CHKNBR;
    parserData.pd_Names    = levelNames;
    parserData.pd_Colors   = levelColors;
    parserData.pd_Flags    = SCPRF_SYNTAXCACHE;
    parserData.pd_Example  = example;

    return(&parserData);
}


/* ------------------------------- StartScanner --------------------------------

 Called by the editor after a new text buffer has been created. We allocate a
 buffer to hold text-specific data. The buffer address is returned as handle.

*/

LibCall ULONG
StartScanner(register __a0 struct GlobalConfig *globalConfigPtr, register __a1 struct EditConfig *editConfigPtr, register __d0 struct SyntaxChunk *syntaxStack)
{
    struct BufferHandle *handle;
    #ifdef ISPELL
    struct MsgPort *replyPort;
    #endif

    #ifdef DEBUG
      DbgPrintf("StartScanner(globalConfiPtr=%08x, editConfiPtr=%08x, syntaxStack=%08x)\n", globalConfigPtr, editConfigPtr, syntaxStack);
    #endif

    if (!(handle = AllocVec(sizeof(struct BufferHandle), MEMF_PUBLIC | MEMF_CLEAR)))

        return(NULL);

    #ifdef ISPELL
    if (!(replyPort=CreateMsgPort())) {

        FreeVec(handle);
        return(NULL);
    }
    #endif

    handle->bh_GlobalConfig = globalConfigPtr;
    handle->bh_EditConfig   = editConfigPtr;
    handle->bh_SyntaxStack  = syntaxStack;
    #ifdef ISPELL
    handle->bh_ReplyPort    = replyPort;
    if (FindPort("IRexxSpell"))
       handle->bh_ISpellCheck=TRUE;
    else
       handle->bh_ISpellCheck=FALSE;
    #endif

    return((ULONG)handle);
}


/* ------------------------------- CloseScanner --------------------------------

 Called by the editor if a text buffer is about to be closed. Deallocate buffer
 specific 'global' data.

*/

LibCall ULONG
CloseScanner(register __d0 ULONG scanID)
{
    #ifdef DEBUG
      DbgPrintf("CloseScanner(scanID=%d)\n", scanID);
    #endif

    if (li.li_Catalog)

        CloseCatalog(li.li_Catalog);

    if (scanID) {

        struct BufferHandle *handle = (struct BufferHandle *)scanID;
        #ifdef ISPELL
        DeleteMsgPort(handle->bh_ReplyPort);
        #endif
        FreeVec(handle);
    }

    return(0);
}


/* ------------------------------- FlushScanner --------------------------------

 Called by the editor in low memory situations: we are supposed to free as much
 memory as possible.

*/

LibCall void
FlushScanner(register __d0 ULONG scanID)
{
    struct BufferHandle *handle;
    struct EditConfig   *config;
    struct LineNode     *lineNode;

    #ifdef DEBUG
      DbgPrintf("FlushtScanner(scanID=%d)\n", scanID);
    #endif

    handle = (struct BufferHandle *)scanID;
    config = handle->bh_EditConfig;

    if (lineNode = config->TextNodes)
        UnparseLines(lineNode, config->Lines);
}


/* ------------------------------- SetupScanner --------------------------------

 Called by the editor if the user wants to change the scanner's configuration.
 We do not support user configuration (parserData.pd_Flags: SCPRF_CONFIGWIN flag
 unset).

*/

LibCall void
SetupScanner(register __a0 struct GlobalConfig *globalConfigPtr)
{
    #ifdef DEBUG
      DbgPrintf("SetupScanner(globalConfiPtr=%08x)\n", globalConfigPtr);
    #endif
}


/* ------------------------------- BriefScanner --------------------------------

 Called to notify a context scanner if lines have been added, deleted or
 modified. We aren't a context scanner (parserData.pd_Flags: SCPRF_CONTEXT
 flag unset), so we won't ever have to request additional display requests:
 the editor's built-in refresh of damage regions is sufficient.

*/

LibCall struct RefreshRequest *
BriefScanner(register __d0 ULONG scanID, register __a0 struct ScannerNotify *notify)
{
    #ifdef DEBUG
      DbgPrintf("BriefScanner(scanID=%d, notify=%08x)\n", scanID, notify);
    #endif

    return(NULL);
}


/* --------------------------------- ParseLine ---------------------------------

 Parse a line, build a syntax description

*/

LibCall struct SyntaxChunk *
ParseLine(register __d0 ULONG scanID, register __a0 struct LineNode *lineNode, register __d1 ULONG line)
{
    #ifdef DEBUG
      DbgPrintf("StartScanner(scanID=%d, lineNode=%08x, line=%d)\n", scanID, lineNode, line);
    #endif

    if (IS_FOLD(lineNode))

        return(NULL);

    else if (lineNode->Len) {

        // line not yet parsed ?

        if (lineNode->UserData == NULL) {

            struct SyntaxChunk *syntaxStack = ParseString(lineNode->Text, lineNode->Len, scanID);

            if (syntaxStack == EMPTY_STACK)
                lineNode->UserData = EMPTY_STACK;
            else
                lineNode->UserData = DupStack(syntaxStack);
        }

        if (lineNode->UserData == EMPTY_STACK)
            return((struct SyntaxChunk *)NULL);
        else
            return((struct SyntaxChunk *)lineNode->UserData);
    }
    else
        return(NULL);
}

/* -------------------------------- UnparseLines -------------------------------

 Called by the editor if lines are to be deleted. We are supposed to free
 private data attached to the lines.

*/

LibCall void
UnparseLines(register __a0  struct LineNode *lineNode, register __d0 ULONG lines)
{
    #ifdef DEBUG
      DbgPrintf("UnparseLines(lineNode=%08x, lines=%d)\n", lineNode, lines);
    #endif

    while (lines--) {

        // free syntax cache

        if (lineNode->UserData) {

            if (lineNode->UserData != (APTR)EMPTY_STACK)
                FreeVec((APTR)lineNode->UserData);

            lineNode->UserData = NULL;
        }

        // free folded subblock

        if (IS_FOLD(lineNode)) {

            struct Fold *fold = (struct Fold *)lineNode->SpecialInfo;

            UnparseLines(fold->TextNodes, fold->Lines);
        }

        ++lineNode;
    }
}

/* -------------------------------- ParseSection -------------------------------

 Called by the editor if lines are to be displayed. The scanner is encouraged to
 preparse the lines.

*/

LibCall void
ParseSection(register __d0 ULONG scanID, register __a0  struct LineNode *lineNode, register __d1 ULONG lines)
{
    #ifdef DEBUG
      DbgPrintf("ParseSection(scanID=%d, lineNode=%08x, lines=%d)\n", scanID, lineNode, lines);
    #endif

    while (lines--) {

        // fold headers have to be ignored

        if (IS_FOLD(lineNode) == FALSE) {

            // line not yet parsed ?

            if (lineNode->Len)
                if (lineNode->UserData == NULL)
                    lineNode->UserData = DupStack(ParseString(lineNode->Text, lineNode->Len, scanID));
        }

        ++lineNode;
    }
}

///
/// "ARexx functions"
/* ---------------------------------- SendRexxCommand -------------------------
 Send ARexx message & wait for answer. Return pointer to result or NULL.
*/
#ifdef ISPELL
ULONG *
SendRexxCommand(port, cmd, replyPort, result2)

struct MsgPort *replyPort;
UBYTE          *cmd, *port, *result2;
{
    struct MsgPort *rexxport;

    #ifdef DEBUG
       DbgPrintf("SendRexxCommand(port=%08x, cmd=\"%s\", replyPort=%08x, result2=\"%s\")\n", port, cmd, replyPort, result2);
    #endif

    Forbid();

    if (rexxport = FindPort(port)) {

        struct RexxMsg *rexxMsg, *answer;

        if (rexxMsg = CreateRexxMsg(replyPort, NULL, NULL)) {

            if (rexxMsg->rm_Args[0] = CreateArgstring(cmd, strlen(cmd))) {

                static ULONG result;

                rexxMsg->rm_Action = RXCOMM | RXFF_RESULT;

                PutMsg(rexxport, &rexxMsg->rm_Node);

                do {

                    WaitPort(replyPort);

                    if (answer = (struct RexxMsg *)GetMsg(replyPort))

                        result = answer->rm_Result1;

                } while (!answer);

                Permit();

                if (answer->rm_Result1 == RC_OK)

                    if (answer->rm_Result2) {

                        #ifdef DEBUG
                            DbgPrintf("rm_Result2=\"%s\"\n", answer->rm_Result2);
                        #endif

                        if (result2)

                            strcpy((char *)result2, (char *)answer->rm_Result2);

                        DeleteArgstring((UBYTE *)answer->rm_Result2);

                    }

                DeleteArgstring((UBYTE *)ARG0(answer));

                DeleteRexxMsg(answer);

                return(&result);
            }
        }
    }

    Permit();

    return(NULL);
}
#endif
///
