#include <exec/types.h>                 // exec
#include <exec/memory.h>
#include <exec/libraries.h>
#include <graphics/gfx.h>               // graphics
#include <graphics/modeid.h>
#include <graphics/gfxbase.h>
#include <datatypes/datatypes.h>        // datatypes
#include <datatypes/datatypesclass.h>
#include <datatypes/pictureclass.h>

#include <proto/exec.h>
#include <proto/graphics.h>
#include <proto/datatypes.h>
#include <proto/iffparse.h>
#include <proto/dos.h>
#include "mybrush.hpp"


struct Library  *DataTypesBase;
struct GfxBase  *GfxBase;
struct Library  *IFFParseBase;

void FreeImg(struct MyImage *Img);


UBYTE *LoadIFF( STRPTR File,  struct MyImage *Img )
    {
    BOOL                ok = FALSE;
    UBYTE              *Data = NULL;
    struct IFFHandle   *iff;

    if( !IFFParseBase )
        return( NULL );

    if( iff = AllocIFF() ) {

        if( iff->iff_Stream = Open( File, MODE_OLDFILE )) {

            InitIFFasDOS( iff );

            if(!( OpenIFF( iff, IFFF_READ ))) {
                UWORD ret;

                PropChunk( iff, ID_ILBM, ID_BMHD );
                PropChunk( iff, ID_ILBM, ID_CMAP );
                StopChunk( iff, ID_ILBM, ID_BODY );

                ret = ParseIFF( iff, IFFPARSE_SCAN );

                if(( ret == 0 ) || ( ret == IFFERR_EOF )) {

                    struct StoredProperty  *prop;

                    if( prop = FindProp( iff, ID_ILBM, ID_BMHD )) {
                        struct BitMapHeader *bmh;
                        ULONG                Size, PlaneSize;
                        UWORD                RowBytes;

                        bmh = (struct BitMapHeader *) prop->sp_Data;

                        if( prop = FindProp( iff, ID_ILBM, ID_CMAP )) {
                            if( Img->CReg = (UBYTE *) AllocVec( prop->sp_Size, MEMF_ANY ))
                                CopyMem( prop->sp_Data, Img->CReg, prop->sp_Size );
                        }

                        Img->Width  = bmh->bmh_Width;
                        Img->Height = bmh->bmh_Height;
                        Img->Depth  = bmh->bmh_Depth;

                        RowBytes  = ((( bmh->bmh_Width + 15 ) & 0xFFF0 ) >> 3 );
                        PlaneSize = RowBytes  * Img->Height;
                        Size      = PlaneSize * bmh->bmh_Depth;

                        Img->BytesPerRow = RowBytes;

                        if(( Img->Depth <= 8 ) && ( Data = (UBYTE *) AllocVec( Size, MEMF_CLEAR ))) {
                            struct ContextNode *cn;
                            UWORD               i;
                            UBYTE              *plane;

                            plane = Data;

                            for( i = 0; i < bmh->bmh_Depth; i++ ) {
                                Img->Planes[ i ]  = plane;
                                plane            += PlaneSize;
                            }

                            cn = CurrentChunk( iff );

                            if( cn->cn_ID == ID_BODY ) {
                                BYTE *buf;

                                if( buf = (BYTE *) AllocVec( cn->cn_Size, MEMF_CLEAR )) {
                                    BYTE *buf2;

                                    buf2 = buf;

                                    ReadChunkBytes( iff, buf, cn->cn_Size );

                                    switch( bmh->bmh_Compression ) {
                                        UWORD  c, d, e, f;
                                        BYTE  *dest, *dest2;

                                        case 0:
                                            for( c = 0; c < bmh->bmh_Height; c++ ) {
                                                dest = Data;
                                                for( d = 0; d < bmh->bmh_Depth; d++ ) {
                                                    dest2 = dest + ( c * RowBytes );
                                                    for( e = 0; e < RowBytes; e++ )
                                                        *dest2++ = *buf++;
                                                    dest += PlaneSize;
                                                }
                                            }
                                            ok = TRUE;
                                            break;

                                        case 1:
                                            for( c = 0; c < bmh->bmh_Height; c++ ) {
                                                dest = Data;
                                                for( d = 0; d < bmh->bmh_Depth; d++ ) {
                                                    dest2 = dest + ( c * RowBytes );
                                                    e = 0;
                                                    do {
                                                        BYTE nuovo = *buf++;
                                                        if( nuovo >= 0 ) {
                                                            e += ( nuovo + 1 );
                                                            for( f = 0; f <= nuovo; f++ )
                                                                *dest2++ = *buf++;
                                                        } else {
                                                            if( nuovo != -128 ) {
                                                                BYTE put;

                                                                nuovo = -nuovo;
                                                                e += ( nuovo + 1 );

                                                                put = *buf++;

                                                                for( f = 0; f <= nuovo; f++ )
                                                                    *dest2++ = put;
                                                            }
                                                        }
                                                    } while( e < RowBytes );
                                                    dest += PlaneSize;
                                                }
                                            }
                                            ok = TRUE;
                                            break;
                                    }

                                    FreeVec( buf2 );
                                }
                            }
                        }
                    }
                }

                CloseIFF( iff );
            }

            Close( iff->iff_Stream );
        }

        FreeIFF( iff );
    }

    if(!( ok )) {
        FreeVec( Data );
        Data = NULL;
    }

    return( Data );
    }


struct MyImage *LoadImage( STRPTR File )
    {
    struct MyImage *Img;

    if( Img = (struct MyImage *) AllocMem( sizeof( struct MyImage ), MEMF_CLEAR ))
        {
        if( ( (UBYTE *)Img->ImageData = LoadIFF( File, Img ) ) == NULL )
          {
          FreeImg( Img );
          Img = NULL;
          }
        }

    return( Img );
    }


void FreeImg(  struct MyImage *Img )
{
    if( Img ) {

        if( Img->Datatype )
            DisposeDTObject( (Object *) Img->Datatype );
        else {
            FreeVec( Img->ImageData );
            FreeVec( Img->CReg );
        }

        FreeMem( Img, sizeof( struct MyImage ));
    }
}



struct MyBrush *LoadBrush( STRPTR File )
{
    GfxBase       = (struct GfxBase *) OpenLibrary ( (UBYTE *) "graphics.library", 38);
    DataTypesBase = (struct Library *) OpenLibrary ( (UBYTE *) "datatypes.library", 38);
    IFFParseBase  = (struct Library *) OpenLibrary ( (UBYTE *) "iffparse.library", 38);

    struct MyImage *Img;
    struct MyBrush *Brush = NULL;

    if( Img = LoadImage( File )) {

        if( Brush = (struct MyBrush *) AllocMem( sizeof( struct MyBrush ), MEMF_CLEAR )) {
            BOOL    err = TRUE;

            Brush->Width  = Img->Width;
            Brush->Height = Img->Height;

            if( Brush->BitMap = AllocBitMap( Brush->Width, Brush->Height, Img->Depth, BMF_DISPLAYABLE | BMF_CLEAR, NULL )) {
                ULONG   i, row;

                row = ( Brush->Width + 7 ) >> 3;

                for( i = 0; i < Img->Depth; i++ ) {
                    UBYTE  *from, *to;
                    ULONG   h;

                    from = Img->Planes[ i ];
                    to   = Brush->BitMap->Planes[ i ];

                    for( h = 0; h < Img->Height; h++ ) {

                        CopyMem( from, to, row );

                        from += Img->BytesPerRow;
                        to   += Brush->BitMap->BytesPerRow;
                    }
                }

                i = ( 1L << Img->Depth ) * 3;

                if( Brush->Colors = AllocVec( i * sizeof( ULONG ), MEMF_ANY )) {
                    UBYTE  *from;

                    if( from = Img->CReg ) {
                        ULONG   n, *to;

                        to = Brush->Colors;

                        for( n = 0; n < i; n++ ) {
                            UBYTE   c;

                            c = *from++;

                            *to++ = ( c << 24 ) | ( c << 16 ) | ( c << 8 ) | c;
                        }
                    }

                    err = FALSE;
                }
            }

            if( err ) {
                FreeMem( Brush, sizeof( struct MyBrush ));
                Brush = NULL;
            }
        }

        FreeImg( Img );
    }

    CloseLibrary ((struct Library *) GfxBase);
    CloseLibrary ((struct Library *) DataTypesBase);
    CloseLibrary ((struct Library *) IFFParseBase);

    return( Brush );
}


void FreeBrush(  struct MyBrush *Brush )
{
    if( Brush ) {

        FreeBitMap( Brush->BitMap );

        FreeVec( Brush->Colors );

        FreeMem( Brush, sizeof( struct MyBrush ));
    }
}


