#include <exec/types.h>
#include <exec/memory.h>
#include <libraries/dos.h>
#include <libraries/iffparse.h>
#include <graphics/gfx.h>
#include <utility/tagitem.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/graphics.h>
#include <proto/iffparse.h>
#include <utility/utility.h>
#include <utility/tagitem.h>

/* Includes needed to use the codecs.library and the codecs */
#include <libraries/codecs.h>
#include <proto/codecs.h>
#include <codecs/sp.h>
#include <codecs/byterun.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "iff_rw.h"

#define ID_ILBM MAKE_ID('I','L','B','M')
#define ID_BODY MAKE_ID('B','O','D','Y')
#define ID_BMHD MAKE_ID('B','M','H','D')
#define ID_CMAP MAKE_ID('C','M','A','P')

#define NUM_STOP_CHUNKS 3

#define SET_TAGITEM(tagitem, tag, data)  (tagitem).ti_Tag = (tag); (tagitem).ti_Data = (ULONG)(data)

struct Library *UtilityBase;
struct Library *CodecsBase;

static int ReadBody(struct IFFHandle *iff, BitMapHeader *bmhd, struct BitMap *bm, ULONG Size)
{
    UBYTE *body;
    Codec *MyCodec = NULL;
    struct TagItem TagList[5];


    if ((body = malloc(Size)) == NULL)
    {
       printf("Could not alloc body: %ld\n", Size);
       return FALSE;
    }
    ReadChunkBytes(iff, body, Size);

    printf("Compressed Size: %ld\n", Size);
    printf("Bytes per Row = %d\n", bm->BytesPerRow);

    /* Crete Codec to decode image */
    switch (bmhd->compression)
    {
        case 0:  printf("This ILBM picture is not compressed (not supported yet)\n"); break;
        case 1:  MyCodec = NewCodec("byterun.codec", 0); printf("Byterun codec\n");   break;
        case 2:  MyCodec = NewCodec("sp.codec", 0); printf("S+P Wavelet codec\n");    break;
        default: printf("Unsupported coding method for ILBM picture\n");              break;
    }

    if (MyCodec)
    {
        SET_TAGITEM(TagList[0], CA_SRC_HANDLE, body);
        SET_TAGITEM(TagList[1], CA_SRC_SIZE,   Size);
        SET_TAGITEM(TagList[2], CA_IMAGEWIDTH, bmhd->w);
        SET_TAGITEM(TagList[3], CA_DST_HANDLE, bm);
        SET_TAGITEM(TagList[4], TAG_END,       0);

        Decode(MyCodec, TagList);

        DisposeCodec(MyCodec);

        printf("Decompressed %d bytes to %d bytes\n", Size, bm->BytesPerRow * bm->Rows * bm->Depth);
    }
    else
    {
        printf("Could not create Codec object\n");
    }

    free(body);

    return TRUE;
}

static LONG SaveBODY(struct IFFHandle *iff, BitMapHeader *bmhd, struct BitMap *bm)
{
    struct TagItem TagList[5];
    ULONG  BufSize = 65536;
    LONG   Size;
    LONG   ReturnValue;
    UBYTE *Buffer;
    Codec *MyCodec = NULL;

    Buffer = malloc(BufSize);

    if (Buffer)
    {
        switch (bmhd->compression)
        {
            case 0:  printf("This ILBM BODY is not compressed (not supported yet)\n"); break;
            case 1:  MyCodec = NewCodec("byterun.codec", 0L); break;
            case 2:  MyCodec = NewCodec("sp.codec", 0L);      break;
            default: printf("Unknown compression method\n");  break;
        }

        if (MyCodec)
        {
            PushChunk(iff, ID_ILBM, ID_BODY, IFFSIZE_UNKNOWN);

            SET_TAGITEM(TagList[0], CA_DST_HANDLE, Buffer);
            SET_TAGITEM(TagList[1], CA_DST_SIZE,   BufSize);
            SET_TAGITEM(TagList[2], CA_IMAGEWIDTH, bmhd->w);
            SET_TAGITEM(TagList[3], CA_SRC_HANDLE, bm);
            SET_TAGITEM(TagList[4], TAG_END,       0);

            Size        = BufSize;
            ReturnValue = 0;

            while (Size == BufSize)
            {
                Size = Encode(MyCodec, TagList);
                if (Size > 0)
                {
                    if (WriteChunkBytes(iff, Buffer, Size) != Size)
                    {
                        printf("Could not write to IFF chunk BODY\n");
                        ReturnValue = -4L;
                        break;
                    }
                    else
                    {
                        ReturnValue += Size;
                        printf("Encoded %d bytes (total = %d)\n", Size, ReturnValue);
                    }
                }
                else
                {
                    printf("Problem %d in encoder", Size);
                    ReturnValue = -3L;
                    break;
                }
            }
            PopChunk(iff);
            DisposeCodec(MyCodec);
        }
        else
        {
            ReturnValue = -2L;
        }
        free(Buffer);
    }
    else
    {
        ReturnValue = -1L;
    }

    return ReturnValue;
}



BOOL LoadILBM(char *filename, BitMapHeader *bmhd, BYTE **cmap, struct BitMap **bm)
{
    struct IFFHandle *iff;
    struct ContextNode *cn;
    LONG StopChunkArray[NUM_STOP_CHUNKS*2] = { ID_ILBM, ID_BMHD,
                                               ID_ILBM, ID_CMAP,
                                               ID_ILBM, ID_BODY
                                             };
    LONG   error;

    *cmap = NULL;
    *bm   = NULL;


    if ((CodecsBase  = OpenLibrary("codecs.library", 0L)))
    {
        if ((UtilityBase = OpenLibrary(UTILITYNAME, 0L)))
        {
            if ((IFFParseBase = OpenLibrary("iffparse.library", 0L)))
            {
                if ((iff = AllocIFF()) != NULL)
                {
                    if ((iff->iff_Stream = (ULONG)Open(filename, MODE_READWRITE)) != NULL)
                    {
                        InitIFFasDOS(iff);
                        OpenIFF(iff, IFFF_READ);

                        if ((error = StopChunks(iff, StopChunkArray, NUM_STOP_CHUNKS)))
                        {
                            printf("error with StopChunk (%d)\n", error);
                            return FALSE;
                        }

                        for (;;)
                        {
                            error = ParseIFF(iff, IFFPARSE_SCAN);

                            if (error == IFFERR_EOF)
                                break;

                            if (error)
                            {
                                printf("Error during parse %d\n", error);
                                return FALSE;
                            }

                            cn = CurrentChunk(iff);
                            if ((cn) && (cn->cn_Type == ID_ILBM))
                            {
                                switch (cn->cn_ID)
                                {
                                    case ID_BMHD:
                                        ReadChunkBytes(iff, bmhd, sizeof(*bmhd));
                                        printf("Size:        %d x %d\n", bmhd->w, bmhd->h);
                                        printf("Planes:      %d\n", bmhd->nPlanes);
                                        printf("Compression: %d\n", bmhd->compression);
                                        break;

                                    case ID_CMAP:
                                        *cmap = malloc(cn->cn_Size);
                                        if (*cmap)
                                        {
                                            ReadChunkBytes(iff, *cmap, cn->cn_Size);
                                        }
                                        else
                                        {
                                            printf("Couldn't alloc memory for ColorMap\n");
                                        }
                                        break;

                                    case ID_BODY:
                                        *bm = AllocBitMap(bmhd->w, bmhd->h, bmhd->nPlanes, BMF_CLEAR, NULL);
                                        ReadBody(iff, bmhd, *bm, cn->cn_Size);
                                        break;
                                }
                            }
                        }

                        CloseIFF(iff);
                        Close(iff->iff_Stream);
                    }
                    FreeIFF(iff);
                }
                else
                {
                   printf("Couldn't alloc iff handle\n");
                }

                CloseLibrary(IFFParseBase);
            }
            else
            {
                printf("Couldn't open iffparse.library\n");
            }

            CloseLibrary(UtilityBase);
        }
        else
        {
            printf("Could not open utility.library\n");
        }

        CloseLibrary(CodecsBase);
    }
    else
    {
        printf("Could not open codecs.library\n");
    }

   return TRUE;
}

LONG SaveILBM(char *filename, BitMapHeader *bmhd, BYTE *cmap, struct BitMap *bm)
{
    struct IFFHandle *iff;
    LONG   BodySize = 0L;

    if ((CodecsBase  = OpenLibrary("codecs.library", 0)))
    {
        if ((UtilityBase = OpenLibrary(UTILITYNAME, 0L)))
        {
            if ((IFFParseBase = OpenLibrary("iffparse.library", 0L)))
            {
                if ((iff = AllocIFF()) != NULL)
                {
                    if ((iff->iff_Stream = (ULONG)Open(filename, MODE_NEWFILE)) != NULL)
                    {
                        InitIFFasDOS(iff);

                        OpenIFF(iff, IFFF_WRITE);
                        printf("iff file is open\n");

                        PushChunk(iff, ID_ILBM, ID_FORM, IFFSIZE_UNKNOWN);
                        {
                            /*
                             * Save BMHD chunk
                             */
                            PushChunk(iff, NULL, ID_BMHD, sizeof(*bmhd));
                            WriteChunkBytes(iff, bmhd,    sizeof(*bmhd));
                            PopChunk(iff);

                            /*
                             * Save CMAP chunk when available
                             */
                            if (cmap)
                            {
                                PushChunk(iff, NULL, ID_CMAP, 3 * (1L << bmhd->nPlanes));
                                WriteChunkBytes(iff, cmap,    3 * (1L << bmhd->nPlanes));
                                PopChunk(iff);
                            }

                            /*
                             * Save BODY chunk
                             */
                            BodySize = SaveBODY(iff, bmhd, bm);

                        }
                        PopChunk(iff);

                        CloseIFF(iff);
                        Close(iff->iff_Stream);
                    }
                    FreeIFF(iff);
                }
                else
                {
                   printf("Couldn't alloc iff handle\n");
                }

                CloseLibrary(IFFParseBase);
            }
            else
            {
               printf("Couldn't open iffparse.library\n");
            }

            CloseLibrary(UtilityBase);
        }
        else
        {
            printf("Could not open utility.library\n");
        }

        CloseLibrary(CodecsBase);
    }
    else
    {
        printf("Could not open codecs.library\n");
    }

    return BodySize;
}
