/*
 * MandelVroom 2.0
 *
 * (c) Copyright 1987,1989  Kevin L. Clague, San Jose, CA
 *
 * All rights reserved.
 *
 * Permission is hereby granted to distribute this program's source
 * executable, and documentation for non-comercial purposes, so long as the
 * copyright notices are not removed from the sources, executable or
 * documentation.  This program may not be distributed for a profit without
 * the express written consent of the author Kevin L. Clague.
 *
 * This program is not in the public domain.
 *
 * Fred Fish is expressly granted permission to distribute this program's
 * source and executable as part of the "Fred Fish freely redistributable
 * Amiga software library."
 *
 * Permission is expressly granted for this program and it's source to be
 * distributed as part of the Amicus Amiga software disks, and the
 * First Amiga User Group's Hot Mix disks.
 *
 * contents: this file contains the functions that open, maintain, operate
 * and close the color palette.
 */

#include "mandp.h"

#include <ctype.h>

#define PENLEFT 8
#define PENTOP  15

extern struct Gadget *ContGadget[], *SelGadget[];

UBYTE PaletteOpen;

/*
 * Holder for Allocated color potentiometer gadgets.
 */
struct ColorGads {
  struct Gadget *RedPot;
  struct Gadget *GreenPot;
  struct Gadget *BluePot;
};

struct Window          *PalWind;
struct ColorGads        PalGads;

LONG CurPen;

struct NewWindow NewPal = {
   128,0,                    /* start position           */
   70,80,                    /* width, height            */
   (UBYTE) 0, (UBYTE) -1,   /* detail pen, block pen    */
   NULL,                     /* IDCMP flags */
                             /* MandWind flags */
   WINDOWCLOSE   | WINDOWDRAG | WINDOWDEPTH | NOCAREREFRESH | SMART_REFRESH,
   (struct Gadget *) NULL,   /* first gadget             */
   (struct Image *) NULL,    /* user checkmark           */
   (UBYTE *) "Colors",       /* window title             */
   (struct Screen *) NULL,   /* pointer to screen        */
   (struct BitMap *) NULL,   /* pointer to superbitmap   */
   80,80,80,80,              /* sizing                   */
   CUSTOMSCREEN              /* type of screen           */
   };

/*
 * Color Palette Commands
 */

CopyRGBCmd(Msg)
  struct IntuiMessage *Msg;
{
  struct Gadget  *gadget;
  register LONG rgb, pen;

  /* Need contour selection to complete */

  gadget = (struct Gadget *) Msg->IAddress;

  if (Msg->Class == GADGETDOWN) {

    if (gadget->GadgetID == PALCOPY) { /* Copy command gadget */

      SetToPointer();
      State = COPYRGBSTATE;
    } else {

      if (GADG_TYPE(gadget->GadgetID) == PALPEN) {

        /* Copy the RGBs from CurPen to NewPen */

        pen = GADG_NUM(gadget->GadgetID);
        rgb = GetRGB4(vp->ColorMap, CurPen);
        SetRGB4(vp, pen, rgb >> 8, rgb >> 4, rgb);
        SaveRGBs(CurPict);

        State = IDLESTATE;
      }
    }
  }
}

SpreadRGBCmd(Msg)
  struct IntuiMessage *Msg;
{
  struct Gadget *gadget;
  register LONG  pen;

  /* Need contour selection to complete */

  gadget = (struct Gadget *) Msg->IAddress;

  if (Msg->Class == GADGETDOWN) {

    if (gadget->GadgetID == PALRANGE) { /* Spread command gadget */

      SetToPointer();
      State = SPREADRGBSTATE;
    } else {

      if (GADG_TYPE(gadget->GadgetID) == PALPEN) {

        /* Spread the RGBs from CurPen to NewPen */

        pen = GADG_NUM(gadget->GadgetID);
        ColorRange(CurPen, pen);
        SaveRGBs(CurPict);

        State = IDLESTATE;
      }
    }
  }
}

ExchangeRGBCmd(Msg)
  struct IntuiMessage *Msg;
{
  register LONG rgb, rgb2, pen;
  struct Gadget *gadget;

  /* Need contour selection to complete */

  gadget = (struct Gadget *) Msg->IAddress;

  if (Msg->Class == GADGETDOWN) {

    if (gadget->GadgetID == PALEXCG) { /* Exchange command gadget */

      SetWithPointer();
      State = XCHGRGBSTATE;
    } else {

      if (GADG_TYPE(gadget->GadgetID) == PALPEN) {

        /* exchange the RGBs in CurPen and NewPen */

        pen = GADG_NUM(gadget->GadgetID);
        rgb  = GetRGB4(vp->ColorMap, CurPen);
        rgb2 = GetRGB4(vp->ColorMap, pen);
        SetRGB4(vp, CurPen, rgb2 >> 8, rgb2 >> 4, rgb2 );
        SetRGB4(vp, pen,    rgb  >> 8, rgb  >> 4, rgb  );
        SaveRGBs(CurPict);

        State = IDLESTATE;
      }
    }
  }
}

SlideRGBCmd(Msg)
  struct IntuiMessage *Msg;
{
  struct Gadget *gadget;

  gadget = (struct Gadget *) Msg->IAddress;

  switch( Msg->Class ) {

    case GADGETDOWN:                          /* Start RGB slide    */
         StartBarDrag();
         State = SLIDERGBSTATE;
         break;

    case MOUSEMOVE:
         ModifyColors();                      /* change RGB colors */
         break;

    case GADGETUP:                            /* Stop the RGB slide */
         ModifyColors();
         SaveRGBs(CurPict);
         State = IDLESTATE;
         break;
  }
}

SetCurPen( pen )
  int pen;
{
  BoxPen( CurPen, NORMALPEN );
  CurPen = pen;
  SetColorProps(pen);
  BoxPen( CurPen, MEDIUMPEN );
  SaveRGBs( CurPict );
}

/*
 * Blend a range of colors between two pens
 */
ColorRange(first, last)
  LONG first, last;
{
    LONG i;
    register LONG whole, redfraction, greenfraction, bluefraction;
    register USHORT rgb;
    LONG firstred, firstgreen, firstblue;
    LONG lastred, lastgreen, lastblue;
    LONG 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;

    rgb = GetRGB4(vp->ColorMap, first);
    firstred = (rgb >> 8) & 0xF;
    firstgreen = (rgb >> 4) & 0xF;
    firstblue = (rgb >> 0) & 0xF;

    rgb = GetRGB4(vp->ColorMap, last);
    lastred = (rgb >> 8) & 0xF;
    lastgreen = (rgb >> 4) & 0xF;
    lastblue = (rgb >> 0) & 0xF;

    whole = (lastred - firstred) << 16;
    redfraction = whole / (last - first);
    whole = (lastgreen - firstgreen) << 16;
    greenfraction = whole / (last - first);
    whole = (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(vp, i, workred, workgreen, workblue);
        }
} /* ColorRange */

/*
 * Modify the colors in the current pen
 */
ModifyColors( )
{
  register LONG newred, newgreen, newblue;

  newred = ((struct PropInfo *)
           PalGads.RedPot->SpecialInfo)->VertPot >> 12;
  newgreen = ((struct PropInfo *)
           PalGads.GreenPot->SpecialInfo)->VertPot >> 12;
  newblue = ((struct PropInfo *)
           PalGads.BluePot->SpecialInfo)->VertPot >> 12;

  newred   = 0xF ^ newred;
  newgreen = 0xF ^ newgreen;
  newblue  = 0xF ^ newblue;

  PrintRGB( newred, newgreen, newblue );

  SetRGB4(vp, CurPen, newred, newgreen, newblue);
} /* ModifyColors */

PrintRGB( r, g, b )
  LONG r,g,b;
{
  PrintGad( PalGads.RedPot,   r );
  PrintGad( PalGads.GreenPot, g );
  PrintGad( PalGads.BluePot,  b );
}

PrintGad( gadget, Color )
  register struct Gadget *gadget;
  LONG   Color;
{
  register struct IntuiText *SaveIntui, *IntuiText = gadget->GadgetText;

  char d[4];

  SaveIntui = IntuiText;

  Color &= 0xf;
  sprintf( d, "%x", Color );

  d[0] = toupper(d[0]);

  IntuiText = IntuiText->NextText;

  IntuiText->IText[0] = d[0];

  IntuiText = IntuiText->NextText;

  IntuiText->IText[0] = d[0];

  PrintIText( PalWind->RPort, SaveIntui, gadget->LeftEdge, gadget->TopEdge);
}

/*
 * Reflect a pen's new color in the proportional gadget
 */
SetColorProps(pen)
  LONG pen;
{
  register LONG rgb, red, green, blue;

  if (PalWind == NULL)
    return;

  pen &= Num_vp_Colors - 1;

  rgb = GetRGB4(vp->ColorMap, pen);

  red = 0xF - ((rgb >> 8) & 0xF);
  green = 0xF - ((rgb >> 4) & 0xF);
  blue = 0xF - (rgb & 0xF);

  red   |= (red << 4);
  red   |= (red << 8);
  green |= (green << 4);
  green |= (green << 8);
  blue  |= (blue <<  4);
  blue  |= (blue <<  8);

  NewModifyProp(PalGads.RedPot,  PalWind,NULL,FREEVERT|PROPBORDERLESS,
             0L,red,0L,0xfffL,1L);
  NewModifyProp(PalGads.GreenPot,PalWind,NULL,FREEVERT|PROPBORDERLESS,
             0L,green,0L,0xfffL,1L);
  NewModifyProp(PalGads.BluePot, PalWind,NULL,FREEVERT|PROPBORDERLESS,
             0L,blue,0L,0xfffL,1L);
} /* SetColorProps */

BoxPen(BoxPen, DrawPen)
  LONG BoxPen, DrawPen;
{
  register LONG  Top, Bot, Left, Right;
  register ULONG row, column;

#define PALTOP    (PENTOP - 1)
#define PALLEFT   (PENLEFT - 1)
#define PENWIDTH  (4 << XScale)
#define PENHEIGHT (4 << YScale)
#define PENXPITCH (6 << XScale)
#define PENYPITCH (6 << YScale)

  if (PalWind == NULL)
    return;

  column = BoxPen/8;

  row = BoxPen - column*8;

  SetAPen(PalWind->RPort, DrawPen);

  Left  = PALLEFT + PENXPITCH * column;
  Top   = PALTOP  + PENYPITCH * row;
  Right = Left    + PENWIDTH  + 2 + XScale;
  Bot   = Top     + PENHEIGHT + 2 + YScale;

  Move(PalWind->RPort, Left,  Top);
  Draw(PalWind->RPort, Right, Top);

  if (DrawPen == HIGHLIGHTPEN) SetAPen(PalWind->RPort, SHADOWPEN);

  Draw(PalWind->RPort, Right, Bot);
  Draw(PalWind->RPort, Left,  Bot);

  SetAPen(PalWind->RPort, DrawPen);

  Draw(PalWind->RPort, Left,  Top + 1);

  if (DrawPen) {
    row = GetRGB4(vp->ColorMap, BoxPen & (Num_vp_Colors - 1));
    PrintRGB( (long) row >> 8, (long) row >> 4, (long) row );
  }
}

static LONG WindowWidth;
static LONG WindowHeight;
static LONG BorderLeft;

static struct Border *PensBorder;
/*
 * Allocate all the gadgets for the color palette window
 */
struct Gadget *MakePalette()
{
           struct Gadget *FirstGadget;
  register struct Gadget *NextGadget;
  register LONG i,Left,x,y,c = 0;

  struct IntuiText *Text, *NextText;
  struct PropInfo  *PropInfo;

  LONG fourx = 4 << XScale;
  LONG foury = 4 << YScale;

  char *str;

  Left = PENLEFT;

  FirstGadget = NextGadget =
    MakeBool( Left, PENTOP, fourx, foury, 0, PALPEN, GADGIMAGE );

  if ( FirstGadget == NULL ) goto error;

  i = 1 << (screen->BitMap.Depth);

  for (x = 0; x < 6*8 && i > 0; x += 6) {
    for (y = 0; y < 6*8 && i > 0; y += 6) {
      if (c != 0) {

        NextGadget->NextGadget =
          MakeBool( Left, (y<<YScale)+PENTOP, fourx, foury, c, PALPEN+c,
                    GADGIMAGE);

        if ((NextGadget = NextGadget->NextGadget) == NULL) goto error;
      }
      c++;
      i--;
    }
    Left += 6 << XScale;
  }
  WindowHeight = ((6*8) << YScale) + PENTOP + 6;

  c = ((y + 1) << YScale) + 2;

  if ( c > WindowHeight ) {
    WindowHeight = c + 4;
  }

  PensBorder = ShadowBorder( BEVELEDUP, 4, 12, Left - 2, c+1);

  if (PensBorder == NULL) goto error;

  Left += 9;

  BorderLeft = Left - 1;

  i = -2 * ( XScale ^ 1 );

  for (x = y = 0; y < 3; x += 10, y++) {

    NextGadget->NextGadget = MakePot( Left + x, 22, fourx,
                                      WindowHeight - 35, PALPOT, y);
    NextGadget = NextGadget->NextGadget;

    if ( NextGadget == NULL ) goto error;

    NextGadget->Activation = GADGIMMEDIATE | FOLLOWMOUSE | RELVERIFY;
    NextGadget->GadgetText = Text =
                           ShadowIntui("0", i, WindowHeight - 32 );
    if ( Text == NULL ) goto error;

    PropInfo = (struct PropInfo *) NextGadget->SpecialInfo;
    PropInfo->VertBody  = 0xfff;

    switch (y) {
      case 0:  PalGads.RedPot = NextGadget;
               str = "R";
               break;

      case 1:  PalGads.GreenPot = NextGadget;
               str = "G";
               break;

      case 2:  PalGads.BluePot = NextGadget;
               str = "B";
               break;
    }
    NextText = Text;

    while ( NextText->NextText ) {
      NextText = NextText->NextText;
    }

    NextText->NextText = ShadowIntui( str, i, -11 );

    if ( NextText->NextText == NULL ) goto error;
  }
  Left += 32;

  for (c = 0, x = 0; x < 17*3; x += 17) {

    NextGadget->NextGadget =
      MakeBool( Left, x + 11, 54, 13, 1, PALCNTL+c, NULL);

    NextGadget = NextGadget->NextGadget;

    if ( NextGadget == NULL) goto error;

    switch (c) {
      case 0:  NextGadget->GadgetText =
                  ShadowIntui("Copy", 12,3);
               break;

      case 1:  NextGadget->GadgetText =
                  ShadowIntui("Spread",4,3);
               break;

      case 2:  NextGadget->GadgetText =
                  ShadowIntui("Exchg", 8,3);
               break;
    }
    c++;
  }
  WindowWidth = Left + 59;

  return(FirstGadget);

error:
  FreeBorder( PensBorder );
  FreeGadgets( FirstGadget );
  return( NULL );
} /* MakePalette */

static struct Gadget *PalGadgets;

/*
 * Open the Palette window
 */
OpenPalWind()
{
  struct Window *OpenMyWind();

  register struct Gadget *gadget;
  register struct RastPort *Rp;
  LONG fourx = 4 << XScale;

  if (CurPict == NULL)
    return;

  if ( PalWind == NULL ) {

    gadget = MakePalette();

    if ( gadget == NULL ) {
      DispErrMsg("Couldn't get palette gadgets", 0 );
      return;
    }

    PalWind = OpenMyWind( &NewPal, screen, NULL, WindowWidth, WindowHeight);

    if ( PalWind != NULL ) {

      ModifyIDCMP( PalWind,  (long)
                   PalWind->IDCMPFlags | MOUSEBUTTONS | MOUSEMOVE );

      Rp = PalWind->RPort;

      SetAPen( Rp, NORMALPEN );
      RectFill( Rp, LEFTMARG, TOPMARG,
                WindowWidth,WindowHeight);

      BorderWindow( PalWind );

      PalGadgets = gadget;

      AddGList( PalWind, gadget, -1, -1);

      RefreshGadgets( gadget, PalWind, NULL );

      DrawBorder( Rp, PensBorder, 0L, 0L );
      FreeBorder( PensBorder );

      CurPen &= Num_vp_Colors - 1;

      BoxPen( CurPen, MEDIUMPEN );
      SetColorProps( CurPen );
    }
  } else {
    WindowToFront( PalWind );
  }
  PaletteOpen = 1;
} /* OpenPalWind */

/*
 * Close the Palette window
 */
ClosePalWind()
{
  if (PalWind != NULL) {

    NewPal.LeftEdge = PalWind->LeftEdge;
    NewPal.TopEdge  = PalWind->TopEdge;

    CloseMyWind(PalWind,PalGadgets);
    PalGadgets = NULL;
  }
  PalWind = NULL;
} /* ClosePalWind */

