#include <string.h>
#include "minmax.h"
#include "ScrollingList.h"
#include "ScrollingListClass.h"
#include "EmbossedGadgetClass.h"
#include "precognition.h"
#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/graphics.h>
#include "amigamem.h"

#define CHAR_WIDTH  8   /* Width  of Topaz 80 font. */
#define CHAR_HEIGHT 9   /* Height of Topaz 80 font. */
#define BROWSER_ENTRY_HEIGHT (CHAR_HEIGHT + 1)
#define BORDER_WIDTH 3
#define BASELINE    7


void ScrollingList_SetKnobInfo( ScrollingList *self )
{
   USHORT position, knobsize, nElts, nRows;


   nElts = self->list.List.nEntries;

   if (nElts == 0)
   {
      position = 0;
      knobsize = 0xFFFF;
   }
   else
   {
      nRows = self->list.nRows;

      position  = ((ULONG) self->list.YOffset << 16) / nElts;

      if (nElts <= nRows)
      {
         knobsize = 0xFFFF;
      }
      else
      {
         knobsize = ((ULONG) nRows << 16) / nElts;
      }
   }
   SetKnobSize( &self->vscroller, knobsize );
   SetValue( &self->vscroller, position );
}


void ScrollingList_PositionList( ScrollingList *self )
{
   USHORT position,     /* Position of self->vscroller.   */
          new_YOffset,  /* new list YOffset.              */
          nElts,        /* # of elements in list.         */
          nRows,        /* # of rows in display           */
          range;        /* range of positions for YOffset */

   ULONG temp;

   nElts    = self->list.List.nEntries;

   if (nElts)
   {
      nRows    = self->list.nRows;

      if ((range = nElts - nRows +1) > 0)
      {
         position = Value( &self->vscroller );

         temp = position * range;
         new_YOffset = temp >>16;
         new_YOffset = MIN( new_YOffset, nElts-1);

         if (new_YOffset != self->list.YOffset)
         {
            self->list.YOffset = new_YOffset;
            Refresh( &self->list );
         }
      }
   }
}


void ScrollingList_Init(  ScrollingList *self,
                          PIXELS         LeftEdge,
                          PIXELS         TopEdge,
                          PIXELS         Width,
                          PIXELS         Height,
                          pcg_3DPens     Pens,
                          BOOL           SelectMany )
{
   Gadget *g;

   StringLister_Init( self );
   self->slister.isa  = ScrollingListClass();
   self->Pens         = Pens;

   ListBrowser_Init( &self->list, 0,0, 0,0, Pens, SelectMany );

   VScroller_Init( &self->vscroller, 0,0, Height, Pens, NULL );

   for ( g = FirstGadget( &self->list );
         g->NextGadget != NULL;
         g = g->NextGadget );

   g->NextGadget = FirstGadget( &self->vscroller );
   self->list.eg.Next = &self->vscroller;

   SetSize( self, Width, Height );
   SetLocation( self, LeftEdge, TopEdge );
}

void ScrollingList_CleanUp( ScrollingList *self )
{
   CleanUp( &self->list );
   CleanUp( &self->vscroller );
}

USHORT ScrollingList_nGadgets( ScrollingList *self )
{
   return nGadgets( &self->list ) + nGadgets( &self->vscroller );
}

Gadget *ScrollingList_FirstGadget( ScrollingList *self )
{
   return FirstGadget( &self->list );
}

Point ScrollingList_AskSize( ScrollingList *self,
                             PIXELS         Width,
                             PIXELS         Height )
{
   Point size, list_size, vscroller_size;

   vscroller_size = AskSize( &self->vscroller, Width, Height );
   /* vscroller's have a fixed width */

   list_size.x = Width - vscroller_size.x;
   list_size.y = vscroller_size.y;

   list_size = AskSize( &self->list, list_size.x, list_size.y );

   size.x = list_size.x + vscroller_size.x;
   size.y = list_size.y;

   return size;
}


Point ScrollingList_SetSize( ScrollingList *self,
                              PIXELS       Width,
                              PIXELS       Height )
{
   Point size, list_size, vscroller_size;

   size = AskSize( self, Width, Height );

   vscroller_size = SetSize( &self->vscroller, size.x, size.y );
   list_size  = SetSize( &self->list, size.x-vscroller_size.x, size.y );
   self->slister.Size = size;

   SetLocation( self, self->slister.Location.x, self->slister.Location.y );
   ScrollingList_SetKnobInfo( self );

   return size;
}

Point ScrollingList_SetLocation( ScrollingList *self,
                                  PIXELS         LeftEdge,
                                  PIXELS         TopEdge )
{
   Point size, location;

   size = Size( &self->list );
   SetLocation( &self->list, LeftEdge, TopEdge );
   SetLocation( &self->vscroller, LeftEdge+size.x, TopEdge );

   location.x             = LeftEdge;
   location.y             = TopEdge;
   self->slister.Location = location;

   return location;
}


USHORT ScrollingList_SetYOffset( ScrollingList *self,
                                 USHORT         YOffset )
{
   return ListBrowser_SetYOffset( &self->list, YOffset );
}


void ScrollingList_Render( ScrollingList  *self,
                           RastPort       *RPort )
{
   Render( &self->list,       RPort );
   Render( &self->vscroller,  RPort );
}


void ScrollingList_SelectAll( ScrollingList *self, BOOL Select )
/* Selects or deselects everything in the list. */
{
   ListBrowser_SelectAll( &self->list, Select );
}


const StringList *ScrollingList_StringList_of( ScrollingList *self )
{
   return StringList_of( &self->list );
}

void ScrollingList_AddString( ScrollingList  *self,
                              char           *entry,
                              UBYTE           qualifier )
{
   AddString( &self->list, entry, qualifier );
   ScrollingList_SetKnobInfo( self );
}


void ScrollingList_DeleteString( ScrollingList  *self,
                                 USHORT          entry )
{
   DeleteString( &self->list, entry );
   ScrollingList_SetKnobInfo( self );
}

void ScrollingList_DeleteAllStrings( ScrollingList  *self )
{
   DeleteAllStrings( &self->list );
   ScrollingList_SetKnobInfo( self );
}

void ScrollingList_SelectString( ScrollingList  *self,
                                 USHORT          entry,
                                 BOOL            Select   )
{
   SelectString( &self->list, entry, Select );
}


void ScrollingList_Refresh( ScrollingList *self )
{
   Refresh( &self->list      );
   Refresh( &self->vscroller );
}

USHORT ScrollingList_ClaimEvent(  ScrollingList  *self,
                                 IntuiMessage   *event )
{
   return ClaimEvent( &self->list,event )
        | ClaimEvent( &self->vscroller, event );
}


#define CLAIM_EVENT(b,e) (&b->g == (Gadget *) e->IAddress)

USHORT ScrollingList_Respond( ScrollingList  *self,
                              IntuiMessage   *Event )
{
   USHORT        list_response;
   USHORT        vscroller_response = 0;

   list_response = Respond( &self->list, Event );

   if (! (list_response & CONSUMED_EVENT))
   {
      vscroller_response = Respond( &self->vscroller, Event );
      if (vscroller_response & CHANGED_STATE)
      {
         ScrollingList_PositionList( self );
         /*  scroll list */
         vscroller_response = RESPONDED; /* Don't propagate the CHANGED_STATE. */
      }
   }
   return list_response | vscroller_response;
}


void ScrollingList_SetInteractorWindow( ScrollingList *self,
                                        pcgWindow     *window )
{
   self->slister.IaWindow = window;
   SetInteractorWindow( &self->list, window );
   SetInteractorWindow( &self->vscroller, window );
}

ULONG ScrollingList_IDCMPFlags( ScrollingList *self )
{
   return IDCMPFlags( &self->list ) | IDCMPFlags( &self->vscroller );
}

BOOL ScrollingList_EnableIactor( ScrollingList *self, BOOL enable )
{
   EnableIactor( &self->list,      enable );
   EnableIactor( &self->vscroller, enable );
   return enable;
}

#ifdef BUILDER
#include "BuilderMethods.h"
#include "GraphicObject_Builder.h"
#include "ScrollingList_Builder.h"
#include "ScrollingList_Coder.h"

void ScrollingList_PropEdit( ScrollingList *self,
                             pcgWindow     *window,
                             pcg_3DPens     pens );

ScrollingList *ScrollingList_New( ScrollingList *self )
{
   ScrollingList *new_list = NULL;
   Point loc, size;

   if (new_list = (ScrollingList*) Amalloc(sizeof(ScrollingList)))
   {
      loc  = Location(self);
      size = Size(self);

      ScrollingList_Init( new_list, loc.x, loc.y,
         size.x, size.y, self->Pens, self->list.SelectMany );
      GiveItAName( new_list );

      StringList_Dup( &self->list.List, &new_list->list.List );

   }
   return new_list;
}

struct BuilderMethods ScrollingList_bm;

#endif

BOOL ScrollingList_elaborated = FALSE;

struct StringListerClass ScrollingList_Class;

void ScrollingListClass_Init( struct StringListerClass *class )
{
   StringListerClass_Init( class );
   class->isa         = StringListerClass();
   class->CleanUp     = ScrollingList_CleanUp;
   class->ClassName   = "ScrollingList";
   class->AskSize     = ScrollingList_AskSize;
   class->SetSize     = ScrollingList_SetSize;
   class->SetLocation = ScrollingList_SetLocation;
   class->Respond     = ScrollingList_Respond;
   class->Refresh     = ScrollingList_Refresh;
   class->Render      = ScrollingList_Render;
   class->ClaimEvent  = ScrollingList_ClaimEvent;
   class->nGadgets    = ScrollingList_nGadgets;
   class->FirstGadget = ScrollingList_FirstGadget;
   class->SetInteractorWindow = ScrollingList_SetInteractorWindow;
   class->IDCMPFlags  = ScrollingList_IDCMPFlags;
   class->isEnabled   = EmbossedGadget_isEnabled;
   class->EnableIactor      = ScrollingList_EnableIactor;

   class->StringList_of = ScrollingList_StringList_of;
   class->AddString     = ScrollingList_AddString;
   class->DeleteString  = ScrollingList_DeleteString;
   class->DeleteAllStrings = ScrollingList_DeleteAllStrings;
   class->SelectString  = ScrollingList_SelectString;

#ifdef BUILDER
   go_InitBuilderMethods( &ScrollingList_bm );

   ScrollingList_bm.New       = ScrollingList_New;
   ScrollingList_bm.PropEdit  = ScrollingList_PropEdit;
   ScrollingList_bm.WriteCode = ScrollingList_WriteCode;
   class->BuilderMethods      = &ScrollingList_bm;
#endif
}



struct StringListerClass *ScrollingListClass( void )
{
   if (! ScrollingList_elaborated)
   {
      ScrollingListClass_Init( &ScrollingList_Class );
      ScrollingList_elaborated = TRUE;
   }

   return &ScrollingList_Class;
}
