/****************************************************************************
 *	ColorSaver  Version 0.84
 *
 *	05 January 1993
 *	Compiled with DICE
 *
 *	Copyright © 1993 By Dan Fish
 *	All rights reserved.
 *
 *	Permission is granted to freely redistribute this program provided 
 *	the source code and documentation is included in the distribution
 *	and this copyright notice is unchanged.
 *
 ****************************************************************************/

#include <exec/types.h>
#include <exec/memory.h>
#include <exec/errors.h>
#include <dos/dos.h>
#include <clib/macros.h>
#include <fcntl.h>
#include <libraries/gadtools.h>
#include <libraries/commodities.h>
#include <libraries/asl.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <intuition/classes.h>
#include <intuition/gadgetclass.h>
#include <devices/printer.h>
#include <workbench/startup.h>
#include <workbench/icon.h>
#include <workbench/workbench.h>

#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>

#include <clib/alib_protos.h>
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/commodities_protos.h>
#include <clib/gadtools_protos.h>
#include <clib/graphics_protos.h>
#include <clib/asl_protos.h>
#include <clib/utility_protos.h>
#include <clib/intuition_protos.h>
#include <clib/alib_protos.h>
#include <clib/wb_protos.h>

#include "CSgads.h"       		/* GadToolsBox file 	*/
#include "Protos.h"			/* Routine prototypes	*/

#define GAD(x)	ColorSaverGadgets[x]

/*
 * An easy way to switch the commodity on or off
 */
#define CxOn( b )           ActivateCxObj( b, TRUE )
#define CxOff( b )          ActivateCxObj( b, FALSE );

/*
 * The following strings represent the
 * possible tooltypes of the ColorSaver icon.
 */
UBYTE   *CX_PRIORITY        =       "CX_PRIORITY";
UBYTE   *CX_POPUP           =       "CX_POPUP";
UBYTE   *CX_POPKEY          =       "CX_POPKEY";
UBYTE   *CX_HSV 	    =       "HSV";
UBYTE   *CX_QUIT            =       "QUIT";

/*
 * Following are the default ColorSaver
 * settings which can be changed with
 * the command line or the icon tooltypes.
 */
#define  CX_DEFPRI                  0
UBYTE   *CX_DEFPOPKEY       =       "lcommand lshift c";
UBYTE   *CX_DEFPOPUP        =       "YES";
UBYTE   *CX_DEFMODE         =       "RGB";
UBYTE   *CX_DEFQUIT         =       "lcommand lshift q";

/*
 * Two HotKey events.
 */
#define  CX_SHOW            1L                      /* show window */
#define  CX_SHUTUP          2L                      /* quit ColorSaver */

/*
 * Miscellaneous program information.
 */
#define  CSVersion          "v0.84"
#define  CSName             "ColorSaver"
#define  CSDescr            "Color Palette Tool"
#define  CSCopy             "© 1993 Dan Fish"
#define  CSTitle            CSName " " CSVersion ", " CSCopy

struct MsgPort              *CSComPort = NULL;      /* commodity port */
ULONG                        CSMask    = NULL;      /* the port bit-mask */
UBYTE                       *CSTTypes  = NULL;      /* the tooltype array */
ULONG                       *CSArgs;                /* shell args array */
CxObj                       *CSBroker  = NULL;      /* the broker */

/*
 * The NewBroker structure defining
 * some important information for
 * the commodities.library and Exchange.
 */
struct NewBroker             CSNBrok  = {
    NB_VERSION, CSName, CSTitle, CSDescr,
    NBU_NOTIFY | NBU_UNIQUE, COF_SHOW_HIDE, NULL, 0
};

/*
 * DCBack required data. DCBack is a tiny link library
 * written by Jan van den Baard to make it possible to write
 * auto-detachable programs with DICE. DCBack also parses the
 * argument line for you using ReadArgs().
 */

UBYTE                       *_procname      = CSName "_" CSVersion;
UBYTE                       *_template      = "CX_PRIORITY/K/N,CX_POPUP/K,CX_POPKEY/K,QUIT/K,HSV/S";
UBYTE                       *_exthelp       = NULL;
LONG                         _stack         = 2048L;
LONG                         _priority      = NULL;
LONG                         _BackGroundIO  = NULL;

/*
 * Shell version string.
 */
static UBYTE CsVer[]        =       "$VER: ColorSaver 0.9 (05-Dec-92)";

/*
 * Required libraries that are not in DICE's
 * auto-init library.
 */
struct Library              *CxBase        = NULL;
struct Library              *IconBase      = NULL;
struct Library              *WorkbenchBase = NULL;

/*
 * The following libraries are all auto-init.
 */
extern struct IntuitionBase        *IntuitionBase;
extern struct UtilityBase          *UtilityBase;
extern struct GadToolsBase         *GadToolsBase;

/*
 * Some other useful global data
 */

#define RESETCOLOR	0		/* default palette selection	*/

struct Screen 		*Scr;			/* screen we open up on	*/
struct WBStartup        *WbMsg      = NULL;     /* workbench message	*/
struct MsgPort          *CSIdPort   = NULL;     /* IDCMP port		*/
UBYTE                   *CSPopkey;              /* popkey string ptr	*/
ULONG                    CSIdMask   = NULL;     /* IDCMP port bit-mask	*/
UBYTE                    CSIsOpen   = FALSE;    /* window open? 	*/
UBYTE                    CSPop      = TRUE;     /* open on startup?	*/
UBYTE                    CSWTitle[80];          /* window title 	*/
UWORD 			 RedVal;		/* RGB red value	*/
UWORD 			 GreenVal;		/* RGB green value	*/
UWORD 			 BlueVal;		/* RGB blue value	*/
UWORD 			 HueLevel;		
UWORD 			 SatLevel;
UWORD 			 ValLevel;
ULONG			 NumColors;		/* # of colors in palette   */
SHORT			 CurrentColor=0;	/* Currently selected color */
USHORT              	*ResetPal;		/* Palette w/window first opens */
USHORT              	*SavePal;         	/* Snapshot of current palette*/
ULONG 			*OffSetABase = NULL;    /* ptr to file offsets	    */
UBYTE		       **OffSetSBase = NULL; 	/* ptr to file offset strings   */
SHORT		     	 OffSetCount = 0;	/* number of offsets found  */
UWORD			 CycleSelect = 0;	/* ordinal value of cycle gad */
UBYTE   		 ScanFileSpec[256];	/* filename for binary scan */
BOOL			 RGBMode = TRUE;	/* False if in HSV mode	    */


/*
 * ASL requester tags & data.
 */

#define FILESAVE	0
#define FILELOAD	1
#define FILESCAN	2
#define DEFAULT_DATA_DIR  "DEVS:"

UBYTE   LoadFile[ 32 ];		
UBYTE   ScanFile[ 32 ];		
UBYTE   LoadDir[224];		
UBYTE   ScanDir[224];		
UBYTE   FileSpec[256];

/* CAUTION! ASL_Window in the following structures is position sensitive */
/* due to initialization in FileSelect().  It must be the first item 	 */
/* in the TagItem structure.						 */

struct TagItem	SaveTags[] = {
    ASL_Window,		NULL,  			/* the window 		*/
    ASL_Hail,		"Save File:",		/* Function text	*/
    ASL_File,		LoadFile,		/* file name buffer 	*/
    ASL_Dir,		LoadDir,		/* path name buffer 	*/
    ASL_OKText,		"Save",			/* "OK" Text		*/
    TAG_DONE };

struct TagItem	LoadTags[] = {
    ASL_Window,		NULL,  			/* the window		*/
    ASL_Hail,		"Load File:",		/* Function text	*/
    ASL_File,		LoadFile,		/* file name buffer 	*/
    ASL_Dir,		LoadDir,		/* path name buffer	*/
    ASL_OKText,		"Load",			/* "OK" Text		*/
    TAG_DONE };

struct TagItem	ScanTags[] = {
    ASL_Window,		NULL,  			/* the window 		*/
    ASL_Hail,		"Scan File:",		/* Function text	*/
    ASL_File,		ScanFile,		/* file name buffer 	*/
    ASL_Dir,		ScanDir,		/* path name buffer 	*/
    ASL_OKText,		"Scan",			/* "OK" Text		*/
    TAG_DONE };

struct TagItem	*FileTags[] = {	SaveTags,
				LoadTags,
				ScanTags	};

/*
 * Open up all required resources. Check the
 * arguments passed to ColorSaver from the argument
 * line if run from the shell or the tool types
 * when run from the workbench.
 */
void SetUpCS( void )
{
    CxObj           *tmp;
    UBYTE           *quitkey, *popup;
    WORD             priority;
    LONG             error;
    UBYTE 	    *str;

    /*  Initialize all the file names/paths	*/

    LoadFile[0] = '\0';
    ScanFile[0] = '\0';
    ScanDir[0]  = '\0';

    if((str = getenv("CS_PALETTES")) != NULL)
      strcpy(LoadDir,str);
    else
      strcpy(LoadDir,DEFAULT_DATA_DIR);	


    if ( ! ( CxBase = OpenLibrary( "commodities.library", 37L ))) {
        error = 20L;
    	CloseDownCS( error );
    }

   /*
    * This one is required by the Argxxxx()
    * routines from the amigas20.lib.
    */
    if ( ! ( IconBase = OpenLibrary( "icon.library", 34L ))) {
        error = 21L;
    	CloseDownCS( error );
    }

    if ( ! ( WorkbenchBase = OpenLibrary( "workbench.library", 37L ))) {
        error = 22L;
    	CloseDownCS( error );
    }

    if ( WbMsg ) {
       /*
        * When WbMsg is non-null it means that
        * ColorSaver was started from the workbench.
        */
        CSTTypes = ( UBYTE * )ArgArrayInit( NULL, ( UBYTE ** )WbMsg );

        priority = ArgInt(( char ** )CSTTypes, CX_PRIORITY, CX_DEFPRI );
        popup    = ArgString(( char ** )CSTTypes, CX_POPUP,    CX_DEFPOPUP  );
        CSPopkey = ArgString(( char ** )CSTTypes, CX_POPKEY,   CX_DEFPOPKEY );
        quitkey  = ArgString(( char ** )CSTTypes, CX_QUIT,     CX_DEFQUIT   );
        if( ArgString(( char ** )CSTTypes, CX_HSV,NULL))
	    RGBMode = FALSE;

    } else {
       /*
        * Otherwise ColorSaver was started from the shell.
        */
        if ( CSArgs[ 0 ] )  priority = ( WORD )*(( ULONG * )CSArgs[ 0 ] );
        else                priority = CX_DEFPRI;
        if ( CSArgs[ 1 ] )  popup    = ( UBYTE * )CSArgs[ 1 ];
        else                popup    = CX_DEFPOPUP;
        if ( CSArgs[ 2 ] )  CSPopkey = ( UBYTE * )CSArgs[ 2 ];
        else                CSPopkey = CX_DEFPOPKEY;
        if ( CSArgs[ 3 ] )  quitkey  = ( UBYTE * )CSArgs[ 3 ];
        else                quitkey  = CX_DEFQUIT;
        if ( CSArgs[ 4 ] )  RGBMode  = FALSE;


    }

    CSNBrok.nb_Pri = priority;

    if ( Stricmp( popup, CX_DEFPOPUP )) CSPop = FALSE;

    if ( CSComPort = CreateMsgPort()) {

        CSMask          = ( 1L << CSComPort->mp_SigBit );
        CSNBrok.nb_Port = CSComPort;

        if ( CSBroker = CxBroker( &CSNBrok, NULL )) {
            if ( tmp = HotKey( CSPopkey, CSComPort, CX_SHOW )) {

                AttachCxObj( CSBroker, tmp );

                if ( tmp = HotKey( quitkey, CSComPort, CX_SHUTUP )) {

                    AttachCxObj( CSBroker, tmp );

                    if ( ! CxObjError( CSBroker )) {
                        CxOn( CSBroker );
                        return;  
                    } else
                        error = 24L;
                } else
                    error = 25L;
            } else
                error = 26L;
        } else
            error = 27L;
    } else
        error = 28L;

CloseDownCS( error );

}

/*
 * Close down and deallocate all resources
 * taken from the system.
 */
void CloseDownCS( __D0 LONG code )
{
    struct Message      *tmp;

    if ( CSBroker )         DeleteCxObjAll( CSBroker );
    if ( CSComPort ) {
        while ( tmp = GetMsg( CSComPort )) ReplyMsg( tmp );
        DeleteMsgPort( CSComPort );
    }
    if ( CSTTypes)          ArgArrayDone();
    if ( WorkbenchBase )    CloseLibrary( WorkbenchBase );
    if ( IconBase )         CloseLibrary( IconBase );
    if ( CxBase )           CloseLibrary( CxBase );

    exit( code );
}

/*
 * Put up a simple requester.
 */
long Req( UBYTE *gadgets, UBYTE *body, ... )
{
    static struct EasyStruct req = {
        sizeof( struct EasyStruct ), NULL, NULL, NULL, NULL };
    va_list                  args;
    LONG                     rc;

    va_start( args, body );

    req.es_Title        = "ColorSaver Info";
    req.es_TextFormat   = body;
    req.es_GadgetFormat = gadgets;

    rc = EasyRequestArgs( ColorSaverWnd, &req, NULL, args );

    va_end( args );

    return( rc );
}


/*
 * The main program loop which wait for events from
 * the different ports that are setup by the program.
 */
void EventHandler( void )
{
    struct Message          *msg;
    struct IntuiMessage     *imsg;
    struct Gadget           *gad;
    ULONG                    sig, type, id, class;
    UWORD                    code;
    UBYTE                    running = TRUE;

   /*
    * Open ColorSaver window when
    * CX_POPUP is YES.
    */
    if ( CSPop ) OpenTheWindow();
    do {
       /*
        * Wait for the commodity and (if open) the window ports.
        */
        sig = Wait( CSMask | CSIdMask );

        if (( sig & CSMask ) == CSMask ) {
           /*
            * A commodity message came through.
            */
            while ( msg = GetMsg( CSComPort )) {

                id      = CxMsgID(( CxMsg * )msg );
                type    = CxMsgType(( CxMsg * )msg );
                ReplyMsg( msg );

                switch ( type ) {

                    case    CXM_IEVENT:
                        switch ( id ) {

                            case    CX_SHOW:
                                OpenTheWindow();
                                break;

                            case    CX_SHUTUP:
                                running = FALSE;
                                break;
                        }
                        break;

                    case    CXM_COMMAND:
                        switch ( id )  {

                            case    CXCMD_KILL:
                                running = FALSE;
                                break;

                            case    CXCMD_DISABLE:
                                CxOff( CSBroker );
                                break;

                            case    CXCMD_ENABLE:
                                CxOn( CSBroker );
                                break;

                            case    CXCMD_UNIQUE:
                            case    CXCMD_APPEAR:
                                OpenTheWindow();
                                break;

                            case    CXCMD_DISAPPEAR:
                                CloseTheWindow();
                                break;
                        }
                        break;
                }
            }
        }

        if ( CSIsOpen )  {
            if (( sig & CSIdMask ) == CSIdMask ) {
               /*
                * A window message came through.
                */
                while ( CSIdPort && ( imsg = GT_GetIMsg( CSIdPort ))) {

                    class   =   imsg->Class;
                    code    =   imsg->Code;
                    gad     =   ( struct Gadget * )imsg->IAddress;
                    GT_ReplyIMsg( imsg );

                    switch ( class ) {

                        case    IDCMP_REFRESHWINDOW:
                            GT_BeginRefresh( ColorSaverWnd );
                            GT_EndRefresh( ColorSaverWnd, TRUE );
                            break;

                        case    IDCMP_CLOSEWINDOW:
                            CloseTheWindow();
                            break;

                        case    IDCMP_CHANGEWINDOW:
                            ColorSaverLeft = ColorSaverWnd->LeftEdge;
                            ColorSaverTop  = ColorSaverWnd->TopEdge;
                            break;

                        case    IDCMP_GADGETUP:
                        case    IDCMP_GADGETDOWN:
                        case    IDCMP_MOUSEMOVE:
                	    HandleGadgetEvent(gad,code);
                   	    break;

                        case IDCMP_VANILLAKEY:
                            HandleVanillaKey(code);
                            break;
                    }
                }
            }
        }

    } while ( running );

    QuitIt:

    CloseTheWindow();
    CxOff( CSBroker );
}


VOID HandleGadgetEvent( struct Gadget *gad, UWORD code)
{

static BOOL copymode;
static BOOL swapmode;
static BOOL rangemode;
static SHORT copycolor;
static UBYTE swapred;
static UBYTE swapgreen;
static UBYTE swapblue;

switch (gad->GadgetID)
    {
      
    case  GD_RED_GAD:
	if(!RGBMode)
          {
	   HueLevel = code; 
  	   CalcRGB();
          }
  	else
	   RedVal = code;

	SetRGB4(&Scr->ViewPort,CurrentColor,RedVal,GreenVal,BlueVal);
        break;

    case  GD_GREEN_GAD:
	if(!RGBMode)
          {
	   SatLevel = code;
  	   CalcRGB();
          }
	else
	   GreenVal=code;

	SetRGB4(&Scr->ViewPort,CurrentColor,RedVal,GreenVal,BlueVal);
        break;

    case  GD_BLUE_GAD:
	if(!RGBMode)
          {
	   ValLevel=code;
  	   CalcRGB();
          }
	else
	   BlueVal=code;

	SetRGB4(&Scr->ViewPort,CurrentColor,RedVal,GreenVal,BlueVal);
        break;

    case  GD_PALETTE_GAD:
	CurrentColor=(SHORT)code;
        if(copymode)
          {
	   GetColors(copycolor);
	   SetRGB4(&Scr->ViewPort,CurrentColor,RedVal,GreenVal,BlueVal);	
 	  }
	else if(swapmode)
          {
	   GetColors(CurrentColor);
	   swapred = RedVal;
	   swapgreen = GreenVal;
	   swapblue = BlueVal;
	   GetColors(copycolor);
	   SetRGB4(&Scr->ViewPort,CurrentColor,RedVal,GreenVal,BlueVal);	
	   SetRGB4(&Scr->ViewPort,copycolor,swapred,swapgreen,swapblue);	
 	  }
	else if(rangemode)
	   ColorRange(copycolor,CurrentColor);

	SetProps(CurrentColor);
    	copymode = swapmode = rangemode = FALSE;
        break;

    case  GD_COMP_GAD:
	SetRGB4(&Scr->ViewPort,CurrentColor,~RedVal,~GreenVal,~BlueVal);	
	SetProps(CurrentColor);
        break;

    case  GD_COPY_GAD:
	copycolor = CurrentColor;
        copymode = TRUE;
        break;

    case  GD_SWAP_GAD:
	copycolor = CurrentColor;
        swapmode = TRUE;
        break;

    case  GD_RANGE_GAD:
	copycolor = CurrentColor;
        rangemode = TRUE;
        break;

    case  GD_SLEFT_GAD:
	ShiftColorsLeft();
	SetProps(CurrentColor);
        break;

    case  GD_SRIGHT_GAD:
	ShiftColorsRight();
	SetProps(CurrentColor);
        break;

    case  GD_RESET_GAD:
	Reset();
        break;

    case  GD_LOAD_GAD:
	if(FileSelect(FILELOAD,LoadDir,LoadFile))
	   LoadColors(FileSpec, NumColors );
	SetProps(CurrentColor);
        break;

    case  GD_SAVE_GAD:
        CopyMem((void *)Scr->ViewPort.ColorMap->ColorTable,(void *)SavePal,64L);
	if(FileSelect(FILESAVE,LoadDir,LoadFile))
	   SaveColors(FileSpec, SavePal, NumColors );
        break;

    case  GD_OKAY_GAD:
        CloseTheWindow();
        break;

    case  GD_DO_GAD:
        CopyMem((void *)Scr->ViewPort.ColorMap->ColorTable,(void *)ResetPal,64L);
        break;

    case  GD_CANCEL_GAD:
	Reset();
        CloseTheWindow();
        break;

    case  GD_SCAN_GAD:
	if(FileSelect(FILESCAN,ScanDir,ScanFile))
           FindTable(FileSpec, ResetPal, NumColors);
        break;

    case  GD_CYCLE_GAD:
        CycleSelect = code;
        break;

    case  GD_WRITE_GAD:
        CopyMem((void *)Scr->ViewPort.ColorMap->ColorTable,(void *)SavePal,64L);
        WriteTable(ScanFileSpec, SavePal, NumColors);
        break;

    case  GD_EXTRA_GAD:
	About();
        break;

    case GD_RGBHSV_GAD:
	if (code == 0)
	  RGBMode = TRUE;
        else
	  RGBMode = FALSE;

	SetColorMode(RGBMode);
	SetProps(CurrentColor);
        break;

    
    }
}

/*
 * Function to handle vanilla keys.
 */
VOID HandleVanillaKey( UWORD code )
{
  switch (code)
    {
    case 'U':
    case 'u':
	Reset();
        break;

    case 'L':
    case 'l':
	if(FileSelect(FILELOAD,LoadDir,LoadFile))
	   LoadColors(FileSpec, NumColors );
	SetProps(CurrentColor);
        break;

    case 'S':
    case 's':
        CopyMem((void *)Scr->ViewPort.ColorMap->ColorTable,(void *)SavePal,64L);
	if(FileSelect(FILESAVE,LoadDir,LoadFile))
	   SaveColors(FileSpec, SavePal, NumColors );
        break;

    case 'O':
    case 'o':
        CloseTheWindow();
        break;

    case 'C':
    case 'c':
	Reset();
        CloseTheWindow();
        break;

    }
}


/*
 * Open up the main window and get
 * the pointer to the userport and
 * the port it's bit mask.
 */
void OpenTheWindow( void )
{

    if ( ! CSIsOpen ) {

        CSIsOpen = TRUE;

        Scr = IntuitionBase->FirstScreen;   

        if ( SetupScreen()) {
            CloseTheWindow();
            return;
        }

   	if(!(ResetPal = AllocMem(64L,MEMF_PUBLIC)))
          return;

   	if(!(SavePal = AllocMem(64L,MEMF_PUBLIC)))
          return;

        CopyMem((void *)Scr->ViewPort.ColorMap->ColorTable,(void *)ResetPal,64L);
        NumColors = (1L << Scr->BitMap.Depth);

        strcpy( CSWTitle, CSTitle);
	ColorSaverWdt = &CSWTitle[0];
	
 
        if ( OpenColorSaverWindow()) {
            CloseTheWindow();
            return;
        }

	SetColorMode(RGBMode);
	SetPaletteColor(RESETCOLOR);
	SetProps(RESETCOLOR);
	CurrentColor=RESETCOLOR;

        CSIdPort = ColorSaverWnd->UserPort;
        CSIdMask = ( 1L << CSIdPort->mp_SigBit );

        return;
    }
}

/*
 * Set the slider props to the specified color number
 */
void SetProps(SHORT num)
{

 GetColors(num);
 if(RGBMode)
  {
   GT_SetGadgetAttrs(GAD(GD_RED_GAD),  ColorSaverWnd,NULL,GTSL_Level,(WORD)RedVal,TAG_END);
   GT_SetGadgetAttrs(GAD(GD_GREEN_GAD),ColorSaverWnd,NULL,GTSL_Level,(WORD)GreenVal,TAG_END);
   GT_SetGadgetAttrs(GAD(GD_BLUE_GAD), ColorSaverWnd,NULL,GTSL_Level,(WORD)BlueVal,TAG_END);
  }
 else
  {
   CalcHSV(RedVal,GreenVal,BlueVal);
   GT_SetGadgetAttrs(GAD(GD_RED_GAD),  ColorSaverWnd,NULL,GTSL_Level,(WORD)HueLevel,TAG_END);
   GT_SetGadgetAttrs(GAD(GD_GREEN_GAD),ColorSaverWnd,NULL,GTSL_Level,(WORD)SatLevel,TAG_END);
   GT_SetGadgetAttrs(GAD(GD_BLUE_GAD), ColorSaverWnd,NULL,GTSL_Level,(WORD)ValLevel,TAG_END);
  }
}

/*
 * Get the current color bits for the specified color number
 */
void GetColors(SHORT num)
{
 UWORD col;
 
 col=GetRGB4(Scr->ViewPort.ColorMap,(LONG)num);
 RedVal  = (col >> 8) & 0x0F;
 GreenVal= (col >> 4) & 0x0F;
 BlueVal = (col     ) & 0x0F;

}


/*
 * Set the color palette to the specified color number
 */
void SetPaletteColor(SHORT color)
{
   GT_SetGadgetAttrs(GAD(GD_PALETTE_GAD), ColorSaverWnd,NULL,GTPA_Color,
	color,TAG_END);   
}


/*
 * Reset to the colors present when ColorSaver was opened
 */
void Reset()
{
   LoadRGB4(&Scr->ViewPort,ResetPal,1L << Scr->BitMap.Depth);
   SetProps(CurrentColor);
}

/*
 * Set the Color Mode to either RGB or HSV
 */
void SetColorMode(BOOL rgb)
{
 if(rgb)
  {
   GT_SetGadgetAttrs(GAD(GD_RGBHSV_GAD),ColorSaverWnd,NULL,GTMX_Active,(UWORD)0,TAG_END);

   GT_SetGadgetAttrs(GAD(GD_RGBR_GAD),ColorSaverWnd,NULL,GTTX_Text,"R",TAG_END);
   GT_SetGadgetAttrs(GAD(GD_RGBG_GAD),ColorSaverWnd,NULL,GTTX_Text,"G",TAG_END);
   GT_SetGadgetAttrs(GAD(GD_RGBB_GAD),ColorSaverWnd,NULL,GTTX_Text,"B",TAG_END);

   GT_SetGadgetAttrs(GAD(GD_RED_GAD),ColorSaverWnd,NULL,GTSL_Max,15,TAG_END);
   GT_SetGadgetAttrs(GAD(GD_GREEN_GAD),ColorSaverWnd,NULL,GTSL_Max,15,TAG_END);
   GT_SetGadgetAttrs(GAD(GD_BLUE_GAD),ColorSaverWnd,NULL,GTSL_Max,15,TAG_END);
  }
 else
  {
   GT_SetGadgetAttrs(GAD(GD_RGBHSV_GAD),ColorSaverWnd,NULL,GTMX_Active,(UWORD)1,TAG_END);

   GT_SetGadgetAttrs(GAD(GD_RGBR_GAD),ColorSaverWnd,NULL,GTTX_Text,"H",TAG_END);
   GT_SetGadgetAttrs(GAD(GD_RGBG_GAD),ColorSaverWnd,NULL,GTTX_Text,"S",TAG_END);
   GT_SetGadgetAttrs(GAD(GD_RGBB_GAD),ColorSaverWnd,NULL,GTTX_Text,"V",TAG_END);

   GT_SetGadgetAttrs(GAD(GD_RED_GAD),ColorSaverWnd,NULL,GTSL_Max,359,TAG_END);
   GT_SetGadgetAttrs(GAD(GD_GREEN_GAD),ColorSaverWnd,NULL,GTSL_Max,100,TAG_END);
   GT_SetGadgetAttrs(GAD(GD_BLUE_GAD),ColorSaverWnd,NULL,GTSL_Max,100,TAG_END);
  }
}

void ShiftColorsLeft(void)
{
   register SHORT i,j;

   UWORD firstred; 
   UWORD firstgreen;
   UWORD firstblue;

   GetColors(0);

   firstred = RedVal; 
   firstgreen = GreenVal;
   firstblue = BlueVal;
   
   for (i = 0, j=1; i < NumColors; i++,j++)   
   {
     if(i == NumColors-1)  /* last color to be set	*/
      {
       	RedVal = firstred;
  	GreenVal = firstgreen;
	BlueVal = firstblue;
      }
     else
        GetColors(j);

     SetRGB4(&Scr->ViewPort,i,RedVal,GreenVal,BlueVal);	    

   }
}

void ShiftColorsRight(void)
{
   register SHORT i,j;

   UWORD firstred; 
   UWORD firstgreen;
   UWORD firstblue;

   GetColors(NumColors-1);

   firstred = RedVal; 
   firstgreen = GreenVal;
   firstblue = BlueVal;
   
   for (i = NumColors-1, j=NumColors-2; i > -1; --i,j--)   
   {
     if(i == 0)  /* last color to be set	*/
      {
       	RedVal = firstred;
  	GreenVal = firstgreen;
	BlueVal = firstblue;
      }
     else
        GetColors(j);

     SetRGB4(&Scr->ViewPort,i,RedVal,GreenVal,BlueVal);	    

   }
}

void ColorRange(SHORT first, SHORT last)
{
   register SHORT i;
   LONG  whole, redfraction, greenfraction, bluefraction;
   UWORD col;
   UWORD firstred, firstgreen, firstblue;
   UWORD lastred, lastgreen, lastblue;
   UWORD workred, workgreen, workblue;

   if (first > last) {
      i = first;
      first = last;
      last = i;
   }

   /* I need to see a spread of at least two, where there's at least one
    * spot between the endpoints, else there's no work to do so I
    * might as well just return now.
    */

   if (first >= last - 1)
      return;

   col=GetRGB4(Scr->ViewPort.ColorMap,(LONG)first);

   firstred   = (col >> 8) & 0x0F;
   firstgreen = (col >> 4) & 0x0F;
   firstblue  = (col >> 0) & 0x0F;

   col=GetRGB4(Scr->ViewPort.ColorMap,(LONG)last);

   lastred   = (col >> 8) & 0x0F;
   lastgreen = (col >> 4) & 0x0F;
   lastblue  = (col >> 0) & 0x0F;

   whole = ((LONG) (lastred - firstred)) << 16;
   redfraction = whole / (last - first);
   whole = ((LONG)(lastgreen - firstgreen)) << 16;
   greenfraction = whole / (last - first);
   whole = ((LONG)(lastblue - firstblue)) << 16;
   bluefraction = whole / (last - first);

   for (i = first + 1; i < last; i++)
      {
      lastred = (redfraction * (i - first) + 0x8000) >> 16;
      workred = firstred + lastred;
      lastgreen = (greenfraction * (i - first) + 0x8000) >> 16;
      workgreen = firstgreen + lastgreen;
      lastblue = (bluefraction * (i - first) + 0x8000) >> 16;
      workblue = firstblue + lastblue;
      SetRGB4(&Scr->ViewPort,i,workred,workgreen,workblue);
      }
}

/*
 * Pick a Filename
 */
BOOL FileSelect(int mode, UBYTE *dir, UBYTE *file ) {
    struct FileRequester    *freq;
    struct TagItem *Tags;
    BOOL fileselected;

    fileselected = FALSE;
    Tags = FileTags[mode];

    /*
     * CAUTION! In order for the following statement to work, "ASL_Window"
     * must be the first Item in the TagItem array
     */

    Tags->ti_Data = (ULONG)ColorSaverWnd;


    if ( freq = AllocAslRequest( ASL_FileRequest, Tags )) {
        if ( RequestFile( freq )) {
            strcpy( dir, freq->rf_Dir );
            strcpy( FileSpec, freq->rf_Dir );
            strcpy( file, freq->rf_File );
	    AddPart(FileSpec,file,sizeof(FileSpec));
	    fileselected = TRUE;	
        }
        FreeAslRequest( freq );
    }

  return(fileselected);

}

/*
 * Close the main window and
 * clear the userport and bit mask.
 */
void CloseTheWindow( void )
{
    if ( CSIsOpen ) {
   	if(ResetPal) FreeMem(ResetPal,64L);
   	if(SavePal) FreeMem(SavePal,64L);
        CloseColorSaverWindow();
        CloseDownScreen();
        CSIdPort = NULL;
        CSIdMask = NULL;
        CSIsOpen = FALSE;
    }
}

/*
 * Here we get started when called from the Shell.
 */
void main( long argc, long *array )
{
    CSArgs = array;

    SetUpCS();
    EventHandler();
    CloseDownCS( NULL );
}

/*
 * Here we get started when called from the WorkBench.
 */
void wbmain( struct WBStartup *wbs )
{
    WbMsg = wbs;

    SetUpCS();
    EventHandler();
    CloseDownCS( NULL );
}

/*
 * Save a color palette
 */
void SaveColors(UBYTE *file, USHORT *palette, ULONG colors)
{

  int fh;
  int w;
  
  if ((fh = open((char *)file, O_WRONLY|O_CREAT|O_TRUNC)) >= 0)
   {
     w = write(fh, (void *)palette, colors << 1);
     close(fh);
     if( w != colors << 1 )
        (void)Req("Egad!","I/O error during write of:\n->>> %s <<<-",file);
   }
  else
     (void)Req("Oops!","Could not open file:\n->>> %s <<<-",file);
}


/*
 * Load a color palette
 */
void LoadColors(UBYTE *file, ULONG colors)
{

 int fh;
 long filelen, bytesread;
 USHORT *palette;
 BOOL sowhat;  

 if(!(palette = AllocMem(64L,MEMF_PUBLIC)))
    {
     (void)Req("Uh Oh!","Can not allocate memory\nfor color palette!");
     return;
    }
 
 if ((fh = open(file,O_RDONLY)) >= 0)	/* Try to open input file */
    {
     bytesread = read(fh, (char *)palette, colors << 1);
     filelen = lseek(fh,0L,2);    /* Get the file length    */
     close(fh);
    }
 else  			  		/* Couldn't open the file */
    {
     (void)Req("Oops!","Could not open file:\n->>> %s <<<-",file);
     return;
    } 

 if (bytesread < 0)		  	/* Uh-Oh! Read Error!     */
    {
     (void)Req("Oh No!","I/O error during read of:\n->>> %s <<<-",file);
     close(fh);
     return;
    }

/* 
 * If there are not enough or too many bytes in the file to match the 
 * number of BitPlanes, alert the user
 */   

 if ((colors << 1 > bytesread)||(filelen > colors << 1)) 
    sowhat =Req("Load Anyway|Cancel","Number of BitPlanes\ndo not match!\n\n\
      Colors:\n\nScreen:%2ld  File:%2ld",colors, filelen >> 1); 
 
 if(sowhat || bytesread == filelen == colors << 1)   
    LoadRGB4(&Scr->ViewPort,palette,1L << Scr->BitMap.Depth);

 if(palette) FreeMem(palette,64L);

}

#define BUFSIZE 2048L		/* size of I/O buffer */

void *FindTable(UBYTE *file, USHORT *palette, ULONG colors)
{
 char *inbuffer;
 long offset;			
 char *buffptr;			/* memory pointer	              	*/
 int bytesread=0;		/* bytes transferred during each read   */
 long filepos=0;		/* total bytes to have been read in     */
 int fh;

 OffSetCount = 0;

 if ((fh= open(file,O_RDONLY)) < 0)    /* try to open input file */
    {
     (void)Req("Oops!","Could not open file:\n->>> %s <<<-",file);
     return;
    }

 if(!(inbuffer = AllocMem(BUFSIZE,MEMF_PUBLIC))) 
    {
     (void)Req("Uh Oh!","Can not allocate memory\nfor input buffer!");
     return;
    }

  /*
   * read in the first block of chars 
   */

  bytesread= read(fh,inbuffer,BUFSIZE);  
  buffptr=inbuffer;

  for (;;)
    {

     if(memcmp((void *)buffptr,(void *)palette,colors<<1)==0)
      {                    		 /* did we get a match ??  */
       filepos=lseek(fh,0L,1);
       offset=((filepos-bytesread)+(buffptr-inbuffer)); 
       if(AddOffSet(offset) < 0)
         (void)Req("Oh No!","Memory Allocation failure\nin routine AddOffSet()");

      }
     buffptr++;

      /*
       * this next bit of code counters the possibility of having the search
       * palette chopped off at the end of the buffer, (would be my luck!)
       * if less than sizeof(palette) bytes remain in the buffer, an lseek
       * backwards is done and these are read back in to the beginning    
       * of the next buffer full                                          
       */

      if((BUFSIZE -(buffptr-inbuffer)) < colors<<1) 
         {
	  filepos=lseek(fh,-colors<<1,1);
          bytesread= read(fh,inbuffer,BUFSIZE); 
	  filepos=lseek(fh,0L,1);
          buffptr=inbuffer; 		/* reset pointers to start of buffer */
         }
      if(buffptr-inbuffer > bytesread)  /* got to the end of the file */ 
         break;
  }

  close(fh);


  if(OffSetCount == 0)	/* Couldn't find a Color Table match	*/
   {
    if(OffSetABase)
       FreeOffSets();  
    (void)Req("Oh Well!","Cannot find Color Table");
     GT_SetGadgetAttrs(GAD(GD_CYCLE_GAD),ColorSaverWnd,NULL,GTCY_Labels,
                       CYCLE_GAD0Labels,GA_Disabled, TRUE,TAG_END);
     GT_SetGadgetAttrs(GAD(GD_WRITE_GAD),ColorSaverWnd,NULL,GA_Disabled,
                       TRUE,TAG_END);
   }
  else
   {
     GT_SetGadgetAttrs(GAD(GD_CYCLE_GAD),ColorSaverWnd,NULL,GTCY_Labels,
                       OffSetSBase,GA_Disabled, FALSE,TAG_END);
     GT_SetGadgetAttrs(GAD(GD_WRITE_GAD),ColorSaverWnd,NULL,GA_Disabled,
                       FALSE,TAG_END);
     if(OffSetCount == 1)
        (void)Req("Yippie!","Found One Color Table match");
     else
        (void)Req("Hmm! (?)","Found %ld Color Table matches",OffSetCount);

     strcpy(ScanFileSpec,file);  
   }

  if(inbuffer) FreeMem(inbuffer,BUFSIZE);

}


#define OFFSETBUFSZ 8	/* number of characters in cycle gadget		*/

int AddOffSet(ULONG offset)
{
  ULONG *OffSetAPtr; 	/* pointer to place to store actual offset	     */
  UBYTE *OffSetBuf;     /* pointer to place to store string version of above */
  UBYTE **OffSetSPtr;   /* pointer to array of above strings		     */

  OffSetCount++;
  
/*-------------------------------------------------------------------------
 *  Store the offset in a continuously increasing array
 *-------------------------------------------------------------------------*/
 if(( OffSetABase = (ULONG *)realloc
    (OffSetABase, OffSetCount * sizeof(ULONG))) == NULL)
   return(-1);

  OffSetAPtr = OffSetABase + (OffSetCount -1);
 *OffSetAPtr = offset;

 
/*-------------------------------------------------------------------------
 *  Allocate memory to hold the offset in string form 
 *-------------------------------------------------------------------------*/
  if(( OffSetBuf = (UBYTE *)malloc(OFFSETBUFSZ)) == NULL)
    return(-1);

/*--------------------------------------------------------------------------
 *  Store pointers to the above strings in a continuously increasing array
 *-------------------------------------------------------------------------*/
  if(( OffSetSBase=(UBYTE **)realloc
     (OffSetSBase,((OffSetCount+1) * sizeof(UBYTE **)))) == NULL)
    return(-1);

  OffSetSPtr = OffSetSBase + (OffSetCount -1);
 *OffSetSPtr = OffSetBuf;

/*-------------------------------------------------------------------------
 *  String pointer array needs to be NULL terminated for GadTools cycle
 *  gadget
 *-------------------------------------------------------------------------*/
  *(++OffSetSPtr) = NULL;
  sprintf(OffSetBuf,"%7d",offset);

  return(0);

}

void FreeOffSets(void)
{
 UBYTE **sptr;  
 ULONG *optr; 
 USHORT i;

 optr = OffSetABase; 	/* Set pointers to head of actual offset array	*/
 sptr = OffSetSBase; 	/* and also to array of pointers to strings	*/

 for(i = 0; i < OffSetCount; i++, optr++, sptr++)
 {
  if(*sptr)free(*sptr); /* Free the string				*/
  if( sptr)free(sptr);  /* ...now free the pointer to the string	*/
  if( optr)free(optr);	/* ...and now the actual offset			*/
 }

 OffSetABase = NULL;	/* need to invalidate these pointers too...	*/
 OffSetSBase = NULL;

}

/*
 * Write the new colors out to the file.
 */
void WriteTable(UBYTE *file, USHORT *palette, ULONG colors)
{

 int fh;
 int w;
 BOOL doit;                  
 ULONG offset;
  
 offset = *(OffSetABase + CycleSelect);

 doit =Req("Go Ahead|Cancel","About to write to file:\n->>> %s <<<-\n\
at offset: %ld",file,offset);

 if (!doit)
   return;

 if ((fh = open(file,O_RDWR)) < 0)	/* Try to open input file */
    {
     (void)Req("Oops!","Could not open file:\n\"%s\"",file);
     return;
    }
 else
    {
     lseek(fh,offset,0);  	/* position to start of palette	*/
     w = write(fh, (void *)palette, colors << 1);
     close(fh);
     if( w != colors << 1 )
        (void)Req("Egad!","I/O error during write of:\n->>> %s <<<-",file);
     else
        (void)Req("I Hope it Works!","  >>>>> ALL DONE!! <<<<<  ");
    }

}

/*
 * Some program info
 */ 
void About(void)
{
(void)Req("I Promise!","     %s\n\n\
   >>>  This program is \"TradeWare¹\"  <<<\n\n\
You may freely distribute this program in a \n\
non-commercial  manner  as long as original \n\
sources  and  documentation are included...\n\n\
(¹See the Docs!)",CSTitle);
}


#define MAX3(a,b,c) MAX(a,MAX(b,c))
#define MIN3(a,b,c) MIN(a,MIN(b,c))

/*
 * The following two routines are slightly modified versions of routines
 * heisted from "HSV" by Frank Ederveen (FF791).  Thanks for the good 
 * explanation of how HSV works, Frank.  I really hadn't much of an idea,
 * and never really thought of adding HSV to ColorSaver until I saw your
 * program....
 */

void CalcHSV (UWORD red, UWORD green, UWORD blue)
{
  float hue, sat, delta, r, g, b, max, min;

  r = ((float) red  ) / 15;
  g = ((float) green) / 15;
  b = ((float) blue ) / 15;

  max = MAX3(r,g,b);
  min = MIN3(r,g,b);

  ValLevel = 100 * max;

  if (max != 0)
    sat = (max - min) / max;
  else
    sat = 0;

  if (sat == 0)
    hue = 0;
  else
    {
      delta = max - min;

      if (r == max)
	hue = (g - b) / delta;
      else if (g == max)
	hue = 2 + (b - r) / delta;
      else
	hue = 4 + (r - g) / delta;

      hue *= 60;

      if (hue < 0)
	hue += 360;
    }
  HueLevel = hue;
  SatLevel = 100 * sat;

}

void CalcRGB(void)
{
  float hue, sat, val, r, g, b, p1, p2, p3;
  int i = 0;
  float f = 0;

  hue = (float) HueLevel / 60;
  val = (float) ValLevel / 100;
  sat = (float) SatLevel / 100;

  while ((f = hue - i) > 1)
    i++;

  p1 = val * (1 - sat);
  p2 = val * (1 - (sat * f));
  p3 = val * (1 - (sat * (1 - f)));

  switch (i)
    {
    case 0:
      r = val;
      g = p3;
      b = p1;
      break;
    case 1:
      r = p2;
      g = val;
      b = p1;
      break;
    case 2:
      r = p1;
      g = val;
      b = p3;
      break;
    case 3:
      r = p1;
      g = p2;
      b = val;
      break;
    case 4:
      r = p3;
      g = p1;
      b = val;
      break;
    case 5:
      r = val;
      g = p1;
      b = p2;
      break;
    }

  RedVal   = (UWORD) (r * 15);
  GreenVal = (UWORD) (g * 15);
  BlueVal  = (UWORD) (b * 15);

}

