#include <intuition/intuitionbase.h>

#include <datatypes/pictureclass.h>

#include <graphics/gfxbase.h>
#include <graphics/gfxmacros.h>
#include <graphics/modeid.h>

#include <dos/dosextens.h>
#include <dos/rdargs.h>

#include <exec/memory.h>

#include <clib/intuition_protos.h>
#include <clib/datatypes_protos.h>
#include <clib/utility_protos.h>
#include <clib/graphics_protos.h>
#include <clib/layers_protos.h>
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/alib_protos.h>

#include <string.h>

enum    {   ARG_NEWPAGE,ARG_FRAME,ARG_RECT,ARG_OVAL,ARG_SPOT,
            ARG_LINE,ARG_ERASE,ARG_COMPLEMENT,ARG_SET,ARG_LEFT,
            ARG_TOP,ARG_WIDTH,ARG_HEIGHT,ARG_RIGHT,ARG_BOTTOM,
            ARGCOUNT
        };

#define MAXVECTORS 10

struct PageContext
{
    struct RastPort      RastPort;
    LONG                 Width, Height;
    struct BitMap       *BitMap;
    struct AreaInfo      AreaInfo;
    UWORD                AreaVectors[5 * MAXVECTORS];
    struct TmpRas        TmpRas;
    APTR                 TmpBits;
    struct Layer_Info   *LayerInfo;
    struct Layer        *Layer;
};

STATIC VOID DeletePageContext(struct PageContext *Context)
{   if(Context)
    {
        if(Context->Layer) DeleteLayer(NULL,Context->Layer);
        if(Context->LayerInfo) DisposeLayerInfo(Context->LayerInfo);
        FreeVec(Context->TmpBits);
        WaitBlit();
        FreeBitMap(Context -> BitMap);
        FreeVec(Context);
    }
}

STATIC struct PageContext *CreatePageContext(LONG Width,LONG Height)
{
    struct PageContext *Context;

    if(Context = (struct PageContext *)AllocVec(
                  sizeof(struct PageContext),MEMF_ANY | MEMF_CLEAR))
    {
        LONG TmpSize = RASSIZE(Width,Height);
        Context -> Width    = Width;
        Context -> Height   = Height;

        if(!(Context -> TmpBits = AllocVec(TmpSize,MEMF_CHIP)))
        {   DeletePageContext(Context);
            return(NULL);
        }

        InitTmpRas(&Context -> TmpRas,Context -> TmpBits,TmpSize);

        if(!(Context -> BitMap = AllocBitMap(Width,Height,
                                        1,BMF_CLEAR,NULL)))
        {   DeletePageContext(Context);
            return(NULL);
        }

        if(!(Context -> LayerInfo = NewLayerInfo()))
        {   DeletePageContext(Context);
            return(NULL);
        }

        if(!(Context->Layer = CreateBehindLayer(Context->LayerInfo,
            Context->BitMap,0,0,Width - 1,Height - 1,
            LAYERSIMPLE | LAYERBACKDROP,NULL)))
        {
            DeletePageContext(Context);
            return(NULL);
        }

        CopyMem(Context -> Layer -> rp,&Context -> RastPort,
            sizeof(struct RastPort));

        Context->RastPort.TmpRas    = &Context->TmpRas;
        Context->RastPort.AreaInfo  = &Context->AreaInfo;
        return(Context);
    }
    else
        return(NULL);
}

STATIC struct PageContext *
ProcessGeo(struct RDArgs *ArgPtr,STRPTR *ArgArray,BPTR FileHandle)
{
    struct PageContext  *Context = NULL;
    UBYTE                Line[512];
    LONG                 Left,Top,
                         Width,Height;
    BOOL                 Dirty = FALSE;
    struct RastPort     *RPort;

    while(FGets(FileHandle,Line,512))
    {
        if(Dirty)
        {   FreeArgs(ArgPtr);
            Dirty = FALSE;
        }

        ArgPtr -> RDA_Flags |= RDAF_NOPROMPT;
        ArgPtr -> RDA_Source . CS_Buffer = Line;
        ArgPtr -> RDA_Source . CS_Length = strlen(Line);
        ArgPtr -> RDA_Source . CS_CurChr = 0;

        memset(ArgArray,0,sizeof(STRPTR) * ARGCOUNT);

        if(ReadArgs("NEWPAGE/S,FRAME/S,RECT/S,OVAL/S,SPOT/S,"
            "LINE/S,ERASE/S,COMPLEMENT/S,SET/S,LEFT/N,TOP/N,"
            "WIDTH/N,HEIGHT/N,RIGHT/K/N,BOTTOM/K/N",
            (LONG *)ArgArray,ArgPtr))
        {
            Dirty = TRUE;
            Left = Top = Width = Height = -1;

            if(ArgArray[ARG_LEFT]) Left = *(LONG *)ArgArray[ARG_LEFT];
            if(ArgArray[ARG_TOP])  Top = *(LONG *)ArgArray[ARG_TOP];
            if(ArgArray[ARG_RIGHT])
                Width = *(LONG *)ArgArray[ARG_RIGHT] - Left + 1;
            if(ArgArray[ARG_BOTTOM])
                Height = *(LONG *)ArgArray[ARG_BOTTOM] - Top + 1;
            if(ArgArray[ARG_WIDTH])
                Width = *(LONG *)ArgArray[ARG_WIDTH];
            if(ArgArray[ARG_HEIGHT])
                Height = *(LONG *)ArgArray[ARG_HEIGHT];

            if(ArgArray[ARG_NEWPAGE])
            {   if(Width > 0 && Height > 0)
                {
                    DeletePageContext(Context);
                    if(Context = CreatePageContext(Width,Height))
                        RPort = &Context -> RastPort;
                    else
                    {   SetIoErr(ERROR_NO_FREE_STORE);
                        return(NULL);
                    }
                }
                continue;
            }
            if(Context)
            {   if(ArgArray[ARG_COMPLEMENT])
                    SetABPenDrMd(RPort,1,0,COMPLEMENT);
                else
                {
                    LONG Pen;
                    if(ArgArray[ARG_ERASE]) Pen = 0;
                    else                    Pen = 1;

                    SetABPenDrMd(RPort,Pen,0,JAM1);
                }

                if(ArgArray[ARG_FRAME])
                {
                    if(Width > 0 && Height > 0)
                    {   Move(RPort,Left,Top);
                        Draw(RPort,Left + Width - 1,Top);
                        Draw(RPort,Left + Width - 1,Top + Height-1);
                        Draw(RPort,Left,Top + Height - 1);
                        Draw(RPort,Left,Top + 1);
                    }
                    continue;
                }

                if(ArgArray[ARG_RECT])
                {   if(Width > 0 && Height > 0)
                    {
                        RectFill(RPort,Left,Top,Left + Width - 1,
                            Top + Height - 1);
                    }
                    continue;
                }

                if(ArgArray[ARG_OVAL])
                {   if(Width > 0 && Height > 0)
                    {
                        LONG RadX,RadY;
                        RadX = Width    / 2;
                        RadY = Height   / 2;

                        if(RadX && RadY)
                        {
                            LONG MidX,MidY;
                            MidX = Left + RadX;
                            MidY = Top  + RadY;
                            DrawEllipse(RPort,MidX,MidY,RadX,RadY);
                        }
                    }
                    continue;
                }

                if(ArgArray[ARG_SPOT])
                {   if(Width > 0 && Height > 0)
                    {
                        LONG RadX,RadY;
                        RadX = Width    / 2;
                        RadY = Height   / 2;

                        if(RadX && RadY)
                        {
                            LONG MidX,MidY;
                            MidX = Left + RadX;
                            MidY = Top  + RadY;
                            InitArea(&Context -> AreaInfo,
                                Context -> AreaVectors,MAXVECTORS);
                            AreaEllipse(RPort,MidX,MidY,RadX,RadY);
                            AreaEnd(RPort);
                        }
                    }
                    continue;
                }

                if(ArgArray[ARG_LINE])
                {
                    Move(RPort,Left,Top);
                    Draw(RPort,Left + Width - 1,Top + Height - 1);
                    continue;
                }
            }
        }
    }

    if(Dirty)  FreeArgs(ArgPtr);
    return(Context);
}

STATIC struct PageContext *ReadGeo(BPTR FileHandle)
{
    struct PageContext  *Context = NULL;
    struct RDArgs       *ArgPtr;

    if(ArgPtr = (struct RDArgs *)
                AllocDosObjectTags(DOS_RDARGS,TAG_DONE))
    {
        STRPTR *ArgArray;
        if(ArgArray = (STRPTR *)AllocVec(sizeof(STRPTR) * ARGCOUNT,
                       MEMF_ANY))
        {   Context = ProcessGeo(ArgPtr,ArgArray,FileHandle);
            FreeVec(ArgArray);
        }
        FreeDosObject(DOS_RDARGS,ArgPtr);
    }
    return(Context);
}

STATIC BOOL GetPicture(Object *object,struct TagItem *TagList)
{
    BOOL    Result = FALSE;
    STRPTR  Name;

    if(Name = (STRPTR)GetTagData(DTA_Name,NULL,TagList))
    {   struct BitMapHeader *BitMapHeader;
        BPTR                 FileHandle;

        if(GetDTAttrs(object,
            DTA_Handle,         &FileHandle,
            PDTA_BitMapHeader,  &BitMapHeader,
            TAG_DONE) == 2)
        {
            if(FileHandle && BitMapHeader)
            {   struct PageContext *Context;

                if(Context = ReadGeo(FileHandle))
                {   struct BitMap *ClassBitMap;

                    if(ClassBitMap = AllocBitMap(Context->Width,
                                     Context->Height,1,NULL,NULL))
                    {   struct ColorRegister    *ColourMap;
                        ULONG                   *ColourTab;

                        BltBitMap(Context -> BitMap,0,0,ClassBitMap,
                            0,0,Context -> Width,Context -> Height,
                            0xC0,0xFF,NULL);

                        BitMapHeader->bmh_Width      = Context->Width;
                        BitMapHeader->bmh_PageWidth  = Context->Width;
                        BitMapHeader->bmh_Height     = Context->Height;
                        BitMapHeader->bmh_PageHeight = Context->Height;
                        BitMapHeader->bmh_Depth      = 1;

                        SetDTAttrs(object,NULL,NULL,
                                   PDTA_NumColors, 2,TAG_DONE);

                        GetDTAttrs(object,
                                   PDTA_ColorRegisters,&ColourMap,
                                   PDTA_CRegs,         &ColourTab,
                                   TAG_DONE);

                        SetDTAttrs(object,NULL,NULL,
                            DTA_ObjName,      FilePart(Name),
                            DTA_NominalHoriz, BitMapHeader->bmh_Width,
                            DTA_NominalVert,  BitMapHeader->bmh_Height,
                            PDTA_BitMap,      ClassBitMap,
                            PDTA_ModeID,      HIRESLACE_KEY,
                            TAG_DONE);

                        ColourMap[0] . red      = 0xFF;
                        ColourMap[0] . green    = 0xFF;
                        ColourMap[0] . blue     = 0xFF;
                        ColourMap[1] . red      = 0x00;
                        ColourMap[1] . green    = 0x00;
                        ColourMap[1] . blue     = 0x00;

                        *ColourTab++            = 0xFFFFFFFF;
                        *ColourTab++            = 0xFFFFFFFF;
                        *ColourTab++            = 0xFFFFFFFF;
                        *ColourTab++            = 0x00000000;
                        *ColourTab++            = 0x00000000;
                        *ColourTab              = 0x00000000;

                        Result = TRUE;
                    }
                    else SetIoErr(ERROR_NO_FREE_STORE);

                    DeletePageContext(Context);
                }
            }
            else SetIoErr(ERROR_OBJECT_NOT_FOUND);
        }
        else SetIoErr(ERROR_OBJECT_NOT_FOUND);
    }
    else SetIoErr(ERROR_OBJECT_NOT_FOUND);

    return(Result);
}

Object * __saveds __asm ClassDispatch(
    register __a0 struct IClass *class,register __a2 Object *object,
    register __a1 Msg msg)
{
    Object *Result;
    switch(msg -> MethodID)
    {   case OM_NEW:
            if(Result = (Object *)DoSuperMethodA(class,object,msg))
            {
                if(!GetPicture(Result,
                               ((struct opSet *)msg)->ops_AttrList))
                {
                    CoerceMethod(class,Result,OM_DISPOSE);
                    Result = NULL;
                }
            }
            break;
        default:
            Result = (Object *)DoSuperMethodA(class,object,msg);
            break;
    }
    return(Result);
}
