#include "graphics_support.h"
#include <clib/graphics_protos.h>
#include <libraries/iffparse.h>
#include <datatypes/pictureclass.h>
#include <clib/iffparse_protos.h>
#include <stdio.h>


/*
** INPUTS:
**          bitmap      = pointer to a bitmap (the bitmap contents will be altered)
**          cm          = pointer to ColorMap of the viewport
**          palette     = pointer to an array of RGB values the bitmap would like to use
**          pens        = pointer to an array of storage for (hopefully) allocated pens
**                        NB: all elements of pens array must be set to 0, prior to calling this function!
**          num_pens    = the size of the array (how many pens it can hold)
**                        NB: max = 256
**
** RESULT:
**          None
**
** NOTES:
**          This call must be matched with a call to freeallocatedpens()
*/
void remapbitmap (struct BitMap *bitmap, struct ColorMap *cm, ULONG *palette, struct pen *pens, UBYTE num_pens)
{
    ULONG R, G, B;
    UBYTE cmap_index[8];    // each pixel ends up with its cmap index val in cmap[pixel]
    WORD i;
    UWORD row, byteinrow;
    ULONG offset=0;

    for (row=0; row<bitmap->Rows; row++)
    {
        for (byteinrow=0; byteinrow<bitmap->BytesPerRow; byteinrow++)
        {
            #ifdef GRAPHICS_SUPPORT_DEBUG
            printf("offset:%d\n", offset);
            #endif

            // Read in 8 pixels down through the relevant bitplanes
            {
                int plane_shift;
                int buffer[8];

                // Erase contents of cmap_index and buffer
                for (i=0; i<8; i++)
                {
                    cmap_index[i]=0;
                    buffer[i]=0;
                }

                #ifdef GRAPHICS_SUPPORT_DEBUG
                printf("Reading plane data\n");
                #endif
                // Plane[0] is least significant plane!
                plane_shift=bitmap->Depth-1;
                for (i=bitmap->Depth-1; i>=0; i--)
                {
                    buffer[i]=*(bitmap->Planes[i]+offset);
                    cmap_index[0]=((UBYTE)(((buffer[i] & 128) >> 7) << plane_shift)) | cmap_index[0];
                    cmap_index[1]=((UBYTE)(((buffer[i] & 64)  >> 6) << plane_shift)) | cmap_index[1];
                    cmap_index[2]=((UBYTE)(((buffer[i] & 32)  >> 5) << plane_shift)) | cmap_index[2];
                    cmap_index[3]=((UBYTE)(((buffer[i] & 16)  >> 4) << plane_shift)) | cmap_index[3];
                    cmap_index[4]=((UBYTE)(((buffer[i] & 8)   >> 3) << plane_shift)) | cmap_index[4];
                    cmap_index[5]=((UBYTE)(((buffer[i] & 4)   >> 2) << plane_shift)) | cmap_index[5];
                    cmap_index[6]=((UBYTE)(((buffer[i] & 2)   >> 1) << plane_shift)) | cmap_index[6];
                    cmap_index[7]=((UBYTE)(((buffer[i] & 1)       ) << plane_shift)) | cmap_index[7];
                    plane_shift--;
                    #ifdef GRAPHICS_SUPPORT_DEBUG
                    printf("buffer[%d]:%4.d   cmap0:%d   cmap1:%d   cmap2:%d   cmap3:%d   cmap4:%d   cmap5:%d   cmap6:%d   cmap7:%d\n",
                            i, buffer[i], cmap_index[0], cmap_index[1], cmap_index[2], cmap_index[3], cmap_index[4], cmap_index[5], cmap_index[6], cmap_index[7]);
                    #endif
                }
            }

            #ifdef GRAPHICS_SUPPORT_DEBUG
            printf("\n");
            #endif

            // Allocate any pens we need
            for (i=0; i<8; i++) // Test each pixel
            {
                if (cmap_index[i]!=0)   // Ignore colour 0, which should be a transparent background
                {
                    int index=3*cmap_index[i];

                    R=palette[index];
                    G=palette[index+1];
                    B=palette[index+2];
                    #ifdef GRAPHICS_SUPPORT_DEBUG
                    printf("R=%x  G=%x  B=%x\n", R, G, B);
                    #endif

                    if (!alreadyallocated(R, G, B, pens, num_pens))
                    {
                        WORD freepen;
                        if ((freepen=findfreepen(pens, num_pens))!=-1)
                        {
                            /*adding OBP_FailIfBad, FALSE doesn't seem to make any difference here... */
                            if ((pens[freepen].number=ObtainBestPen(cm, R, G, B, OBP_Precision, PRECISION_IMAGE, TAG_DONE))!=-1)
                            {
                                pens[freepen].red=R;
                                pens[freepen].green=G;
                                pens[freepen].blue=B;
                            }
                        }
                    }
                }
            }

            // Give every pixel that needs it its new pen value
            #ifdef GRAPHICS_SUPPORT_DEBUG
            printf("Reassigning pixel pen values\n");
            #endif
            for (i=0; i<8; i++)
            {
                if (cmap_index[i]!=0)   // Ignore colour 0, which should be a transparent background
                {
                    WORD gotpen;

                    R=palette[3*cmap_index[i]];
                    G=palette[3*cmap_index[i]+1];
                    B=palette[3*cmap_index[i]+2];

                    if ((gotpen=findallocatedpen(R, G, B, pens, num_pens))!=-1)
                    {
                        cmap_index[i]=(UBYTE)(pens[gotpen].number);
                        #ifdef GRAPHICS_SUPPORT_DEBUG
                        printf("gotpen:%4.d  pennum=%4.d  cmap[%d]:%d\n", gotpen, pens[gotpen].number, i, cmap_index[i]);
                        #endif
                    }
                }
                #ifdef GRAPHICS_SUPPORT_DEBUG
                else
                    printf("Skipped colour 0\n");
                #endif
            }

            #ifdef GRAPHICS_SUPPORT_DEBUG
            print_pens(pens, num_pens);
            printf("\n");
            #endif

            // Write 8 pixels back into bitmap with new cmap index values for current
            // viewport's struct ColorMap
            #ifdef GRAPHICS_SUPPORT_DEBUG
            printf("Writing plane data\n");
            #endif
            {
                UBYTE buffer;
                int bitmask=1;
                int shift=0;

                for (i=bitmap->Depth-1; i>0; i--)
                    bitmask=bitmask*2;

                // Plane[0] is least significant plane!
                for (i=bitmap->Depth-1; i>=0; i--)
                {
                    buffer=   ((cmap_index[0] & bitmask)<<shift)
                            | ((cmap_index[1] & bitmask)<<shift)>>1
                            | ((cmap_index[2] & bitmask)<<shift)>>2
                            | ((cmap_index[3] & bitmask)<<shift)>>3
                            | ((cmap_index[4] & bitmask)<<shift)>>4
                            | ((cmap_index[5] & bitmask)<<shift)>>5
                            | ((cmap_index[6] & bitmask)<<shift)>>6
                            | ((cmap_index[7] & bitmask)<<shift)>>7;

                    #ifdef GRAPHICS_SUPPORT_DEBUG
                    printf("bitmask:%4.d   i:%d buffer:%d\n", bitmask, i, buffer);
                    #endif
                    *(bitmap->Planes[i]+offset)=buffer;
                    bitmask=bitmask>>1;
                    shift++;
                }
            }
            #ifdef GRAPHICS_SUPPORT_DEBUG
            printf("\n\n");
            #endif
            offset++;
        }
    }
}


/*
** INPUTS:
**          pens        = pointer to an array of pen numbers
**          num_pens    = the size of the array (how many pens it can hold)
**                        NB: max = 256
**
** RESULT:
**          The free pen index, or -1 on failure
*/
WORD findfreepen (struct pen *pens, UBYTE num_pens)
{
    WORD i;

    for (i=0; i<num_pens; i++)
    {
        if (pens[i].number==-1)
            return i;
    }
    return -1;  // if we get this far, there were no free pens
}

/*
** INPUTS:
**          R, G, B     = red, green and blue values (32 bit left justified fraction)
**          pens        = pointer to an array of pen numbers
**          num_pens    = the size of the array (how many pens it can hold)
**                        NB: max = 256
**
** RESULT:
**          TRUE if we already have a suitable pen, FALSE otherwise
*/
BOOL alreadyallocated (ULONG R, ULONG G, ULONG B, struct pen *pens, UBYTE num_pens)
{
    UBYTE i;

    for (i=0; i<num_pens; i++)
    {
        if (pens[i].red==R && pens[i].green==G && pens[i].blue==B)
            return TRUE;
    }
    return FALSE;
}


/*
** INPUTS:
**          R, G, B     = red, green and blue values (32 bit left justified fraction)
**          pens        = pointer to an array of pen numbers
**          num_pens    = the size of the array (how many pens it can hold)
**                        NB: max = 256
**
** RESULT:
**          An index into our array of allocated pens, or -1 if no matching pen is found
*/
WORD findallocatedpen (ULONG R, ULONG G, ULONG B, struct pen *pens, UBYTE num_pens)
{
    UBYTE i;

    for (i=0; i<num_pens; i++)
    {
        if (pens[i].red==R && pens[i].green==G && pens[i].blue==B && pens[i].number!=-1)
            return i;
    }
    return -1;
}

/*
** INPUTS:
**          cm          = pointer to ColorMap of the viewport
**          pens        = pointer to an array of pen numbers
**          num_pens    = the size of the array (how many pens it can hold)
**                        NB: max = 256
**
** RESULT:
**          None
*/
void freeallocatedpens (struct ColorMap *cm, struct pen *pens, UBYTE num_pens)
{
    UBYTE i;

    for (i=0; i<num_pens; i++)
    {
        if (pens[i].number!=-1)
            ReleasePen(cm, pens[i].number);
        else
            break;
    }
}

#ifdef GRAPHICS_SUPPORT_DEBUG
void print_pens (struct pen *pens, UBYTE num_pens)
{
    UBYTE i;

    printf("Pens: ");
    for (i=0; i<num_pens; i++)
    {
        if (pens[i].number!=-1)
            printf("%3.d = R:%x G:%x B%x\n", pens[i].number, pens[i].red, pens[i].green, pens[i].blue);
        else
            break;
    }
    printf("\n");
}
#endif


struct BitMap *bodytobitmap (ULONG width, ULONG height, ULONG depth, UBYTE *body)
{
    struct BitMap *bitmap;

    // Allocate 8 bitplanes so that the remapping routine has room to write
    // back pen numbers from ObtainBestPen() with values up to 255
    bitmap=AllocBitMap(width, height, 8, BMF_CLEAR | BMF_MINPLANES, NULL);

    {
        char plane, scanline;
        int row_width=((width+15)/16)*2;   // row width in bytes
        UBYTE *body_rover;
        int plane_offset=0;

        body_rover=body;

        for (scanline=0; scanline<height; scanline++)
        {
            for (plane=0; plane<depth; plane++)
            {
                memcpy(bitmap->Planes[plane]+plane_offset, body_rover, row_width);
                body_rover+=row_width;
            }
            plane_offset=row_width*(scanline+1);
        }
    }

    return bitmap;
}

BOOL loadiff (char *filename, ULONG *width, ULONG *height, ULONG *depth, ULONG *compression, UBYTE **body, ULONG **palette)
{
    struct IFFHandle *iff;
    BOOL result=FALSE;

    if (iff=AllocIFF())
    {
        if (iff->iff_Stream=Open(filename, MODE_OLDFILE))
        {
            InitIFFasDOS(iff);

            if (!OpenIFF(iff, IFFF_READ))
            {
                if (!PropChunk(iff, ID_ILBM, ID_BMHD))
                {
                    if (!PropChunk(iff, ID_ILBM, ID_CMAP))
                    {
                        if (!StopChunk(iff, ID_ILBM, ID_BODY))
                        {
                            ParseIFF(iff, IFFPARSE_SCAN);

                            {
                                struct StoredProperty *bmhd;

                                if (bmhd=FindProp(iff, ID_ILBM, ID_BMHD))
                                {
                                    struct BitMapHeader *the_bmhd;

                                    the_bmhd=(struct BitMapHeader *)bmhd->sp_Data;
                                    *width=the_bmhd->bmh_Width;
                                    *height=the_bmhd->bmh_Height;
                                    *depth=the_bmhd->bmh_Depth;

                                    // Don't store bmh_Compression value if compression==NULL
                                    if (compression)
                                        *compression=the_bmhd->bmh_Compression;
                                }
                            }

                            {
                                struct StoredProperty *cmap;

                                if (cmap=FindProp(iff, ID_ILBM, ID_CMAP))
                                {
                                    int num_primary_colours=cmap->sp_Size;
                                    UBYTE *primary_colour=cmap->sp_Data;
                                    int i;

                                    *palette=(ULONG *)calloc(num_primary_colours, sizeof(ULONG));
                                    memset(*palette, 0, sizeof(ULONG)*num_primary_colours);

                                    for (i=0; i<num_primary_colours; i++)
                                    {
                                        (*palette)[i]=*primary_colour++;
                                        (*palette)[i]=(*palette)[i]<<24;
                                    }
                                }
                            }

                            {
                                struct ContextNode *cn;

                                cn=CurrentChunk(iff);
                                *body=(UBYTE *)malloc(cn->cn_Size);
                                ReadChunkBytes(iff, *body, cn->cn_Size);
                            }

                            result=TRUE;
                        }
                    }
                }
                CloseIFF(iff);
            }
            Close(iff->iff_Stream);
        }
        FreeIFF(iff);
    }

    return result;
}


