/*
    module: smurf.cc - real global data and misc Genesys routines in ORCA/C.
    author: Marc Wolfgram
      date: 19Jun89
   revised: 24Jul89 - Repentant effort to apply list and control manager rules
            27Jul89 - Implemented kill-n-rebuild mentality. 
            30Jul89 - Added selection algorithm to replace SelectMember2 calls
            04Nov89 - Changed SMURF for EditResult2, added EMN2 MenuPtr table
           (15Nov89 - ICG) added NullUpdate command for rapid button events.
            18Nov89 - Replaced GetItem and GetType with versions using
                      get_name() calls [engine.cc]
            27Nov89 - Added generalized version of GetWRefCon... RefConSetup()
            28Nov89 - Added complete ICG.CC: update() UserCancel() ThermoBar()
            03Dec89 - Added additional EADR and ENM2 entry for generic editor
                      also GEDloaded is negative it has been loaded.
            14Dec89 - Fixed RefConSetup() desktop window bug.
            10Jan90 - Removed ICG code, added CenterWin (optimized)
            28Jan90 - Added code to save and restore the grafport data around
                      list calls.
            16Mar90 - Added ResourceFileID at SMURF+0x4e.
            20Mar90 - Added PaulElseth() at SMURF.PaulEProc.
        B5  09Apr90 - Added get_name2() call [rname.cc]
        B7          - get_name() generalized to support resnames.
        B8  28Apr90 - path prefixes set to extended formats.
    1.01    30May90 - All sorts of sugar and spice - see shell.asm rev history
    1.02    08Jun90 - Added EUID array for handling editor Application ID's
    1.1b1   31Jul90 - Added resname support code (saveName and in rebuildItem)
    1.1c    31Aug90 - Converted PaulEProc to SMURFvector.  Added font hell.
    2.0a8   12Nov90 - Prefix changed from 3, 5 and 6 to 14, 15 and 16.
                      Added OldAntMarch to allow interrum support while adding
                      EOS deviation.
    2.0a10  30Nov90 - Replaced resname pstrcmp with new pstrucmp.
*/
#include "genesys.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <control.h>
#include <list.h>
#include <font.h>
#include <gsos.h>
#include <memory.h>
#include <misctool.h>
#include <qdaux.h>
#include <resources.h>
#include <window.h>
#include <usrlib.h>
#include "smurf.h"

extern  int     EndResAccess(long);
extern  long    SetResAccess(int, int);
extern  long    DrawCoords;
extern  long    OldAntMarch;
extern  long    PaulElseth;
extern  int     get_label_status;
extern  long    idiotWin;

Smurf SMURF;

/*** VARIABLE DECLARATIONS ***************************************************/

char
    aParams[3][TMAX],
    ETXT[EMAX][TMAX],
   *TypeStr,               /* str for the types list strings */
   *ItemStr,               /* str for the items list strings */
    EADD[] = "\p Add ";    /* Add pop up string */

int
    MenuHeight,             /* returned height of menus */
    QuitFlag,               /* 0 = no quit */
    MasterID,               /* userID for tool startup/shutdown */
    ListsID,                /* user ID for list memory */
    EUID[EMAX+1],           /* editor userIDs */
    EditError,              /* editor error variable */
    WindType,               /* type of window */
    ResFileID,              /* ID returned from OpenResourceFile */
    GenFileID,              /* ID of the shell's resource fork */
    GenDataID,              /* ID of the shell's auxillary resource file */
    NewFlag,                /* flags if new file or existing file */
    GEDloaded,              /* negative if a generic editor is loaded */
    SubEditActive,          /* negative if a sub edit window is in front */
    ClipCount,
    ImportDefType,
    ChangedFlag,            /* these resulted from SMURF 2 modifiers demise */
    RHidingFlag,
    CoordOnFlag,
    XHairOnFlag,
    NewFileFlag;

word
    ECNT,                   /* number of loaded editors */
    EMID[EMAX],             /* editor menu IDs for pop up */
    ERES[EMAX],             /* editor resource types */
    ListType,               /* displayed res type in types list */
    ListTnum,               /* number of items in types list */
    NewSubType,
    ActSubType;

long
    taskORmask,
    taskANDmask,
    Params[3],
    EADR[EMAX+1],           /* addresses of loaded dedicated editors */
    ENM2[EMAX+1],           /* addresses of dedicated editor's NewMenu2 */
    ListIrID,               /* holds items list res ID */
    ListInum,               /* holds number of items in items list */
    NewSubID,
    NewSubOwner,
    ActSubID,
    RootWPtr,               /* ptr to the main root window */
    ClipWPtr;

struct Eitem {                  /* Edit pop up */
    int a, ID, b, c, d;
    long Tptr;
} EMIT[EMAX];

struct Emenu {                  /* Add pop up */
    int a, b, c;
    char *text;
    long EMTB[EMAX];
} EMEN = {0, 300, 0, EADD};

struct tCtlRec **TypeHandle;
struct iCtlRec **ItemHandle;

/*****************************************************************************
 * Type and Item control records
 *****************************************************************************/

struct tList TypeList = {15,           /* number of fields in the record   */
                         3,            /* application assigned ID          */
                         17,10,79,210, /* bounding rect for control        */
                         0x89000000,   /* new list control defproc         */
                         0,            /* visible flag                     */
                         0x1000,       /* control proc not ptr (MUST SET)  */
                         0L,           /* refCon                           */
         /* set later */ 0,            /* number of members in list        */
         /*  derived  */ 0,            /* number members seen at one time  */
                         2,            /* single select pascal strings     */
         /* set later */ 0,            /* list start                       */
                         0,            /* member drawing routine, default  */
                         10,           /* height of each member in pixels  */
                         7,            /* num bytes each member uses       */
         /* set later */ 0,            /* ref to list of member records    */
                         0};

struct iList ItemList = {15,           /* see paramters above for types    */
                         4,
                         17,245,79,445,
                         0x89000000,
                         0,
                         0x1000,
                         0L,
                         0,
                         0,
                         2,
                         0,
                         0,
                         10,
                         11,
                         0,
                         0};

/*****************************************************************************
 * Genesys path definitions
 *****************************************************************************/

struct gsos_str EditDir = { 10, "@:Gen.Edit"};          /* 6:editors folder  */
struct gsos_str LangDir = { 10, "@:Gen.Lang"};          /* 5:template folder */
struct gsos_str LangDsk = { 19, ":Genesys.2:Gen.Lang"}; /* 5 (template disk) */
struct gsos_str WorkDir = { 10, "@:Gen.Work"};          /* 4:work folder     */
struct gsos_str Scratch = { 13, "4:ScratchFile"};      /*    work path       */
struct gsos_str DefFile = { 13, "4:DefaultFile"};      /*    work path       */
struct gsos_str GenData = { 10, "@:Gen.Data"};          /*    data file       */

/*****************************************************************************
 * function prototypes employed here
 *****************************************************************************/

int         AddItem(word, long);
void        cSetUp(void);
int         DelItem(word, long);
char       *get_label(word, long, int);
char       *get_name(word, int);
int         InitTypeList(void);
void        KillList(void);
void        rebuildItem(word, word);
void        rebuildType(void);
int         RefConSetup(GrafPortPtr fwPtr);
void        refreshItem(long);
void        refreshType(word);
char       *rNameBuild(char *,long,char *);
void        shError(int, int);
int         saveNames(word);
int         toolerror(void);
void        updateList(int);


struct gsos_pfx pDir;
struct minFinfo pInf;

/*****************************************************************************
 * cSetUp initializes the fixed data in the smurf record and pre-builds the
 * edit table arrays.
 *****************************************************************************/

void cSetUp(void)
{
    int i;
    long PEtemp;
    GrafPortPtr deskTemp, idiotTemp;

    pDir.pCount = pInf.pCount = 2;          /* set standard prefix... */
    pDir.prefix = 4;
    pDir.path = &WorkDir;
    SetPrefixGS(&pDir);
    shError(toolerror(), 0xB002);
    pDir.prefix = 5;
    pInf.path = &LangDir;
    GetFileInfoGS(&pInf);                   /* check if folder or disk */
    if (toolerror() == 0)
        pDir.path = &LangDir;
    else
        pDir.path = &LangDsk;
    SetPrefixGS(&pDir);
    shError(toolerror(), 0xB004);
    pDir.prefix = 6;
    pDir.path = &EditDir;
    SetPrefixGS(&pDir);
    shError(toolerror(), 0xB006);

    SubEditActive = 0;
    NewSubType = 0;
    NewSubID = 0L;
    RootWPtr = 0L;
    ClipWPtr = 0L;
    ImportDefType = 1;

    Params[0] = (long) &aParams[0];
    Params[1] = (long) &aParams[1];
    Params[2] = (long) &aParams[2];

    for (i = 0; i < EMAX; i++) {               /* init the editor tables */
        EMEN.EMTB[i] = (long) &EMIT[i];
        EMIT[i].a = EMIT[i].b = EMIT[i].c = EMIT[i].d = EUID[i] = 0;
        EMIT[i].ID = 0x1000 + i;
        EMIT[i].Tptr = (long) ETXT[i];
    }
    GEDloaded = 0;
    ListTnum = 0;
    ListInum = 0L;

    SMURF.CoordProc = &DrawCoords;

    SMURF.MarchProc = &OldAntMarch;

    SMURF.SMURFvector = (long) &PaulElseth;

}

/*****************************************************************************
 * InitTypeList is the front end which builds and displays the lists.
 *****************************************************************************/
int InitTypeList(void)
{
    rebuildType();
    refreshType(0);
    rebuildItem(ListType, -1);
    refreshItem(0L);
    return 0;
}

/*****************************************************************************
 * rebuildType and rebuildItem construct the type and item list data from the
 * current resource file.
 *****************************************************************************/
void rebuildType(void)  /* no inputs or outputs */
{
    char *sPtr;
    int i, rType, oType;
    long memSize, rav;
    handle memHand;

    WaitCursor();

    rav = SetResAccess(ResFileID, 1);
    
    if (ListTnum) {
        for (i = 0, oType = 0L; i < ListInum; i++)
            if ((**TypeHandle).ctlList[i].flag == 0x80)
                oType = (**TypeHandle).ctlList[i].rType;
        ufree((long) TypeStr);
        ufree((long) (**TypeHandle).ctlList); 
        (**TypeHandle).ctlList = 0L;
    }
     
    if (ListTnum = CountTypes()) {

        memSize = (long) ListTnum * TMAXL;
        memHand = NewHandle(memSize, ListsID, 0xc018, 0L);
        shError(toolerror(), 0xB307);
        HLock(memHand);
        TypeStr = (char *) *memHand;

        memSize = (long) ListTnum * 7L;
        memHand = NewHandle(memSize, ListsID, 0xc018, 0L);
        shError(toolerror(), 0xB309);
        HLock(memHand);
        (**TypeHandle).ctlList = (struct tMember *) *memHand;

        (**TypeHandle).ctlData = ListTnum;

        sPtr = TypeStr;

        for (i = 0; i < ListTnum; i++) {
            rType = GetIndType(i + 1);

            /* fill member records: |0  1  2  3 |4 |5  6 |
                                    |  str_ptr  |fl|rtype| */
            (**TypeHandle).ctlList[i].text = sPtr;
            (**TypeHandle).ctlList[i].flag = 0x00;
            (**TypeHandle).ctlList[i].rType = rType;
            if (oType && (**TypeHandle).ctlList[i].rType == oType)
                (**TypeHandle).ctlList[i].flag = 0x80;

            /* fill strings */
            pstrcpy(sPtr, c2pstr(get_name(rType, 0)));
            sPtr += TMAXL;
        }
    }

    else { /* There aren't any resources in the file */
        if ((**TypeHandle).ctlList) {
            ufree((long) (**TypeHandle).ctlList);
            ufree((long) TypeStr);
        }
        (**TypeHandle).ctlList = 0L;
        (**TypeHandle).ctlData = (**TypeHandle).ctlValue = 0;
        ListTnum = 0;
        ListInum = 0L;
    }
    EndResAccess(rav);

    InitCursor();
}

void rebuildItem(word rType, word dupChk)
{
    char *sPtr, *tPtr;
    int rDepth, i, j, result, nameHit;
    long rID, oID, memSize, rav;
    handle memHand;

    WaitCursor();
    
    rav = SetResAccess(ResFileID, 1);

    if (ListInum) {
        for (i = 0, oID = 0L; i < ListInum; i++)
            if ((**ItemHandle).ctlList[i].flag == 0x80)
                oID = (**ItemHandle).ctlList[i].rID;
        ufree((long) ItemStr);
        ufree((long) (**ItemHandle).ctlList); 
        (**ItemHandle).ctlList = 0L;
    }

    if (rType && (ListInum = CountResources(rType))) {

        memSize = (long) ListInum * IMAXL;
        memHand = NewHandle(memSize, ListsID, 0xc018, 0L);
        shError(toolerror(), 0xB407);
        HLock(memHand);
        ItemStr = (char *) *memHand;

        memSize = (long) ListInum * 11L;
        memHand = NewHandle(memSize, ListsID, 0xc018, 0L);
        shError(toolerror(), 0xB409);
        HLock(memHand);
        (**ItemHandle).ctlList = (struct iMember *) *memHand;

        (**ItemHandle).ctlData = (int) ListInum;

        sPtr = ItemStr;
        nameHit = 0;

        for (i = 0; i < (int) ListInum; i++) {
            rID = GetIndResource(rType, (long) i+1);

            /* fill member records: |0  1  2  3 |4 |5  6  7  8 |
                                    |  str_ptr  |fl|    rID    | */
            (**ItemHandle).ctlList[i].text = sPtr;
            (**ItemHandle).ctlList[i].flag = 0x00;
            (**ItemHandle).ctlList[i].rID = rID;
            if (oID && (**ItemHandle).ctlList[i].rID == oID)
                (**ItemHandle).ctlList[i].flag = 0x80;

            /* fill strings */
            tPtr = c2pstr(get_label(rType, rID, 0));
            (**ItemHandle).ctlList[i].alias = get_label_status;
            if (*tPtr >= IMAX)
                *tPtr = (IMAX - 1);
            pstrcpy(sPtr, tPtr);
            sPtr += IMAXL;

            /* N O T E - The inclusion of the get_label_status test and the
             subsequent alias/continue causes the shortcut of tests so that
             genesys "created" names are not tested at all.  This will be a
             problem only if some dweeb names a resource the same as one of
             our names. Marc 1.1b3 21Aug90 */

            if (dupChk != 0 && get_label_status != 0) { /* check dup names */
                for (j = 0; i != 0 && i > j; j++) {
                    if ((**ItemHandle).ctlList[j].alias == 0)
                        continue;

                    if (pstrucmp((**ItemHandle).ctlList[i].text,
                                (**ItemHandle).ctlList[j].text) == 0) {

                    /* set up params */
                        strcpy(aParams[0], get_name(rType, 0));
                        strcpy(aParams[1], p2cstr((**ItemHandle).ctlList[i].text));
                        sprintf(aParams[2], "%s_%0.8lX", get_name(rType, 1),
                            (**ItemHandle).ctlList[i].rID);

                    /* reduce duplicate name to Genesys alias */
                        pstrcpy((**ItemHandle).ctlList[i].text,
                                c2pstr(aParams[2]));
                        (**ItemHandle).ctlList[i].alias = 0;
                        nameHit++;

                    /* make sure we access the right alert - pffffst! */
                        SetResAccess(GenFileID, -1);
                        InitCursor();
                        result = AlertWindow(4, Params, 17L);
                        WaitCursor();
                        SetResAccess(ResFileID, 1);
                    }
                }
            }
        }
        if (nameHit != 0)   /* rewrite the rName resource */
            saveNames(rType);

    }

    else { /* We have no items */
        if ((**ItemHandle).ctlList) {
            ufree((long) (**ItemHandle).ctlList);
            ufree((long) ItemStr);
        }
        (**ItemHandle).ctlList = 0L;;
        (**ItemHandle).ctlData = (**ItemHandle).ctlValue = 0;
    }
    EndResAccess(rav);
    InitCursor();
}

/*****************************************************************************
 * refreshType and refreshItem setup the list display parameters for the type
 * and item lists.
 *****************************************************************************/
void refreshType(word rType)
{
    int i, select, start;
    GrafPortPtr curPort;

    curPort = GetPort();
    SetPort(RootWPtr);

    ListType = 0;
    start = (ListTnum == 0) ? 0 : 1;

    NewList2((long) 0, start, (**TypeHandle).ctlList, 0, ListTnum, TypeHandle);
    shError(toolerror(), 0xB102);

    if (start) {
        SortList2((long) 0, TypeHandle);
        shError(toolerror(), 0xB104);

        select = 1;

        if (rType)
            for (; select <= ListTnum; select++)
                if ((**TypeHandle).ctlList[select-1].rType == rType)
                    break;

        if ((**TypeHandle).ctlData <= (**TypeHandle).ctlView)
            start = 1;
        else if (select > (**TypeHandle).ctlData - (**TypeHandle).ctlView)
            start = (**TypeHandle).ctlData - (**TypeHandle).ctlView + 1;
        else
            start = select;

        for (i = 1; i <= (**TypeHandle).ctlData; i++)
            (**TypeHandle).ctlList[i-1].flag = (i == select ? 0x80 : 0x00);

        NewList2((long) 0, start, (**TypeHandle).ctlList, 0, ListTnum,
            TypeHandle);
        shError(toolerror(), 0xB106);

        ListType = (**TypeHandle).ctlList[select-1].rType;
    }
    SetPort(curPort);

}

void refreshItem(long rID)
{
    int i, select, start;
    GrafPortPtr curPort;

    curPort = GetPort();
    SetPort(RootWPtr);

    ListIrID = 0L;
    start = (ListInum == 0L) ? 0 : 1;

    NewList2((long) 0, start, (**ItemHandle).ctlList, 0, (int) ListInum,
                            ItemHandle);
    shError(toolerror(), 0xB202);

    if (start) {

        SortList2((long) 0, ItemHandle);
        shError(toolerror(), 0xB204);
        select = 1;
        if (rID)
            for (; select <= (int) ListInum; select++)
                if ((**ItemHandle).ctlList[select-1].rID == rID)
                    break;

        if ((**ItemHandle).ctlData <= (**ItemHandle).ctlView)
            start = 1;
        else if (select > (**ItemHandle).ctlData - (**ItemHandle).ctlView)
            start = (**ItemHandle).ctlData - (**ItemHandle).ctlView + 1;
        else
            start = select;

        for (i = 1; i <= (**ItemHandle).ctlData; i++)
            (**ItemHandle).ctlList[i-1].flag = (i == select ? 0x80 : 0x00);

        NewList2((long) 0, start, (**ItemHandle).ctlList, 0, (int) ListInum,
                            ItemHandle);
        shError(toolerror(), 0xB206);

        ListIrID = (**ItemHandle).ctlList[select-1].rID;
    }
    SetPort(curPort);
}

/*****************************************************************************
 * AddItem adds an item to the item list
 *****************************************************************************/
int AddItem(word rType, long rID)
{
    int i, buildType;
    word LastType;

    buildType = TRUE;

    if (ListTnum) { /* if there's a list then see if we're in it */
        LastType = ListType; /* remember what type and item was selected */
        for (i = 0; i < ListTnum; i++)
            if ((**TypeHandle).ctlList[i].rType == rType) /* we're here... */
                buildType = FALSE; /* ...so don't build the typelist */

        if (buildType == TRUE) { /* we're not in the list... */
            rebuildType(); /* make us a new list */
            refreshType(LastType);
        }

        else if (LastType == rType) { /* we're in the list, is it us? */
            rebuildItem(rType, 0);
            refreshItem(rID);
        }
    }
    else
        InitTypeList();
        
    return(0);
}

/*****************************************************************************
 * DelItem removes an item from the item list.
 *****************************************************************************/
int DelItem(word rType, long rID)
{
    int rDepth, i;
    word LastType;
    long rCount, LastID, rav;
    
    WaitCursor();
    LastType = ListType; /* remember which one is selected in both lists */
    LastID = ListIrID;    
    rav = SetResAccess(ResFileID, 1);
    rCount = CountResources(rType);
    shError(toolerror(), 0xB704);
    EndResAccess(rav);
    InitCursor();

    if (LastType == rType) {    /* The item we deleted was in the item list */
        if (rCount) {               /* There are more in the list */
            rebuildItem(rType, 0);
            refreshItem(LastID == rID ? 0L : LastID);
        }
        else
            InitTypeList();
    }
    else if (!rCount) {         /* The item we deleted was not in the list */
        rebuildType();
        refreshType(LastType == rType ? 0 : LastType);
    }

    return 0;
}

/*****************************************************************************
 * KillList uses the private list user ID to kill all memory for the lists
 *****************************************************************************/
void KillList(void)
{
    DisposeAll(ListsID);
    if (toolerror())
        SysBeep();
    ListTnum = 0;
    ListInum = 0L;
    (**TypeHandle).ctlList = 0L;
    (**ItemHandle).ctlList = 0L;
}

/*****************************************************************************
 * updateList lets us know what item is currently selected
 *****************************************************************************/
void updateList(int i)
{
    int NextType;

    if (!ListTnum) {
        ListType = 0;
        ListIrID = 0;
        return;
    }

    if (!i) {
        NextType = (**TypeHandle).ctlList[NextMember2(0, TypeHandle) - 1].rType;
        shError(toolerror(), 0xB902);
        if (NextType != ListType) {
            ListType = NextType;
            rebuildItem(ListType, 1);
            refreshItem(0L);
        }
    }

    ListIrID = (**ItemHandle).ctlList[NextMember2(0, ItemHandle) - 1].rID;
    shError(toolerror(), 0xB904);
}

/*****************************************************************************
 * saveNames will save resource names to a rName resource from the current
 * root window types list.
 *****************************************************************************/
int saveNames(word rType) {

    char    *rNames;
    int     rDepth, fileID, i;
    long    nameID, ID, maxSize, maxNames, cntNames, rav;
    ResNameRecHndl nameHndl;

    cntNames = 0L;

    rav = SetResAccess(ResFileID, 1);

    maxNames = CountResources(rType); /* maximum number of possible names */
    maxSize = 6 + (maxNames * (IMAX+4)); /* and thus, the largest handle */
    nameHndl = (ResNameRecHndl) NewHandle(maxSize, ListsID, 0xc018, 0L);
    shError(toolerror(), 0xBA10);

    rNames = (char *) (*nameHndl);
    rNames += 6;
    for(i = 0; i < maxNames; i++) {
        if ((**ItemHandle).ctlList[i].alias != 0) {
            rNames = rNameBuild(rNames, (**ItemHandle).ctlList[i].rID,
                (**ItemHandle).ctlList[i].text);
            cntNames++;
        }
    }

    if (cntNames == 0L)
        DisposeHandle(nameHndl);

    else {
        nameID = 0x10000L + (long) rType;
        (**nameHndl).nameCount = cntNames;
        (**nameHndl).version = 1;
        maxSize = (long) rNames - (long) *nameHndl;
        SetHandleSize(maxSize, nameHndl);
        shError(toolerror(), 0xBA12);

        ReleaseResource(-1, 0x8014, nameID);
        RemoveResource(0x8014, nameID);

        AddResource(nameHndl, 0, 0x8014, nameID);
        shError(toolerror(), 0xBA20);
        WriteResource(0x8014, nameID);
        shError(toolerror(), 0xBA21);
        ReleaseResource(-1, 0x8014, nameID);
        shError(toolerror(), 0xBA22);

        SMURF.EditModifiers |= 0x8000;

    }
    EndResAccess(rav);
    return 0;
}
