/*-*-C-*-
 * class & cse functions for ResEd shell
 */

#include "resed.h"
#include "main.h"

#include "swicall.h"
#include "wimp.h"
#include "resformat.h"

#include "class.h"
#include "document.h"


static CSEPtr cses = NULL;            /* head of list of CSE records */
static ClassPtr classes = NULL;       /* head of list of class records */

static ClassPtr unkclassptr = NULL;   /* describes CSE which will edit
                                         unknown objects - if any */


/*
 * find out all about the CSE whose app directory is quoted
 */

static error * investigate_cse (const char *cseapp)
{
    char name[FILENAMELEN + 30];              /* extra space is to avoid overflowing when adding a leafname etc */
    char buf[100];
    FILE *config = NULL;
    CSEPtr thiscse = (CSEPtr) malloc (sizeof(CSERec));

    if (thiscse == NULL)
        return error_lookup("NoMem");

    strcpy(name, cseapp);
    strcat(name, ".!Config");

    /* Make sure the config file is there */
    if ((config = fopen(name, "r")) == NULL)
    {
        free((char *) thiscse);
        return error_lookup("CantRead", name);
    }

    /* Fill the CSE record in, and attach to head of CSE list */

    thiscse->taskid = 0;                      /* not running */
    strcpy(thiscse->pathname, cseapp);
    thiscse->next = cses;
    cses = thiscse;

    /* Read config file and allocate class records */

    while (fgets(buf, sizeof(buf), config) != NULL)
    {
        char one[sizeof(buf)], two[sizeof(buf)], three[sizeof(buf)];

/*        dprintf ("SHELL: config line *%s*\n", buf); */

        /* This cannot overflow the buffers because they are all the same length as the
         * input string, which is known to be terminated.
         */

        if (sscanf (buf, "%[^,],%[^,],%s", one, two, three) == 3)
        {
            ClassPtr thisclass = (ClassPtr) malloc(sizeof(ClassRec));
            Bool catchall = (strcmp (one, "Unknown") == 0);

            if (thisclass == NULL)
            {
                fclose(config);
                return error_lookup("NoMem");
            }

            if (catchall)
                thisclass->class = -1;
            else if (*one == '&')
                sscanf(one + 1, "%x", &thisclass->class);
            else if (*one == '0' && (*(one + 1) == 'x' || *(one + 1) == 'X'))
                sscanf(one + 2, "%x", &thisclass->class);
            else
                sscanf(one, "%d", &thisclass->class);
    
            sprintf(thisclass->classname, "%.*s", sizeof(thisclass->classname) - 1, two);
            sprintf(thisclass->spritename, "%.*s", sizeof(thisclass->spritename) - 1, three);

            thisclass->cse = thiscse;

            if (catchall)
                unkclassptr = thisclass;
            else
            {
                /* Link it onto the list */
                thisclass->next = classes;
                classes = thisclass;
            }

/*            dprintf ("SHELL: config for class 0x%x: name=%s, sprite=%s\n",
                     thisclass->class, thisclass->classname, thisclass->spritename); */
        }
    }
    
    fclose(config);
    
    /* Load the Icons file (ignore any error generated) */

/*    sprintf(name, "%%iconsprites %s.!Icons", cseapp);  */
/*    system(name);                                      */
/* It's better not to use system(..) with the debugger   */

    {
        char *suffix;
        char *end;
        Bool spritesloaded = FALSE;

        sprintf(name, "%s.!Icons", cseapp);
        end = name + strlen(name);

        /* try, eg, !Icons22, first of all */
        if (swi (Wimp_ReadSysInfo, R0, 2,      /* sets suffix to, eg, "22" */
                              OUT, R0, &suffix, END) == NULL)
        {
            int objtype = 0;

            strcpy (end, suffix);

            /* set objtype = 1 if the suffixed file exists */
            swi (OS_File, R0, 17,
                          R1, (int) name,
                     OUT, R0, &objtype, END);

            if (objtype == 1)
                spritesloaded =
                  (swi (Wimp_SpriteOp, R0, 11, R2, (int) name, END)) == NULL;
        }

        /* otherwise, try plain old !Icons */
        if (!spritesloaded)
        {
            *end = 0;
            swi (Wimp_SpriteOp, R0, 11, R2, (int) name, END);
        }
    }

    return NULL;
}


/*
 * Examine CSE directory and determine what classes we have available.
 * Build the Classes and CSEs lists, and get all the CSEs' palettes and
 * sprites loaded up.
 */

error * class_initialise ()
{
    int offset = 0;
    char csedir[FILENAMELEN];
    struct
    {
        unsigned int l_addr, e_addr, length, attr, type, filetype;
        char name[FILENAMELEN];
    } stat;

    /* Iterate over all the application directories inside appdir.CSE */
    strcpy(csedir, appdir);
    strcat(csedir, ".CSE");

    do
    {
        int numread;
        ER ( swi (OS_GBPB,  R0, 12,  R1, csedir,  R2, &stat,  R3, 1,  R4, offset,  R5, sizeof(stat),  R6, "!*",
                  OUT,  R3, &numread,  R4, &offset,  END) );
        if (numread == 1 && stat.filetype == 0x2000)
        {
            char fullname [FILENAMELEN + 30];         /* extra is to avoid overflow when appending leafname */
            strcpy (fullname, csedir);
            strcat (fullname, ".");
            strcat (fullname, stat.name);
            ER ( investigate_cse (fullname) );    /* find out about that CSE */
        }
    } while (offset != -1);

    return NULL;
}


/*
 * Return the ClassPtr corresponding to a given class ID, or unkclassptr
 * if unknown.
 */

ClassPtr class_lookup (ObjectClass class)
{
    ClassPtr this = classes;
    while (this)
    {
        if (this->class == class)
            return this;
        this = this->next;
    }
    return unkclassptr;
}


/*
 * Prime the palette document with all the palette data
 */

error * class_prime_palette ()
{
    char name[FILENAMELEN + 20];
    CSEPtr this;
/*    dprintf ("SHELL: class_prime_palette()\n"); */
    for (this = cses; this; this = this->next)
    {
        strcpy(name, this->pathname);
        strcat(name, ".!Palette");
/*        dprintf ("SHELL: priming palette for %s\n", name); */
        ER ( document_merge_palette (name) );
    }
/*    dprintf ("SHELL: palette priming complete\n"); */
    return NULL;
}


/*
 * Look up the given task ID.  If it is one of our CSEs, then return its
 * address.  Otherwise return NULL.
 */

CSEPtr class_lookup_cse (int taskid)
{
    CSEPtr this = cses;
    for (; this; this = this->next)
        if (this->taskid == taskid)
            return this;
    return NULL;
}


/*
 * Attempt to launch the CSE for the specified class
 */

error * class_launch_cse (ClassPtr class)
{
    char arguments[FILENAMELEN + 10];
    error *err = NULL;
    if (class == NULL)
        return error_lookup("CantEdit");
    if (class->cse->taskid != 0)
        return NULL;            /* it is running already */

    sprintf (arguments, "%s %d", class->cse->pathname, taskhandle);
    err = swi (Wimp_StartTask,  R0, arguments,  OUT,  R0, &class->cse->taskid,  END);

    if (class->cse->taskid == 0)
        err = error_lookup ("CSEStart", class->classname);
    else if (err)
        class->cse->taskid = 0;

    return err;
}
