#include <exec/types.h>
#include <exec/nodes.h>
#include <exec/lists.h>
#include <exec/ports.h>
#include <stdio.h>

#include "pcgWindow.h"
#include "pcgWindowClass.h"
#include "Precognition_Utils.h"
#include "minmax.h"
#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/graphics.h>
#include "amigamem.h"

extern struct IntuitionBase *IntuitionBase;


Screen *pcgWindow_GetScreen( pcgWindow *self )
{
   Screen *screen;
   Window *iwindow;

   if (iwindow = iWindow(self))
      screen = self->Window->WScreen;
   else
      screen = self->NewWindow.Screen;

   return screen;
}


BOOL pcgWindow_SanityCheck(  pcgWindow *self,
                             Point     *location,
                             Point     *size )
   /* returns TRUE if location was changed. */
{
   Screen *screen;
   BOOL loc_changed = FALSE;

/*   printf("SanityCheck before: Location=%d,%d, Size=%d,%d\n",*/
/*      location->x, location->y, size->x, size->y );*/

   screen = pcgWindow_GetScreen(self);

   if (screen != NULL)
   {
      size->x = MIN( size->x, screen->Width );
      size->y = MIN( size->y, screen->Height );

      /* size is now ok, so check location. */
      if (location->x + size->x >= screen->Width)
      {
         location->x = screen->Width - size->x;
         loc_changed = TRUE;
      }
      if (location->y + size->y >= screen->Height)
      {
         location->y = screen->Height - size->y;
         loc_changed = TRUE;
      }

   }

   return loc_changed;
}


Point pcgWindow_Location( pcgWindow *self )
{
   Point location;

   struct Window *iwindow;

   if (iwindow = iWindow(self))
   {
      location.x = iwindow->LeftEdge;
      location.y = iwindow->TopEdge;
   }
   else
   {
      location = self->Location;
   }

   return location;
}


Point pcgWindow_Size( pcgWindow *self )
{
   Point size;

   struct Window *iwindow;

   if (iwindow = iWindow(self))
   {
      size.x = iwindow->Width;
      size.y = iwindow->Height;
   }
   else
   {
      size = self->Size;
   }

   return size;
}



Point pcgWindow_SetLocation( pcgWindow *self, WORD LeftEdge, WORD TopEdge )
{
   Point location, new_loc, size;
   WORD dx, dy;
   struct Window *iwindow;

   location  = Location(self);
   size      = Size(self);
   new_loc.x = LeftEdge;
   new_loc.y = TopEdge;

   pcgWindow_SanityCheck( self, &new_loc, &size );

   /*printf("pcgWindow_SetLocation\n");*/
   if (iwindow = iWindow(self))
   {

      dx = new_loc.x - location.x;
      dy = new_loc.y - location.y;

      MoveWindow( iwindow, dx, dy );

      self->Location.x = self->NewWindow.LeftEdge = iwindow->LeftEdge;
      self->Location.y = self->NewWindow.TopEdge  = iwindow->TopEdge;
   }
   else
   {
      self->Location.x = self->NewWindow.LeftEdge = new_loc.x;
      self->Location.y = self->NewWindow.TopEdge  = new_loc.y;
   }


   return self->Location;
}


Point pcgWindow_SetSize( pcgWindow *self, WORD width, WORD height )
{
   Point size, location, new_size;
   WORD dx, dy;
   struct Window *iwindow;

   new_size.x = width;
   new_size.y = height;
   location = Location(self);
   if (pcgWindow_SanityCheck( self, &location, &new_size ))
   {
      /* In order to set this size, you have to first move the window. */
      SetLocation( self, location.x, location.y );
   }

   /*printf("pcgWindow_SetSize\n");*/
   if (iwindow = iWindow(self))
   {
      /*printf("window is open.\n");*/

      size     = Size(self);
      dx = new_size.x - size.x;
      dy = new_size.y - size.y;
      /*printf("size.x,y = (%d,%d), dx,dy=(%d,%d)\n", size.x, size.y, dx, dy );*/
      SizeWindow( iwindow, dx, dy );
      self->NewWindow.Width  = self->Size.x = iwindow->Width;
      self->NewWindow.Height = self->Size.y = iwindow->Height;
   }
   else
   {
      self->Size.x = self->NewWindow.Width  = new_size.x;
      self->Size.y = self->NewWindow.Height = new_size.y;
   }

   return self->Size;
}


struct Window *pcgWindow_OpenWindow( pcgWindow *self )
{
   /*printf("pcgWindow_OpenWindow\n");*/

   if (self->Window) return self->Window;

   if (self->SharedUserPort)
      self->Window = OpenWindowWithSharedUserPort( 
         &self->NewWindow, self->SharedUserPort );
   else  /* normal open window. */
      self->Window = OpenWindow( &self->NewWindow );

   if (self->Window)
   {
      self->Window->UserData = (APTR) self;

      if (self->MenuStrip)
         SetMenuStrip( self->Window, self->MenuStrip );
   }
   return self->Window;
}



void pcgWindow_CloseWindow( pcgWindow *self )
{
   Window *w;
   Point size, loc;

   if (w = self->Window)
   {
      /* Record size and location (for possible reopen). */
      loc  = Location(self);
      size = Size(self);

      if (w->MenuStrip) ClearMenuStrip( w );

      if (self->SharedUserPort)
      {
         /* Cannot simply close a SharedUserPort window, because
          * Intution will deallocate the UserPort, and some other
          * applications may be using them.  (Instant GURU)
          */
         CloseWindowWithSharedUserPort( w );
      }
      else
         CloseWindow( w );

      self->Window = NULL;

      SetSize( self, size.x, size.y );
      SetLocation( self, loc.x, loc.y );
   }
}

pcgWindow *pcgWindow_InteractorWindow( pcgWindow *self ) { return self; }


Gadget *pcgWindow_FirstGadget( pcgWindow *self )
{
   Gadget *g;
   struct Window *iwindow;

   if (iwindow = iWindow(self))
   {
      g = iwindow->FirstGadget;
   }
   else
   {
      g = self->NewWindow.FirstGadget;
   }
   return g;
}


USHORT pcgWindow_nGadgets( pcgWindow *self )
{
   Gadget *g;
   USHORT count = 0;

   for (g = FirstGadget(self); g!=NULL; g=g->NextGadget )
      count++;

   return count;
}


ULONG pcgWindow_IDCMPFlags( pcgWindow *self )
{
   struct Window *iwindow;

   if (iwindow=iWindow(self))
      return iwindow->IDCMPFlags;
   else
      return self->NewWindow.IDCMPFlags;
}


USHORT pcgWindow_ClaimEvent( pcgWindow *self, IntuiMessage *event )
{
   struct Window *iwindow;
   USHORT response = 0;
   Interactor *iactor;

   if (iwindow = iWindow(self))
   {
      if (event->IDCMPWindow = iwindow)
      {
         response = RESPONDED;

         for (iactor = self->FirstInteractor; iactor!=NULL; iactor=iactor->Next)
         {
            response |= ClaimEvent(iactor, event);
            if (response & CONSUMED_EVENT)
               break;
         }
      }
   }
   return response;
}


void pcgWindow_AddGraphicObject( pcgWindow      *window,
                                 GraphicObject   *graphic )
{
   GraphicObject *g;

   if (window->FirstGraphic)
   {
      for (g = window->FirstGraphic;
           g->Next != NULL;
           g = g->Next );

      g->Next = graphic;
   }
   else
      window->FirstGraphic = graphic;
}

void pcgWindow_RemoveGraphicObject( pcgWindow      *window,
                                    GraphicObject  *graphic )
{
   GraphicObject *g;

   if (window->FirstGraphic)
   {
      if (window->FirstGraphic == graphic)
      {
         window->FirstGraphic = graphic->Next;
      }
      else
      {
         for(  g  = window->FirstGraphic;
               g != NULL;
               g  = g->Next )
         {
            if (g->Next == graphic)
            {
               g->Next = graphic->Next;
               break;
            }
         }
      }
   }
   graphic->Next = NULL;
}


void pcgWindow_AddInteractor( pcgWindow  *window,
                              Interactor   *interactor )
{
   Interactor    *iactor;
   ULONG          flags;
   struct Window *iwindow;

   SetInteractorWindow( interactor, window );

   if (nGadgets(interactor)) /* Interactor has gadget(s) */
   {
      if (iwindow = iWindow(window))
      {
         AddGList( iwindow, FirstGadget(interactor),
            (USHORT) ~0, nGadgets(interactor), NULL );
      }
      else
      {
         /* Add Gadgets to last gadget of window. */
         if (window->NewWindow.FirstGadget)
         {
            ChainGadgets( window->NewWindow.FirstGadget,
                          FirstGadget(interactor) );
         }
         else
            window->NewWindow.FirstGadget = FirstGadget(interactor);

      }
   }

   /* add this interactor to chain of interactors. */
   if (window->FirstInteractor)
   {
      for (iactor = window->FirstInteractor;
           iactor->Next != NULL;
           iactor = iactor->Next );

      iactor->Next = interactor;
   }
   else
      window->FirstInteractor = interactor;

   /* Add IDCMP flags */

   flags = IDCMPFlags( window ) | IDCMPFlags( interactor );
   SetIDCMPFlags( window, flags );

   /*printf("AddIActor: %s IDCMPFlags=%d\n",*/
   /*   ClassName( interactor ), IDCMPFlags(interactor) );*/
}


ULONG pcgWindow_SetIDCMPFlags( pcgWindow *self, ULONG newflags )
{
   struct Window *iwindow;
   self->NewWindow.IDCMPFlags = newflags;

   /*printf("SetIDCMPFlags: %d\n", newflags );*/
   if (iwindow = iWindow(self)) /* window is open */
   {
      ModifyIDCMP( iwindow, newflags );
   }

   return IDCMPFlags(self);
}



struct RastPort *pcgWindow_RPort( pcgWindow *self )
{
   if (self->Window)
      return self->Window->RPort;
   return NULL;
}



void pcgWindow_Refresh( pcgWindow *window )
{
   GraphicObject     *graphic;
   Interactor        *interactor;
   RastPort          *rport;
   struct Window     *iwindow;

   if (iwindow = iWindow(window))
   {
      RefreshWindowFrame(iwindow);

      rport = RPort( window );

      /* Draw window graphics */
      for( graphic = window->FirstGraphic;
           graphic != NULL;
           graphic = graphic->Next )
      {
         Render( graphic, rport );
      }

      /* Draw interactors */
      for ( interactor  = window->FirstInteractor;
            interactor != NULL;
            interactor  = interactor->Next )
      {
         Refresh( interactor );
      }

   }
}


BOOL pcgWindow_Activate( pcgWindow *self, BOOL activate )
{
   struct Window *iwindow;

   if (iwindow = iWindow(self))
   {
      if (activate)
      {
         ActivateWindow( iwindow );
      }
   }
   return isActive(self);
}



#define DONT_CHANGE_SCREEN_TITLE ((char*)-1)

BOOL pcgWindow_SetTitle( pcgWindow *self,
                         char      *title )
{
   struct Window *iwindow;
   char *t;
   
   Afree( Title(self) );
   t = Astrdup(title);
   
   if (iwindow = iWindow(self))
   {
      SetWindowTitles( iwindow, t, DONT_CHANGE_SCREEN_TITLE );
   }

   self->NewWindow.Title = t;
   return TRUE;
}

char *pcgWindow_Title( pcgWindow *self )
{
   return (char*) self->NewWindow.Title;
}

struct Window *pcgWindow_iWindow( pcgWindow *self )
{
   return self->Window;
}


struct Window *pcgOpenWindow( pcgWindow *self )
{
   struct pcgWindowClass *class;
   struct Window *result = NULL;

   /*printf("pcgOpenWindow\n");*/

   if (class = (struct pcgWindowClass *) self->isa)
   {
      if (class->OpenWindow)
         result = (*class->OpenWindow)( self );
   }

   if (result)
      Refresh(self);
      
   return result;
}


void pcgCloseWindow( pcgWindow *self )
{
   struct pcgWindowClass *class;

   if (class = (struct pcgWindowClass *) self->isa)
   {
      if (class->CloseWindow)
         (*class->CloseWindow)( self );
   }
}


struct Window *iWindow( pcgWindow *self )
{
   struct pcgWindowClass *class;
   struct Window *result = NULL;

   if (class = (struct pcgWindowClass *) self->isa)
   {
      if (class->iWindow)
         result = (*class->iWindow)( self );
   }
   return result;
}

RastPort *RPort( pcgWindow *self )
{
   struct pcgWindowClass *class;
   struct RastPort *result = NULL;

   if (class = (struct pcgWindowClass *) self->isa)
   {
      if (class->RPort)
         result = (*class->RPort)( self );
   }
   return result;
}


ULONG SetIDCMPFlags( pcgWindow *self, ULONG flags )
{
   struct pcgWindowClass *class;

   if (class = (struct pcgWindowClass *) self->isa)
   {
      if (class->SetIDCMPFlags)
         flags = (*class->SetIDCMPFlags)( self, flags );
   }
   return flags;
}


void AddGraphicObject( pcgWindow *self, GraphicObject *graphic )
{
   struct pcgWindowClass *class;

   if (class = (struct pcgWindowClass *) self->isa)
   {
      if (class->AddGraphicObject)
         (*class->AddGraphicObject)( self, graphic );
   }
}

void RemoveGraphicObject( pcgWindow *self, GraphicObject *graphic )
{
   struct pcgWindowClass *class;

   if (class = (struct pcgWindowClass *) self->isa)
   {
      if (class->RemoveGraphicObject)
         (*class->RemoveGraphicObject)( self, graphic );
   }
}

void AddInteractor( pcgWindow *self, Interactor *interactor )
{
   struct pcgWindowClass *class;

   if (class = (struct pcgWindowClass *) self->isa)
   {
      if (class->AddInteractor)
         (*class->AddInteractor)( self, interactor );
   }
}

void RemoveInteractor( pcgWindow *self, Interactor *interactor )
{
   struct pcgWindowClass *class;

   if (class = (struct pcgWindowClass *) self->isa)
   {
      if (class->RemoveInteractor)
         (*class->RemoveInteractor)( self, interactor );
   }
}

void AddWindowPObject( pcgWindow *window, GraphicObject *obj )
{
   if (isa(obj, InteractorClass()))
      AddInteractor(window, (Interactor*) obj);
   else
      AddGraphicObject(window, obj );
}

void RemoveWindowPObject( pcgWindow *window, GraphicObject *obj )
{
   if (isa(obj, InteractorClass()))
      RemoveInteractor(window, (Interactor*) obj);
   else
      RemoveGraphicObject(window, obj );
}



void AddMenuStrip( pcgWindow *self, Menu *menustrip )
{
   struct pcgWindowClass *class;

   if (class = (struct pcgWindowClass *) self->isa)
   {
      if (class->AddMenuStrip)
         (*class->AddMenuStrip)( self, menustrip );
   }
}

void RemoveMenuStrip( pcgWindow *self )
{
   struct pcgWindowClass *class;

   if (class = (struct pcgWindowClass *) self->isa)
   {
      if (class->RemoveMenuStrip)
         (*class->RemoveMenuStrip)( self );
   }
}

void pcgWindow_RemoveIactorGadgets( pcgWindow *self,
                                    Interactor *interactor )
{
   Gadget        *gadget, *prev_gadget, *next_gadget, *last_gadget;
   USHORT         i, ngadgets;
   struct Window *iwindow;

   if (gadget = FirstGadget(interactor))
   {
      ngadgets = nGadgets(interactor);
      if (iwindow = iWindow(self)) /* window is open. */
      {
         RemoveGList( iwindow, gadget, ngadgets );
      }
      else /* window is not open. */
      {
         next_gadget = gadget;

         for( i=0; i<ngadgets; i++)
         {
            last_gadget = next_gadget;
            next_gadget = next_gadget->NextGadget;
         }
         last_gadget->NextGadget = NULL;

         if (self->NewWindow.FirstGadget == gadget)
         {
            self->NewWindow.FirstGadget = next_gadget;
         }
         else
         {
            for( prev_gadget  = self->NewWindow.FirstGadget;
                 prev_gadget != NULL;
                 prev_gadget  = prev_gadget->NextGadget )
            {
               if (prev_gadget->NextGadget == gadget)
               {
                  prev_gadget->NextGadget = next_gadget;
                  break;
               }
            }
         }
      }
   }

}

void pcgWindow_RemoveInteractor( pcgWindow  *self,
                                 Interactor *interactor )
{
   Interactor    *iactor;
   ULONG          flags = 0;

   SetInteractorWindow( interactor, NULL );

   if (self->FirstInteractor)
   {
      if (self->FirstInteractor == interactor)
         self->FirstInteractor = interactor->Next;
      else
      {
         for(  iactor  = self->FirstInteractor;
               iactor != NULL;
               iactor  = iactor->Next )
         {
            if (iactor->Next == interactor)
            {
               iactor->Next = interactor->Next;
               pcgWindow_RemoveIactorGadgets( self, interactor ); 
               break;
            }
         }
      }
      interactor->Next = NULL;

   }


   /* Compute needed IDCMP flags w/o this responder.        */
   flags = self->IDCMPFlags;
   for(  iactor  = self->FirstInteractor;
         iactor != NULL;
         iactor  = iactor->Next )
      flags |= IDCMPFlags(iactor);

   SetIDCMPFlags( self, flags );
}

BOOL pcgWindow_EnableIactor( pcgWindow *self, BOOL enable )
{
   Window      *iwindow;
   Interactor  *iactor;
   Gadget      *gadget;
   Menu        *menu;
   UWORD        mcount;

   if (iwindow = self->Window)
   {
      iactor = self->FirstInteractor;
      gadget = (iactor) ? FirstGadget(iactor) : NULL;

      if (enable)
      {
         if (gadget)
            AddGList( iwindow, gadget, nGadgets(self), -1, NULL );

         SetIDCMPFlags( self, self->NewWindow.IDCMPFlags );
         /* enable menus. */
         for( menu  = iwindow->MenuStrip, mcount=0;
              menu != NULL;
              menu  = menu->NextMenu, mcount++ )
            OnMenu( iwindow, mcount | (NOITEM<<5) );

        ClearPointer( iwindow );

      }
      else /* disable it. */
      {
         if (gadget) 
            RemoveGList( iwindow, gadget, -1 );

         if (self->SharedUserPort)
         {
            /* Can't set IDCMPport to NULL if its shared. */
            ModifyIDCMP( iwindow, REFRESHWINDOW );
         }
         else
         {
            ModifyIDCMP( iwindow, 0 );
         }

         SetWaitPointer( iwindow );

         /* disable menus. */
         for( menu  = iwindow->MenuStrip, mcount=0;
              menu != NULL;
              menu  = menu->NextMenu, mcount++ )
            OffMenu( iwindow, mcount | (NOITEM<<5) );

      }
   }
   else return FALSE;
}


void pcgWindow_AddMenuStrip( pcgWindow *window, Menu *menustrip )
{
   if (window->MenuStrip) /* window already has a menu */
   {
      RemoveMenuStrip(window);
   }

   window->MenuStrip = menustrip;
   if (window->Window) /* Window is open */
   {
      SetMenuStrip(window->Window, menustrip);
   }
}


void pcgWindow_RemoveMenuStrip( pcgWindow *window )
{
   window->MenuStrip = NULL;
   if (window->Window) /* its open. */
   {
      ClearMenuStrip(window->Window);
   }
}

void pcgWindow_Erase( pcgWindow *self )
{
   Window    *window;
   RastPort  *rport;

   window = iWindow(self);
   rport  = RPort(self);

   SetDrMd ( rport, JAM2 );
   SetAPen ( rport, 0 );
   RectFill( rport, 0,0, window->Width, window->Height );
}



void pcgWindow_Render( pcgWindow *self, RastPort *dontcare )
{
   pcgWindow_Erase( self );
   Refresh ( self );
}

BOOL pcgWindow_elaborated = FALSE;

struct pcgWindowClass pcgWindow_Class;



void pcgWindowClass_Init( struct pcgWindowClass *class )
{
   InteractorClass_Init( (struct InteractorClass *) class );
   class->isa                 = InteractorClass();
   class->ClassName           = "pcgWindow";
   class->CleanUp             = NULL; 

   class->Location            = pcgWindow_Location;
   class->SetLocation         = pcgWindow_SetLocation;
   class->Size                = pcgWindow_Size;
   class->AskSize             = NULL;
   class->SetSize             = pcgWindow_SetSize;
   /*class->SizeFlags           = GraphicObject_SizeFlagsAll;*/
   class->Render              = pcgWindow_Render;
   class->InteractorWindow    = pcgWindow_InteractorWindow;
   class->SetInteractorWindow = NULL;
   class->FirstGadget         = pcgWindow_FirstGadget;
   class->nGadgets            = pcgWindow_nGadgets;
   class->IDCMPFlags          = pcgWindow_IDCMPFlags;
   class->ClaimEvent          = pcgWindow_ClaimEvent;
   class->Respond             = NULL;
   class->Refresh             = pcgWindow_Refresh;
   class->EnableIactor              = pcgWindow_EnableIactor;
/*   class->isEnabled           = pcgWindow_isEnabled;*/
   class->Activate            = pcgWindow_Activate;
   class->isActive            = NULL; /* pcgWindow_isActive;*/
   class->SetTitle            = pcgWindow_SetTitle;
   class->Title               = pcgWindow_Title;

   class->OpenWindow          = pcgWindow_OpenWindow;
   class->CloseWindow         = pcgWindow_CloseWindow;
   class->iWindow             = pcgWindow_iWindow;
   class->RPort               = pcgWindow_RPort;
   class->SetIDCMPFlags       = pcgWindow_SetIDCMPFlags;
   class->AddGraphicObject    = pcgWindow_AddGraphicObject;
   class->RemoveGraphicObject = pcgWindow_RemoveGraphicObject;
   class->AddInteractor       = pcgWindow_AddInteractor;
   class->RemoveInteractor    = pcgWindow_RemoveInteractor;
   class->AddMenuStrip        = pcgWindow_AddMenuStrip;
   class->RemoveMenuStrip     = pcgWindow_RemoveMenuStrip;
}


struct pcgWindowClass *pcgWindowClass( void )
{
   if (! pcgWindow_elaborated)
   {
      pcgWindowClass_Init( &pcgWindow_Class );
      pcgWindow_elaborated = TRUE;
   }

   return &pcgWindow_Class;
}



void pcgWindow_Init( pcgWindow     *self,
                     UWORD          leftedge,
                     UWORD          topedge,
                     UWORD          width,
                     UWORD          height,
                     UWORD          minwidth,
                     UWORD          minheight,
                     UWORD          maxwidth,
                     UWORD          maxheight,
                     char          *title,
                     ULONG          IDCMPFlags,
                     ULONG          flags,
                     struct Screen *screen )
{

   Interactor_Init( self );

   self->isa                     = pcgWindowClass();
   self->Window                  = NULL;
   self->FirstInteractor         = NULL;
   self->FirstGraphic            = NULL;
   self->SharedUserPort          = NULL;
   self->MenuStrip               = NULL;

   self->NewWindow.BlockPen      = 1;
   self->NewWindow.DetailPen     = 0;
   self->NewWindow.IDCMPFlags    = self->IDCMPFlags = IDCMPFlags;
   self->NewWindow.Flags         = flags;
   self->NewWindow.FirstGadget   = NULL;
   self->NewWindow.CheckMark     = NULL;
   self->NewWindow.Title         = NULL; 
   self->NewWindow.BitMap        = NULL;
   self->NewWindow.MinWidth      = minwidth;
   self->NewWindow.MinHeight     = minheight;
   self->NewWindow.MaxWidth      = maxwidth;
   self->NewWindow.MaxHeight     = maxheight;
   if (screen)
   {
      self->NewWindow.Type = CUSTOMSCREEN;
      self->NewWindow.Screen = screen;
   }
   else
   {
      self->NewWindow.Type   = WBENCHSCREEN;
      self->NewWindow.Screen = NULL;
   }

   /*
   ** Have to have an initial size before you can call
   ** SetSize or SetLocation
   */
   self->Location.x = self->NewWindow.LeftEdge = 0;
   self->Location.y = self->NewWindow.TopEdge  = 0;
   self->Size.x     = self->NewWindow.Width    = 0;
   self->Size.y     = self->NewWindow.Height   = 0;

   SetSize( self, width, height );
   SetLocation( self, leftedge, topedge );
   SetTitle( self, title );
}


