#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/utility.h>
#include <proto/graphics.h>
#include <graphics/gfxmacros.h>
#include <dos.h>
#include <graphics/gfx.h>

#include <stdio.h>
#include <exec/types.h>
#include <utility/tagitem.h>
#include <intuition/intuition.h>
#include <intuition/screens.h>
#include <intuition/gadgetclass.h> /* contains IDs for gadget attributes */
#include <intuition/icclass.h>     /* contains ICA_MAP, ICA_TARGET       */
#include <intuition/imageclass.h>
#include <math.h>
#include <debug.h>

UBYTE *vers = "\0$VER: SysI2 1.0";
struct BitMap *TmpRasBM;
struct SignalSemaphore TRBMSem;

typedef union MsgUnion
{
  ULONG  MethodID;
  struct opSet    opSet;
  struct opUpdate opUpdate;
  struct opGet    opGet;
  struct impDraw  impDraw;
} *Msgs;

void FillArea(struct RastPort *RP,ULONG Points, ...);
ULONG __saveds __asm Dispatcher(register __a0 Class *C, register __a2 struct Image *O, register __a1 Msgs M );
ULONG RenderImage(Class *C, struct Image *Image, Msgs M);
void DrawBack(struct RastPort *Shine, 
              struct RastPort *Shadow,
              struct RastPort *Fill,
              WORD X1,
              WORD Y1,
              WORD X2,
              WORD Y2);
void MyRectFill(struct RastPort *RP,
              WORD X1,
              WORD Y1,
              WORD X2,
              WORD Y2);

struct IntuitionBase *IntuitionBase;
struct Window *w;
struct IntuiMessage *msg;

#define NUM_IMAGES 12

ULONG sysnum[]=
{
  ZOOMIMAGE,
  SIZEIMAGE,
  CLOSEIMAGE,	
  SDEPTHIMAGE,	
  LEFTIMAGE,
  UPIMAGE,
  RIGHTIMAGE,
  DOWNIMAGE,
  CHECKIMAGE,
  MXIMAGE,
  MENUCHECK,
  AMIGAKEY
};

struct SysIData
{
  ULONG si_Which;
  struct DrawInfo *si_DrawInfo;
};

//#define DEMO


#ifdef DEMO
#define MYNAME "testmyiclass"
#define SUPERNAME "sysiclass"
#else
#define MYNAME "sysiclass"
#define SUPERNAME "oldsysiclass"
#endif

void install(void);
void test(void);

void main(void)
{
  if (IntuitionBase = OpenLibrary((UBYTE *)"intuition.library", 39L))
  {
    InitSemaphore(&TRBMSem);
    if(TmpRasBM=AllocBitMap(100,100,1,BMF_DISPLAYABLE,0))
    {
#ifndef DEMO
      install();
#else
      test();
#endif
      FreeBitMap(TmpRasBM);
    }    

    CloseLibrary(IntuitionBase);
  }
}

#ifndef DEMO
void install(void)
{
  Class *cl,*syscl;
  STRPTR oldsysname;

   if(cl=MakeClass(0,(UBYTE *)"sysiclass",NULL,0,0))
  {
    syscl=cl->cl_Super;
    RemoveClass(syscl);
    oldsysname=syscl->cl_ID;
    syscl->cl_ID=(UBYTE *)"oldsysiclass";
    AddClass(syscl);
    FreeClass(cl);
    if(cl=MakeClass((UBYTE *)"sysiclass",(UBYTE *)"oldsysiclass",NULL,sizeof(struct SysIData),0))
    {
      cl->cl_Dispatcher.h_Entry=Dispatcher;
      AddClass(cl);
      while(1) // endless loop
        Wait(0);
      FreeClass(cl);
    }
    syscl->cl_ID=oldsysname;
  }
}
#else
void test(void)
{
  Class *cl;
  Object *o[NUM_IMAGES];
  struct DrawInfo *di;
  struct Window *w;
  WORD width=32, height=32,l,done=0;
  LONG state=0;


  if(cl=MakeClass((UBYTE *)"testiclass",(UBYTE *)"sysiclass",NULL,sizeof(struct SysIData),0))
  {
    cl->cl_Dispatcher.h_Entry=Dispatcher;
    AddClass(cl);
    
    
    if (w = OpenWindowTags(NULL,
                      WA_Flags,       WFLG_CLOSEGADGET|WFLG_SIZEGADGET|WFLG_DRAGBAR|WFLG_DEPTHGADGET|WFLG_SIZEBBOTTOM,
                      WA_IDCMP,       IDCMP_CLOSEWINDOW|IDCMP_MOUSEBUTTONS|IDCMP_NEWSIZE,
                      WA_MinWidth,    100,
                      WA_MinHeight,   100,
                      WA_MaxWidth,    32000,
                      WA_MaxHeight,   32000,
                      WA_Width      ,32000,
                      WA_Height     ,32000,
                      WA_AutoAdjust, 1,
                      TAG_END))
    {                 
      if(di=GetScreenDrawInfo(w->WScreen))
      {
        
        for(l=0;l<NUM_IMAGES;l++)
        {
          o[l] = (struct Image *)NewObject(cl, NULL,
                          IA_Top      ,(w->BorderTop) + 5,  
                          IA_Left     ,(w->BorderLeft) + 5 + l * (width+10),
                          IA_Width    ,width,
                          IA_Height   ,height,
                          SYSIA_Which ,sysnum[l],
                          SYSIA_DrawInfo,di,
                          TAG_END);
        }
        while (done == FALSE)    
        {                        
            
          for(l=0;l<NUM_IMAGES;l++)
          {
            
            SetAttrs(o[l],IA_Top      ,(w->BorderTop) + 5,  
                          IA_Left     ,(w->BorderLeft) + 5 + l * (width+10),
                          IA_Width    ,width,
                          IA_Height   ,height,
                          TAG_DONE);
            
            DrawImageState(w->RPort,(struct Image *)o[l],(w->BorderLeft) + 5L + l * (width+10),(w->BorderTop) + 5+ l *(height + 10),state ,di);
          }
            
            
          state++;
          if(state>8)
          {
            state=0;
            width++;
            height++;
          }
            
          WaitPort((struct MsgPort *)w->UserPort);
          while (msg = (struct IntuiMessage *)GetMsg((struct MsgPort *)w->UserPort))
          {
            if (msg->Class == IDCMP_CLOSEWINDOW)
              done = TRUE;
            ReplyMsg((struct Message *)msg);
          }
        }
        for(l=0;l<NUM_IMAGES;l++)
        DisposeObject(o[l]);
        FreeScreenDrawInfo(w->WScreen,di);
      }
      CloseWindow(w);
    }
    FreeClass(cl);
  }
}
#endif


ULONG __saveds __asm Dispatcher(register __a0 Class *C, register __a2 struct Image *O, register __a1 Msgs M )
{
  struct SysIData *sidata;
  struct Image *object;
  ULONG retval=0;
/*
  WORD width,height;


  Point sizetable[]=
  {
    18,-1,24,-1,24,-1,
    18,-1,18,-1,24,-1,
    13,11,19,13,19,13,
    15,-1,20,-1,20,-1,
    -1,-1,-1,-1,-1,-1,
    17,-1,23,-1,23,-1,
    -1,-1,-1,-1,-1,-1,
    -1,-1,-1,-1,-1,-1,
    -1,-1,-1,-1,-1,-1,
    -1,-1,-1,-1,-1,-1,
    16,11,19,13,19,13,
    16,11,19,13,19,13,
    16,11,19,13,19,13,
    16,11,19,13,19,13,
  };
*/
  switch(M->MethodID)
  {
    case OM_NEW:
      {
        ULONG which,size;
        struct DrawInfo *di;
        
        di=(struct DrawInfo *)GetTagData(SYSIA_DrawInfo,0, M->opSet.ops_AttrList);
        
        if(di)
        {
          if(object=(struct Image *)DoSuperMethodA(C,(Object *)O,(Msg)M))
          {

            which =GetTagData(SYSIA_Which,   0, M->opSet.ops_AttrList);
            size  =GetTagData(SYSIA_Size,   2,M->opSet.ops_AttrList);

/*
            switch(which)
            {
              case SIZEIMAGE:
              case UPIMAGE:
              case DOWNIMAGE:
              case LEFTIMAGE:
              case RIGHTIMAGE:
                width=  sizetable[which*3+size].x;
                height= sizetable[which*3+size].y;
            
                if(width>-1)
                  object->Width=width;
                if(height>-1)
                  object->Height=height;
                
                object->Height=GetTagData(IA_Height,object->Height,M->opSet.ops_AttrList);
                object->Width =width;//(object->Height*di->dri_Resolution.Y)/di->dri_Resolution.X;
            }*/

            sidata=INST_DATA(C, object);
            //kprintf("sidata - %8lx\n",sidata);
            sidata->si_Which   =which;
            sidata->si_DrawInfo=di;

/*            
            switch(which)
            {
              case SIZEIMAGE:
              case LEFTIMAGE:
              case UPIMAGE:
              case RIGHTIMAGE:
              case DOWNIMAGE:
                object->Width=(object->Height*di->dri_Resolution.X)/di->dri_Resolution.Y;
                DoSuperMethod(C,object,OM_UPDATE,
                      IA_Width,object->Width,
                      TAG_DONE);
              break;
            }
            */
          }
          //kprintf("di - %8lx\n",di);
          //kprintf("which - %8lx\n",which);
          retval=(ULONG)object;
        }
      }
      break;

    case IM_DRAWFRAME:
    case IM_DRAW:
      //kprintf("IM_DRAW\n");
      retval=RenderImage(C,O,(Msgs)M);
      break;
/*      
    case OM_DISPOSE:
      sidata=INST_DATA(C, object);
      retval=DoSuperMethodA(C,(Object *)O,(Msg)M);
      break;
  */    
    default:
      retval=DoSuperMethodA(C,(Object *)O,(Msg)M);
      break;
  }
  return(retval);
}

#define SI2_INACTIVE    1<<0
#define SI2_SELECTED    1<<1
#define SI2_DISABLED    1<<2

ULONG RenderImage(Class *C, struct Image *Image, Msgs M)
{
  struct DrawInfo *di;
  struct SysIData *sidata;
  struct RastPort *rp,shinerp,shadowrp,backrp,fillrp,
                  *bshinerp,*bshadowrp,*bfillrp,
                  *ishinerp,*ishadowrp,*ibackrp;
  LONG state;
  LONG left,top,right,bottom,width,height;
  ULONG l,shine,shadow,back,fill;
  WORD w[11],h[11];
  ULONG att[]=
  {
    0,
    SI2_SELECTED,
    SI2_DISABLED,
    0,
    0,
    SI2_INACTIVE,
    SI2_INACTIVE | SI2_SELECTED,
    SI2_INACTIVE | SI2_DISABLED,
    SI2_SELECTED | SI2_DISABLED,
  };
  ULONG rv=1;

  geta4();
     
  sidata=INST_DATA(C, Image);

  if(!(di=M->impDraw.imp_DrInfo))
    di=sidata->si_DrawInfo;

  shine =di->dri_Pens[SHINEPEN];
  shadow=di->dri_Pens[SHADOWPEN];
  back  =di->dri_Pens[BACKGROUNDPEN];
  fill  =di->dri_Pens[FILLPEN];
  
  rp=M->impDraw.imp_RPort;
  shinerp =*rp;
  
  SetDrMd(&shinerp,JAM1);
  SetDrPt(&shinerp,~0);
  
  shadowrp=shinerp;
  fillrp  =shinerp;
  backrp  =fillrp;
  
  SetAPen(&shinerp,shine);
  SetAPen(&shadowrp,shadow);
  SetAPen(&backrp,back);
  SetAPen(&fillrp,fill);

  left=M->impDraw.imp_Offset.X;
  top =M->impDraw.imp_Offset.Y;
  
  if(M->MethodID==IM_DRAWFRAME)
  {
    width =M->impDraw.imp_Dimensions.Width;
    height=M->impDraw.imp_Dimensions.Height;
  }
  else
  {
    width =Image->Width;
    height=Image->Height;
  }
  
  switch(sidata->si_Which)
  {
    case DEPTHIMAGE:
    case ZOOMIMAGE:
      width--;
  }

  right =left + width  -1;
  bottom=top  + height -1;
  
  for(l=0;l<6;l++)
  {
    LONG x,y;
    
    x=(width  * l)/10;
    y=(height * l)/10;
    w[l]=x + left;
    h[l]=y + top;
    w[10-l]=right-x;
    h[10-l]=bottom-y;
  }
  
  ////kprintf("Drawing %ld %ld %ld %ld\n  sidata %8lx\n  DI %8lx  Which %8lx\n",left,top,width,height,sidata,sidata->si_DrawInfo,sidata->si_Which);
  
  /*** Draw Frame ***/
  SetDrMd(rp,JAM1);
  SetDrPt(rp,~0);

  state=att[M->impDraw.imp_State];

  //kprintf("state - %ld\n",state);
  
  if(state & SI2_SELECTED)
  {
    bshinerp  =&shadowrp;
    bshadowrp =&shinerp;
  }
  else
  {
    bshinerp  =&shinerp;
    bshadowrp =&shadowrp;
  }
  
  ishadowrp =&shadowrp;
  ibackrp   =&backrp;
  
  if(state & (SI2_INACTIVE))
  {
    bfillrp=&backrp;
    ishinerp=&shinerp; // <---------------
  }
  else
  {
    bfillrp=&fillrp;
    ishinerp=&shinerp;
  }
  
  /*
  if(sidata->si_Which<CHECKIMAGE)
  {
    MyRectFill(bfillrp,left,top,right,bottom);
    Move(bshadowrp,right,top);
    Draw(bshadowrp,right,bottom);
    Draw(bshadowrp,left,bottom);
    
    Move(bshinerp,left,bottom);
    Draw(bshinerp,left,top);
    Draw(bshinerp,right,top);
  }
  */
  
  switch(sidata->si_Which)
  {
    case SDEPTHIMAGE:
      bfillrp=&backrp;
    case DEPTHIMAGE:
      DrawBack(bshinerp,bshadowrp,bfillrp,left,top,right,bottom);
    
      Move(ishadowrp,w[2],h[6]);
        Draw(ishadowrp,w[2],h[2]);
        Draw(ishadowrp,w[6],h[2]);
      Move(ishadowrp,w[6],h[4]);
        Draw(ishadowrp,w[8],h[4]);
      Move(ishadowrp,w[4],h[6]);
        Draw(ishadowrp,w[4],h[8]);
      
      Move(ishinerp,w[6],h[2]+1);
        Draw(ishinerp,w[6],h[4]);
      Move(ishinerp,w[2]+1,h[6]);
        Draw(ishinerp,w[4],h[6]);
      Move(ishinerp,w[4]+1,h[8]);
        Draw(ishinerp,w[8],h[8]);
        Draw(ishinerp,w[8],h[4]+1);
      
      MyRectFill(ibackrp,w[2]+1,h[2]+1,w[6]-1,h[6]-1);
      MyRectFill(ibackrp,w[4]+1,h[4]+1,w[8]-1,h[8]-1);

//      MyRectFill(ishinerp,w[4]+1,h[4]+1,w[8]-2,h[8]-2);

      Move(&shadowrp,left-1,top+1);
        Draw(&shadowrp,left-1,bottom);
      break;
    case ZOOMIMAGE:
      DrawBack(bshinerp,bshadowrp,bfillrp,left,top,right,bottom);
    
      Move(ishinerp,w[8],h[2]);
        Draw(ishinerp,w[8],h[8]);
        Draw(ishinerp,w[2],h[8]);
      
      Move(ishadowrp,w[2],h[8]);
        Draw(ishadowrp,w[2],h[2]);
        Draw(ishadowrp,w[8],h[2]);
      
      MyRectFill(ibackrp  ,w[2]+1,h[2]+1,w[8]-1,h[8]-1);
      MyRectFill(ishadowrp,w[2]+2,h[2]+2,w[4]+2,h[4]+2);
      
      Move(&shadowrp,left-1,top+1);
        Draw(&shadowrp,left-1,bottom);
      break;
    case SIZEIMAGE:
      DrawBack(bshinerp,bshadowrp,bfillrp,left,top,right,bottom);

      FillArea(&backrp,3,w[8],h[2],
                         w[8],h[8],
                         w[2],h[8]);
      Move(ishinerp,w[8],h[2]);
        Draw(ishinerp,w[8],h[8]);
        Draw(ishinerp,w[2],h[8]);
      
      Move(ishadowrp,w[2],h[8]);
        Draw(ishadowrp,w[8],h[2]);
      break;
    case CLOSEIMAGE:
      DrawBack(bshinerp,bshadowrp,bfillrp,left,top,right,bottom);      

      Move(ishadowrp,w[2],h[6]-1);
        Draw(ishadowrp,w[2],h[4]+1);
      Move(ishadowrp,w[2]+1,h[4]);
        Draw(ishadowrp,w[8]-1,h[4]);
      
      Move(ishinerp,w[8],h[4]+1);
        Draw(ishinerp,w[8],h[6]-1);
      Move(ishinerp,w[8]-1,h[6]);
        Draw(ishinerp,w[2]+1,h[6]);
      
      MyRectFill(ibackrp,w[2]+1,h[4]+1,w[8]-1,h[6]-1);
      
      Move(&shinerp,right+1,top+1);
        Draw(&shinerp,right+1,bottom);
      break;
    case LEFTIMAGE:
      DrawBack(bshinerp,bshadowrp,bfillrp,left,top,right,bottom);
      
      FillArea(&backrp,3,w[7],h[2],
                         w[7],h[8],
                         w[2],h[5]);
      
      Move(ishinerp,w[7],h[2]);
        Draw(ishinerp,w[7],h[8]);
        Draw(ishinerp,w[2],h[5]);
      
      Move(ishadowrp,w[2],h[5]);
        Draw(ishadowrp,w[7],h[2]);
      break;
    case UPIMAGE:
      DrawBack(bshinerp,bshadowrp,bfillrp,left,top,right,bottom);
      
      FillArea(&backrp,3,w[2],h[7],
                         w[5],h[2],
                         w[8],h[7]);
      
      Move(ishinerp,w[8],h[7]);
        Draw(ishinerp,w[2],h[7]);
      
      Move(ishadowrp,w[2],h[7]);
        Draw(ishadowrp,w[5],h[2]);
        Draw(ishadowrp,w[8],h[7]);
      break;
    case RIGHTIMAGE:
      DrawBack(bshinerp,bshadowrp,bfillrp,left,top,right,bottom);

      FillArea(&backrp,3,w[3],h[2],
                         w[3],h[8],
                         w[8],h[5]);
      
      Move(ishinerp,w[8],h[5]);
        Draw(ishinerp,w[3],h[8]);
        
      Move(ishadowrp,w[3],h[8]);
        Draw(ishadowrp,w[3],h[2]);
        Draw(ishadowrp,w[8],h[5]);
      
      break;
    case DOWNIMAGE:
      DrawBack(bshinerp,bshadowrp,bfillrp,left,top,right,bottom);      
     
      FillArea(&backrp,3,w[2],h[3],
                         w[8],h[3],
                         w[5],h[8]);
     
     
      Move(ishinerp,w[8],h[3]);
        Draw(ishinerp,w[5],h[8]);
        Draw(ishinerp,w[2],h[3]);
      
      Move(ishadowrp,w[2],h[3]);
        Draw(ishadowrp,w[8],h[3]);
      break;
    //case MENUCHECK:
    /*
    case CHECKIMAGE:
      DrawBack(ishinerp,ishadowrp,ibackrp,left,top,right,bottom);
      if(state & SI2_SELECTED)
      {
        Move(ishinerp,w[9],h[2]);
          Draw(ishinerp,w[3],h[8]);
          Draw(ishinerp,w[1],h[6]);
        Move(ishadowrp,w[1],h[6]);
          Draw(ishadowrp,w[2],h[6]);
          Draw(ishadowrp,w[3],h[7]);
          Draw(ishadowrp,w[8],h[2]);
          Draw(ishadowrp,w[9],h[2]);
      }
      break;
    */
    case MXIMAGE:
      {
        struct RastPort *mxrp;
        
        mxrp=&backrp;
        if(state & SI2_SELECTED)
          mxrp=&fillrp;
        
        FillArea(mxrp,4,w[2],h[5],
                         w[5],h[2],
                         w[8],h[5],
                         w[5],h[8]);
      }
      Move(bshadowrp,w[0],h[5]);
        Draw(bshadowrp,w[5],h[10]);
        Draw(bshadowrp,w[10],h[5]);
      Move(bshinerp,w[0],h[5]);
        Draw(bshinerp,w[5],h[0]);
        Draw(bshinerp,w[10],h[5]);
      
      
      
      /*
      Move(bshadowrp,w[9],h[2]);
        Draw(bshadowrp,w[10],h[4]);
        Draw(bshadowrp,w[10],h[6]);
        Draw(bshadowrp,w[9],h[8]);
        Draw(bshadowrp,w[8],h[9]);
        Draw(bshadowrp,w[6],h[10]);
        Draw(bshadowrp,w[4],h[10]);
        Draw(bshadowrp,w[2],h[9]);
        Draw(bshadowrp,w[1],h[8]);
      Move(bshinerp,w[1],h[8]);
        Draw(bshinerp,w[0],h[6]);
        Draw(bshinerp,w[0],h[4]);
        Draw(bshinerp,w[1],h[2]);
        Draw(bshinerp,w[2],h[1]);
        Draw(bshinerp,w[4],h[0]);
        Draw(bshinerp,w[6],h[0]);
        Draw(bshinerp,w[8],h[1]);
        Draw(bshinerp,w[9],h[2]); 
        */
      break;
    default:
      rv=DoSuperMethodA(C,(Object *)Image,(Msg)M);
  }
  return(rv);
}

void DrawBack(struct RastPort *Shine, 
              struct RastPort *Shadow,
              struct RastPort *Fill,
              WORD X1,
              WORD Y1,
              WORD X2,
              WORD Y2)
{
  MyRectFill(Fill,X1,Y1,X2,Y2);
  Move(Shadow,X2,Y1);
  Draw(Shadow,X2,Y2);
  Draw(Shadow,X1,Y2);
    
  Move(Shine,X1,Y2);
  Draw(Shine,X1,Y1);
  Draw(Shine,X2,Y1);
}

struct Point32
{
  LONG X,Y;
};

void FillArea(struct RastPort *RP,ULONG Points, ...)
{
  struct AreaInfo ai={0},*oldai;
  __aligned UBYTE ab[5*5];

  struct TmpRas tr,*oldtr;

  WORD minx,maxx,miny,maxy,sx,sy;
  LONG l,points,*pntr;
  struct Point32 *p;

  pntr  = &Points;
  points= *pntr;

//  kprintf("--%8lx\n",pntr);

  pntr++;
  p=pntr;
  
  minx=miny= 32000;
  maxx=maxy=-32000;
  


  for(l=0;l<points;l++)
  {
    minx=min(minx,p[l].X);
    miny=min(miny,p[l].Y);
    maxx=max(maxx,p[l].X);
    maxy=max(maxy,p[l].Y);
  }
  sx=maxx-minx;
  sy=maxy-miny;
  
  if( sx<100 && sy<100 )
  {
    ObtainSemaphore(&TRBMSem);
    tr.RasPtr=TmpRasBM->Planes[0];
    tr.Size=TmpRasBM->BytesPerRow * TmpRasBM->Rows;
    oldtr=RP->TmpRas;
    oldai=RP->AreaInfo;
    RP->TmpRas=&tr;
    RP->AreaInfo=&ai;
    InitArea(&ai,ab,5);
    AreaMove(RP,p[0].X,p[0].Y);
    for(l=1;l<points;l++)
    {
//      printf("%ld  %ld\n",p[l].X,p[l].Y);
      AreaDraw(RP,p[l].X,p[l].Y);
    }
    AreaEnd(RP);
    RP->TmpRas=oldtr;
    RP->AreaInfo=oldai;
    WaitBlit();
    ReleaseSemaphore(&TRBMSem);
  }
}
  

void MyRectFill(struct RastPort *RP,
              WORD X1,
              WORD Y1,
              WORD X2,
              WORD Y2)
{
  if(X2>=X1 && Y2>=Y1 && X1>=0 && Y1>=0)
    RectFill(RP,X1,Y1,X2,Y2);
}
