/* this implements the Plot BOOPSI Gagdet */

#include "struct.c"
#include "WinPlot.h"
#include "OutlineFont.h"
#include "amigawin.h"

struct MUI_Palette_Entry ColorEntries[] =
{
 {  0, 0x00000000, 0x00000000, 0x00000000,  0}, /* border */
 {  1, 0x22222222, 0x22222222, 0x22222222,  1}, /* axes */
 {  2, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,  2}, /* background */
 {  3, 0x00000000, 0x00000000, 0x00000000,  6},
 {  4, 0x33333333, 0x33333333, 0x33333333,  7},
 {  5, 0x77777777, 0x77777777, 0x77777777,  8},
 {  6, 0xBBBBBBBB, 0xBBBBBBBB, 0xBBBBBBBB,  9},
 {  7, 0xFFFFFFFF, 0x00000000, 0x00000000,  3},
 {  8, 0x00000000, 0xFFFFFFFF, 0x00000000,  4},
 {  9, 0x00000000, 0x00000000, 0xFFFFFFFF,  5},
 { MUIV_Palette_Entry_End,0,0,0,0 },
};

char *ColorNames[] =
{
 "Border",
 "Axes",
 "BackGround",
 "Color 0",
 "Color 1",
 "Color 2",
 "Color 3",
 "Color 4",
 "Color 5",
 "Color 6",
};

Class *PlotImgClass = NULL;

struct PlotImageData
{
 struct ColorMap *cm;
 UWORD            Pens[NUMCOLORS];
};

static ULONG __asm DispatchPlotImage(register __a0 Class *, 
				     register __a2 Object *,
				     register __a1 Msg);

int _STI_10000_InitPlotImgClass(void)
{                                
 Class *cl;
 
 if (cl = MakeClass(NULL, GADGETCLASS, NULL, sizeof(struct PlotImageData), 0))
  {
   cl->cl_Dispatcher.h_Entry = (ULONG(*)()) DispatchPlotImage;
   PlotImgClass = cl;

   return 0;
  }

 return 1;
}

void _STD_10000_FreePlotImgClass(void)
{
 if (PlotImgClass) FreeClass(PlotImgClass);
}

/* a quick&dirty emulation of OS3 ObtainBestPen */
static int GetPen(struct ColorMap *cm, UBYTE Depth, ULONG lr, ULONG lg, ULONG lb)
{
 ULONG entrie;
 ULONG  sum, best;
 BYTE  r1, g1, b1;
 UBYTE r, g, b;
 int   i, bestpen;

 r = lr >> 28; g = lg >> 28; b = lb >> 28;
 best = INT_MAX; bestpen = 0;
 for(i = 0; i<Depth; i++)
  {
   entrie = GetRGB4(cm, i);
   r1 = (entrie >> 16) & 0x0F;
   g1 = (entrie >>  8) & 0x0F;
   b1 = entrie & 0x0F;

   sum = (r1-r)*(r1-r)+(g1-g)*(g1-g)+(b1-b)*(b1-b);
   if (sum < best)
    {
     best = sum;
     bestpen = i;
    }
  }

 return bestpen;
}

struct PlotCommand *commands = NULL;
int                 ncommands = 0;

/* this the main plot routine, which interprets the GnuPlot draw-commands */
static void __interrupt RenderImage(struct gpRender *gpr, struct Gadget *g,
				    struct PlotImageData *pig)
{
 int              i, w, h;
 int              justify = 0;
 int              x,y,xmin,xmax,ymin,ymax,txlen; 
 BOOL             Vertical = FALSE;
 struct Task     *MyTask; 
 struct RastPort *rp = gpr->gpr_RPort;

 MyTask = FindTask(NULL);
 if (MyTask->tc_Node.ln_Type == NT_PROCESS)
  {
   if (GfxBase->LibNode.lib_Version >= 39)
    {
     if (pig->cm != NULL)
      for(i=0; i<NUMCOLORS; i++)
       ReleasePen(pig->cm, pig->Pens[i]);
     else
      pig->cm = gpr->gpr_GInfo->gi_Screen->ViewPort.ColorMap;

     for(i=0; i<NUMCOLORS; i++)
      pig->Pens[i] = ObtainBestPen(pig->cm,
				   ColorEntries[i].mpe_Red,
				   ColorEntries[i].mpe_Green,
				   ColorEntries[i].mpe_Blue,
				   OBP_Precision, PRECISION_IMAGE,
				   TAG_END);
    }
   else
    {
     UBYTE Depth = 1 << gpr->gpr_GInfo->gi_DrInfo->dri_Depth;

     pig->cm = gpr->gpr_GInfo->gi_Screen->ViewPort.ColorMap;
     for(i=0; i<NUMCOLORS; i++)
      pig->Pens[i] = GetPen(pig->cm, Depth,
			    ColorEntries[i].mpe_Red,
			    ColorEntries[i].mpe_Green,
			    ColorEntries[i].mpe_Blue);
    }
   SetAPen(rp, pig->Pens[2]);
   RectFill(rp, g->LeftEdge, g->TopEdge,
	    g->LeftEdge+g->Width-1, g->TopEdge+g->Height-1);
   SetAPen(rp, pig->Pens[0]);
   SetDrMd(rp, JAM1);
   
   w = g->Width;
   h = g->Height;
   SetFontSize(w, h);
   for(i=0; i<ncommands; i++)
    switch (commands[i].pc_com)
     {
     case LINETYP:
      if (commands[i].pc_arg1 == -2)
       SetAPen(rp, pig->Pens[0]);
      else if (commands[i].pc_arg1 == -1)
       SetAPen(rp, pig->Pens[1]);
      else 
       SetAPen(rp, pig->Pens[3+ commands[i].pc_arg1 % (NUMCOLORS-3)]);
      break;
     case JUSTIFY:
      justify = commands[i].pc_arg1;
      break;
     case TEXT_ANGLE:
      Vertical = commands[i].pc_arg1 == 1;
      break;
     case PUT_TEXT:
      txlen = TextLen(commands[i].pc_argstr);
      x = (commands[i].pc_arg1 * w) / RESOLUTION;
      y = h-(commands[i].pc_arg2 * h) / RESOLUTION;
      if (!Vertical)
       {
	switch (justify)
	 {
	 case 0 :
	  xmin = x;
	  xmax = x + txlen;
	  break;
	 case 1:
	  xmin = x - txlen/2;
	  xmax = x + txlen/2;
	  break;
	 case 2:
	  xmin = x - txlen;
	  xmax = x;
	  break;
	 }
	ymin = y;
	ymax = ymin+h/70;
	PrintString(rp, g->LeftEdge+xmin, g->TopEdge+ ymax,
		    commands[i].pc_argstr, FALSE); 
       }
      else
       {
	ymax = y + txlen/2;
	PrintString(rp, g->LeftEdge+x, g->TopEdge+ymax, commands[i].pc_argstr, TRUE);
       }
      break;
     case MOVE:
      Move(rp,
	   g->LeftEdge + (commands[i].pc_arg1 * w) / RESOLUTION,
	   g->TopEdge + h - (commands[i].pc_arg2 * h) / RESOLUTION);
      break;
     case VECTOR:
      Draw(rp,
	   g->LeftEdge + (commands[i].pc_arg1 * w) / RESOLUTION,
	   g->TopEdge + h - (commands[i].pc_arg2 * h) / RESOLUTION);
      break;
     }
  }
}

/* the BOOPSI stuff, quite standard */
/* Although this is a BOOPSI-Gadget, we only need its image, */
/* so the input related stuff ist not implemented */
static ULONG __interrupt __asm __saveds DispatchPlotImage(register __a0 Class *cl,
							  register __a2 Object *o,
							  register __a1 Msg msg)
{
 ULONG                 retval = FALSE;
 int                   i;
 Object               *object;
 struct PlotImageData *this;

 switch (msg->MethodID)
  {
  case GM_RENDER:
   this = INST_DATA(cl, o);
   RenderImage((struct gpRender *)msg, (struct Gadget *)o, this);
   retval = TRUE;
   break;
  case OM_NEW:
   if (object = (Object *)DoSuperMethodA(cl, o, msg))
    {
     this = INST_DATA(cl, object);
     this->cm = NULL;
    }
   retval = (ULONG)object;
   break;
  case OM_DISPOSE:
   this = INST_DATA(cl, o);
   if (GfxBase->LibNode.lib_Version >= 39 && this->cm)
    for(i=0; i<NUMCOLORS; i++)
     ReleasePen(this->cm, this->Pens[i]);
   retval = TRUE;
   break;
  default:
   retval = DoSuperMethodA(cl, o, msg);
   break;
  }
 
 return retval;
}
