/*
**
**
**
**
**
*/
#include "handleiff.h"
#include "undefs.h"
#include "iffparse.pra"
#include <ctype.h>

/*#####################*\
 * Internal functions. *
\*#####################*/
/****************************\
 * Name:     SeekStream     *
 * Function: Move forward   *
 * or backward in the file  *
 * or in the clipboard      *
 * relative where we are.   *
\****************************/
long
SeekStream(struct IFF *iff, long delta)
{
    long error;

    geta4();
    TraceCall;
    if (iff->iff_Type==IOREQUEST) {
        IOReq(iff)->io_Offset += delta;
        error = SUCCESS;
    } else {
        if ((error = Seek(File(iff), delta, (long)OFFSET_CURRENT))>0)
            error = SUCCESS;
    }
    return error;
}

/*******************************\
 * Name:     WriteStream       *
 * Function: Write data to the *
 * stream (a file or the clip- *
 * board).                     *
\*******************************/
long 
WriteStream(
    struct IFF *iff, 
    UBYTE *data, 
    long length)  
{
    long error;

    geta4();
    TraceCall;
    if (iff->iff_Type==IOREQUEST) {
        IOReq(iff)->io_Command = CMD_WRITE;
        IOReq(iff)->io_Data = (UBYTE *)data;
        IOReq(iff)->io_Length = (long)length;
        DoClipIO(IOReq(iff));
        error = (long)-IOReq(iff)->io_Error;
    } else {
        error = Write(File(iff), (char *)data, length);
    }
    if (error>0)
        error = 0L;
    return error;
}

/********************************\
 * Name:     ReadStream         *
 * Function: Read data from the *
 * device (a file or the clip-  *
 * board).                      *
\********************************/
long
ReadStream(struct IFF *iff, UBYTE *data, long length)  
{
    long error;

    geta4();
    TraceCall;
    if (iff->iff_Type==IOREQUEST) {
        IOReq(iff)->io_Command = CMD_READ;
        IOReq(iff)->io_Data = (UBYTE *)data;
        IOReq(iff)->io_Length = (long)length;
        DoClipIO(IOReq(iff));
        if (IOReq(iff)->io_Error==0)
            error = IOReq(iff)->io_Actual;
        else
            error = (long)-IOReq(iff)->io_Error;
    } else {
        error = Read(File(iff), (char *)data, length);
    }
    return error;
}

/********************************\
 * Name:     AlignStream        *
 * Function: Align the read     *
 * data so that it will be an   *
 * even number of bytes.        *
\********************************/
long
AlignStream(struct IFF *iff)
{
    long error;
    
    geta4();
    TraceCall;
    if (iff->iff_Type==IOREQUEST) {
        if (IOReq(iff)->io_Offset%2)
            IOReq(iff)->io_Offset++;
        error = 0L;
    } else {
        if (Seek(File(iff), 0L, (long)OFFSET_CURRENT)%2)
            error = Seek(File(iff), 1L, (long)OFFSET_CURRENT);
    }
    if (error>0L)
        error = 0L;
    return error;
}


/*############################*\
 * Public iffparse functions. *
\*############################*/
/********************\
 * Initializations. *
\********************/
long
InitIFFasClip(struct IFF *iff)    
{
    geta4();
    TraceCall;
    iff->iff_Type = IOREQUEST;
    return SUCCESS;
}

long
InitIFFasDOS(struct IFF *iff)
{
    geta4();
    TraceCall;
    iff->iff_Type = FILEHANDLE;
    return SUCCESS;
}

long
OpenIFF(struct IFF *iff, long rwmode)
{
    geta4();
    TraceCall;
    IFFptr = 0;
    IFFStack[0].Size = 0;
    iff->iff_Flags = rwmode;
    if (iff->iff_Type==IOREQUEST) {
        if (rwmode==IFFF_READ)
            IOReq(iff)->io_Command = CMD_READ;
        else
            IOReq(iff)->io_Command = CMD_WRITE;
        IOReq(iff)->io_Offset = 0;
        IOReq(iff)->io_ClipID = 0;
    }
    return SUCCESS;
}

void
CloseIFF(struct IFF *iff)
{
    geta4();
    TraceCall;
    if (iff->iff_Type==IOREQUEST) {
        if ((iff->iff_Flags&IFFF_RWBITS)==IFFF_WRITE) { /* Make the clip public. */
            IOReq(iff)->io_Command = CMD_UPDATE;
            DoClipIO(IOReq(iff));
        } else {                        /* Flush the stuff that is not used. */
            long dummy;
            while (IOReq(iff)->io_Actual && !IOReq(iff)->io_Error) {
                IOReq(iff)->io_Data = (STRPTR)&dummy;
                IOReq(iff)->io_Length = 4;
                DoClipIO(IOReq(iff));
            }
        }
    }
}

struct IFF *
AllocIFF(void)
{
    struct IFF *iff;

    geta4();
    TraceCall;
    iff = NULL;
    if ((iff = AllocMem((long)sizeof(struct IFF), MEMF_PUBLIC|MEMF_CLEAR))==NULL)
        return NULL;

    if ((iff->iff_Stack = AllocMem((long)sizeof(long)*256L, 0L))==NULL) {
        FreeMem((APTR)iff, (long)sizeof(struct IFF));
        iff = NULL;
    }
    return iff;
}
   
void
FreeIFF(struct IFF *iff)
{
    geta4();
    TraceCall;
    if (iff) {
        if (iff->iff_Stack)
            FreeMem((APTR)iff->iff_Stack, (long)sizeof(long)*256L);
        FreeMem((APTR)iff, (long)sizeof(struct IFF));
    }
}
            
struct CBHandle *
OpenClipboard(long unit)
{
    struct Library *IFFParseBase;
    struct CBHandle *ch = NULL;

    geta4();
    TraceCall;
    if ((ch = AllocMem((long)sizeof(struct CBHandle), MEMF_CLEAR))==NULL)
        goto fail;
    if ((ch->cbh_CBport = CreatePort(0L, 0L))==NULL)
        goto fail;
    if ((ch->cbh_SatisfyPort = CreatePort(0L, 0L))==NULL)
        goto fail;
    if ((ch->cbh_Req = (struct IOClipReq *)CreateExtIO(ch->cbh_CBport, 
                                                (long)sizeof(struct IOClipReq)))==NULL)
        goto fail;
    if (OpenDevice("clipboard.device", unit, (struct IORequest *)ch->cbh_Req, 0L)) {
        DeleteExtIO((struct IORequest *)ch->cbh_Req);
        ch->cbh_Req = NULL;
        goto fail;
    }

    return ch;

fail:
    CloseClipboard(ch);
    return NULL;
}

void
CloseClipboard(struct CBHandle *ch)
{
    geta4();
    TraceCall;
    if (ch->cbh_Req) {
        CloseDevice((struct IORequest *)ch->cbh_Req);
        DeleteExtIO((struct IORequest *)ch->cbh_Req);
        ch->cbh_Req = NULL;
    }
    if (ch->cbh_CBport)
        DeletePort((struct MsgPort *)ch->cbh_CBport);
    if (ch->cbh_SatisfyPort)
        DeletePort((struct MsgPort *)ch->cbh_SatisfyPort);
    if (ch)
        FreeMem((APTR)ch, (long)sizeof(struct CBHandle));
}

/*************************\
 * Miscelous functions . *
\*************************/
long
NOOP()
{
    geta4();
    TraceCall;
    return FAILED;
}

void *
NULL_NOOP()
{
    geta4();
    TraceCall;
    return NULL;
}

UBYTE *
IDtoStr(long ID, UBYTE *buf)
{
    geta4();
    TraceCall;
    *((long *)buf) = ID;
    buf[4] = '\0';
    return buf;
}

#define ValidChar(l)    (isalpha(l) || ((l)==' '))

long
GoodID(long ID)
{
    char *str = (char *)&ID;

    geta4();
    TraceCall;
    return (long)(ValidChar(str[0])
                && ValidChar(str[1])
                && ValidChar(str[2])
                && ValidChar(str[3]));
}

long
GoodType(long ID)
{
    char *str = (char *)&ID;

    geta4();
    TraceCall;
    return (long)(ValidChar(str[0])
                && ValidChar(str[1])
                && ValidChar(str[2])
                && ValidChar(str[3]));
}

/************************\
 * The 'big' functions. *
\************************/
long
WriteChunkBytes(
    struct IFF *iff, 
    APTR data, 
    long size)
{
    long datasize = size;   /* Due to Manx bug! */

    geta4();
    TraceCall;
    if (WriteStream(iff, (UBYTE *)data, datasize))
        return IFFERR_READ;
    IFFStack[IFFptr].Size += datasize;
    return 0L;
}

long
ReadChunkBytes(
    struct IFF *iff, 
    APTR data, 
    size_t size)
{
    long error;
    long datasize = size;   /* Due to Manx bug! */

    geta4();
    TraceCall;
    if (data==NULL) {
        return IFFERR_SYNTAX;
    } else {
        if ((datasize==0) || (datasize>IFFStack[IFFptr].Size))
            datasize = IFFStack[IFFptr].Size;
    }
    if ((error=ReadStream(iff, (UBYTE *)data, datasize))<=0)
        if (error==0)
            return IFFERR_EOF;
        else
            return IFFERR_READ;
    IFFStack[IFFptr].Size -= datasize;
    if ((IFFStack[IFFptr].Size==0) && AlignStream(iff))
        return IFFERR_NOTIFF;
    return datasize;
}

long
PushChunk(
    struct IFF *iff, 
    long type, 
    long id, 
    long size)
{
    long t_type = type;

    geta4();
    TraceCall;
    if (WriteStream(iff, (UBYTE *)&id, 8L))
        return IFFERR_WRITE;
    if (t_type!=0L) {
        if (WriteStream(iff, (UBYTE *)&t_type, 4L))
            return IFFERR_WRITE;
        IFFStack[++IFFptr].Size=4;
    } else {
        IFFStack[++IFFptr].Size=0;
    }
    return 0L;
}

long
PopChunk(struct IFF *iff)
{
    long offset;
    long size;

    geta4();
    TraceCall;
    offset = IFFStack[IFFptr].Size;
    if (SeekStream(iff, -offset-4L))
        return IFFERR_SEEK;
    size = IFFStack[IFFptr].Size;
    if (WriteStream(iff, (UBYTE *)&size, 4L))
        return IFFERR_READ;
    if (IFFptr)
        IFFStack[--IFFptr].Size+=IFFStack[IFFptr+1].Size+8L;
    if (SeekStream(iff, offset))
        return IFFERR_SEEK;
    if (IFFStack[IFFptr+1].Size%2) {
        if (WriteStream(iff, (UBYTE *)"", 1L))
            return IFFERR_READ;
        IFFStack[IFFptr].Size++;
    }
    return 0L;
}

long
ParseIFF(struct IFF *iff, long control)
{
    struct IFFParseBase *IFFParseBase;
    long error;
    long type = 0L; 
    long id;
    long size;

    geta4();
    TraceCall;
    if (control==IFFPARSE_RAWSTEP) {
        if (IFFptr && IFFStack[IFFptr].Size==0) {
            IFFptr--;
            iff->current.cn_ID = 0L;
            return IFFERR_EOC;
        } else if (IFFptr && IFFStack[IFFptr].Complex==0) {
            iff->current.cn_ID = 0L;
            if (SeekStream(iff, (long)(IFFStack[IFFptr].Size+((IFFStack[IFFptr].Size)&1))))
                return IFFERR_SEEK;
            IFFptr--;
            return IFFERR_EOC;
        }
        
        if ((error=ReadStream(iff, (UBYTE *)&id, 4L))<=0)
            if (error==0)
                return IFFERR_EOF;
            else
                return IFFERR_READ;

        if (!GoodID(id))
            return IFFERR_NOTIFF;

        if ((error=ReadStream(iff, (UBYTE *)&size, 4L))<=0)
            if (error==0)
                return IFFERR_EOF;
            else
                return IFFERR_READ;

        if ((id==ID_FORM
        || id==ID_PROP
        || id==ID_CAT
        || id==ID_LIST)
        && ReadStream(iff, (UBYTE *)&type, 4L)<=0)
            return IFFERR_READ;
            

        if (type!=0L) {
            IFFStack[IFFptr].Size -= size + (size%2) + 8L;
            IFFStack[++IFFptr].Size = size - 4L;
            IFFStack[IFFptr].Complex = 1;
            iff->current.cn_Type = type;
        } else {
            IFFStack[IFFptr].Size -= size + (size%2) + 8L;
            IFFStack[++IFFptr].Size = size;
            IFFStack[IFFptr].Complex = 0;
        }
        iff->current.cn_ID = id;
        iff->current.cn_Size = size;
        iff->current.cn_Scan = ((type!=0) ? 4 : 0);
        return SUCCESS;
    } else
        return FAILED;
}

struct ContextNode*
CurrentChunk(struct IFF *iff)
{
    geta4();
    TraceCall;
    return &iff->current;
}
