#include "struct.h"
#include "plot.h"
#include <graphics/gfxmacros.h>

extern struct Window *window;
extern int CHARWIDTH;
extern int CHARHEIGHT;
extern int LMARGIN;  /* CHARWIDTH x 7 */
extern int TMARGIN;
extern int BMARGIN;    /* CHARHEIGHT x 3 */
extern int XMINP;      /* LMARGIN */
extern int XMAXP;      /* MAXHORIZ - RMARGIN */
extern int YMAXP;   /* MAXVERT - TMARGIN */
extern int YMINP;    /* BMARGIN */

extern int debug;
extern int MAXVERT;
extern int MAXHORIZ;

extern FFP fround();
extern struct Pict *Pict;

struct TextAttr xfont = { "Times.font", 14, 0, 1 };
struct TextAttr yfont = { "Times.font", 14, 0, 1 };

struct TextAttr tfont = { "Times.font", 24, 0, 1 };


struct IntuiText XITLabel = {
        1,0,JAM1, /* front and back text pens, drawmode and fill byte */
        0,0,    /* XY origin relative to container TopLeft */
        &xfont,   /* font pointer or NULL for default */
        NULL,     /* pointer to text */
        NULL    /* next IntuiText structure */
};

struct IntuiText YITLabel = {
        1,0,JAM1, /* front and back text pens, drawmode and fill byte */
        0,0,    /* XY origin relative to container TopLeft */
        &yfont,   /* font pointer or NULL for default */
        NULL,     /* pointer to text */
        NULL    /* next IntuiText structure */
};

struct IntuiText TITLabel = {
        1,0,JAM1, /* front and back text pens, drawmode and fill byte */
        0,0,    /* XY origin relative to container TopLeft */
        &tfont,   /* font pointer or NULL for default */
        NULL,     /* pointer to text */
        NULL    /* next IntuiText structure */
};

int OLD_XMINP;
int NEW_XMIN;

struct TextFont *xf;
struct TextFont *yf;
struct TextFont *tf;

AllowForText(Pict)
struct Pict *Pict;
{
int i,n=3;
struct Tics *Tics;
char tmpstr[20];

   if (Pict->XLabel!=NULL) {
      xf=(struct TextFont *) OpenDiskFont(&xfont);
      if (xf)  {
                 YMINP = BMARGIN=( xfont.ta_YSize +CHARHEIGHT *4);
                 CloseFont(xf);
               }
      else YMINP = BMARGIN= CHARHEIGHT *5;
   }
   if (Pict->Title!=NULL) {
      tf=(struct TextFont *) OpenDiskFont(&tfont);
      if (tf) {
                YMAXP = MAXVERT - (TMARGIN=( tfont.ta_YSize +CHARHEIGHT *2));
                CloseFont(tf);
              }
      else YMAXP = MAXVERT- (TMARGIN=CHARHEIGHT*3);
   }
   OLD_XMINP=XMINP;
   Tics = Pict->Tics;
   for (i=0; i < Tics->NY; i++) { n=max(n,GetString(&tmpstr,Tics->y[i]));}
   XMINP=(n+2)*CHARWIDTH;
   if (Pict->YLabel!=NULL) {
      yf=(struct TextFont *) OpenDiskFont(&yfont);
      YITLabel.IText=Pict->YLabel;
      if (yf) {
                 XMINP=LMARGIN=XMINP+CHARWIDTH+ abs(IntuiTextLength(&YITLabel)/strlen(Pict->YLabel));
                 CloseFont(yf);
              }
      else XMINP=LMARGIN=XMINP+(2*CHARWIDTH);
   }
   if (XMINP!=OLD_XMINP)  NEW_XMIN=TRUE;
   return(0);
}


/**********************/
void GetDataLimits(Pict)
struct Pict *Pict;
{
   short i;
   FFP *x, *y, *e;
   struct Plot *Plot;
   FFP xmin, xmax, ymin, ymax;

   xmin = (ymin = FFPLARGE);
   xmax = (ymax = -FFPLARGE);

   Plot = Pict->Plot;
   while (Plot) {
      if (Plot->Enabled) {
         i = 0; x = Plot->x; y = Plot->y;
         if (Pict->ShowErr) e = Plot->e;
         while (i++ < Plot->NPts) {
            xmax = max(*x,xmax);
            xmin = min(*x,xmin);
            x++;
            if (Pict->ShowErr) {
               ymax = max((*y + *e), ymax);
               ymin = min((*y - *e), ymin);
               y++; e++;
            }
            else {
               ymax = max(*y,ymax);
               ymin = min(*y,ymin);
               y++;
            }
         }
      }
      Plot = Plot->NextPlot;
   }

   if (!Pict->XRegionLock)
      {Pict->CurrReg->XMax = xmax; Pict->CurrReg->XMin = xmin;}
   if (!Pict->YRegionLock)
      {Pict->CurrReg->YMax = ymax; Pict->CurrReg->YMin = ymin;}
}

void GetPlotLimits(Pict,Reg)
struct PlotRegion *Reg;
struct Pict *Pict;
{
   short i;
   FFP *x, *y, *e;
   struct Plot *Plot;
   FFP xmin, xmax, ymin, ymax;

   xmin = (ymin = FFPLARGE);
   xmax = (ymax = -FFPLARGE);

   Plot = Pict->Plot;
   while (Plot) {
     i = 0; x = Plot->x; y = Plot->y;
     if (Pict->ShowErr) e = Plot->e;
     while (i++ < Plot->NPts) {
       xmax = max(*x,xmax);
       xmin = min(*x,xmin);
       x++;
       if (Pict->ShowErr) {
         ymax = max((*y + *e), ymax);
         ymin = min((*y - *e), ymin);
         y++; e++;
       }
       else {
         ymax = max(*y,ymax);
         ymin = min(*y,ymin);
         y++;
       }
     }
     Plot = Plot->NextPlot;
  }
  Reg->XMax=xmax; Reg->XMin=xmin;
  Reg->YMax=ymax; Reg->YMin=ymin;
}


/*****************************************/
void AdjustForTics(dmin, dmax, ntics, tics)
FFP *dmin, *dmax, *tics;
short ntics;
{
   FFP tmp, fract, f_ntics;
   short i;

   f_ntics = (FFP)(ntics);
   fract = (FFP)(ntics+1) / f_ntics;

   /*** adjust data limits to make "nice" numbers for tic marks ***/
   tmp = *dmin - (*dmax - *dmin) / 5;
   *dmin = fround( *dmin, DOWN, tmp);
   tmp = (*dmax - *dmin) / f_ntics;
   tmp = fround( tmp, UP, (tmp * fract) );
   *dmax = *dmin + tmp * f_ntics;

   tics[0] = fround( *dmin, UP, *dmin+tmp/2);
   for (i=1; i<ntics; i++) {
      tics[i] = tics[i-1] + tmp;
   }
}


/**************/
void Scale(Pict)
struct Pict *Pict;
{
   struct Plot *Plot;
   struct PlotRegion *Reg;
   struct Tics *Tics;
   register short i, *xp, *yp;
   register FFP *x, *y, xmin, ymin, xscale, yscale;
   short *ep, err, err_sav = TRUE;
   FFP *e, xmax, ymax;

   Reg = Pict->CurrReg;
   xmin = Reg->XMin; xmax = Reg->XMax;
   ymin = Reg->YMin; ymax = Reg->YMax;

   if (FFPSAME(xmax,xmin))
      {Message("Scaling error: X to small"); xscale = FFPLARGE;}
   else
      Pict->XScale = xscale = (FFP)(XMAXP-XMINP) / (xmax - xmin);
   if (FFPSAME(ymax,ymin))
      {Message("Scaling error: Y to small"); yscale = FFPLARGE;}
   else
      Pict->YScale = yscale = (FFP)(YMAXP-YMINP) / (ymax - ymin);

#define NEW_X_REGION ( !FFPSAME(Reg->XMin,xmin) || !FFPSAME(Reg->XMax,xmax) )
#define NEW_Y_REGION ( !FFPSAME(Reg->YMin,ymin) || !FFPSAME(Reg->YMax,ymax) )

   /* DATA */
   Plot = Pict->Plot;
   err = Pict->ShowErr;
   while (Plot) {
      if (Plot->Enabled) {
         Reg = Plot->Reg;

         if ((NEW_X_REGION)||(NEW_XMIN)) {
            x = Plot->x; xp = Plot->xp;
            for (i = 0; i < Plot->NPts; i++) {
               *xp++ = XMINP + (short)((*x++ - xmin) * xscale);
            }
            Reg->XMin = xmin; Reg->XMax = xmax;
         }

         if ( (NEW_Y_REGION) || (err_sav!=err) ) {
            y = Plot->y; yp = Plot->yp; e = Plot->e; ep = Plot->ep;
            for (i = 0; i < Plot->NPts; i++) {
               *yp++ = YMINP + (short)((*y++ - ymin) * yscale);
               if (err) {
                  *ep++ = (short)(*e++ * yscale);
               }
            }
            Reg->YMin = ymin; Reg->YMax = ymax;
         }

      }
      Plot = Plot->NextPlot;
   }
   err_sav = err;

   /* TICS */
   Tics = Pict->Tics;
   for (i=0; i < Tics->NX; i++)
      Tics->xp[i] = XMINP + (short)((Tics->x[i] - xmin) * xscale);

   for (i=0; i < Tics->NY; i++)
      Tics->yp[i] = YMINP + (short)((Tics->y[i] - ymin) * yscale);
}


/**********************/
void DrawAxes(Tics,Grid)
struct Tics *Tics; short Grid;
{
   short *xp, *yp, n, tmpx, tmpy, i;
   char tmpstr[20];

   /*** DRAW BORDER ***/
   SetDrMd(rp, JAM1);
   SetAPen(rp,2); SetOPen(rp,1);
   PRectFill(XMINP-1,YMINP-1,XMAXP+1,YMAXP+1);
   PRectFill(XMINP,YMINP,XMAXP,YMAXP);


   /*** DRAW TICS/GRID ***/
   SetAPen(rp,3);
   xp=Tics->xp; yp=Tics->yp;
   if (Grid) SetRGB4(vp,3,0,15,15);
   for (i=0; i < Tics->NX; i++) {
      if (Grid) {
         if (xp[i]>XMINP) {PMove(xp[i],YMINP+1); PDraw(xp[i],YMAXP-1);}
      }
      else {
         if (xp[i]>XMINP)
           {
             PMove(xp[i],YMINP+1); PDraw(xp[i],YMINP+X_TIC_SIZE);
             PMove(xp[i],YMAXP-1); PDraw(xp[i],YMAXP-X_TIC_SIZE);
           }
      }
   }
   for (i=0; i < Tics->NY; i++) {
      if (Grid) {
         if (yp[i]>YMINP) {PMove(XMINP+1,yp[i]); PDraw(XMAXP-1,yp[i]);}
      }
      else {
         if (yp[i]>YMINP)
           {
              PMove(XMINP+1,yp[i]); PDraw(XMINP+Y_TIC_SIZE,yp[i]);
              PMove(XMAXP-1,yp[i]); PDraw(XMAXP-Y_TIC_SIZE,yp[i]);
           }
      }
   }


   /*** PRINT TIC VALUES AND TEXT ***/
   SetAPen(rp,1);
   for (i=0; i < Tics->NX; i++) {
      n=GetString(&tmpstr,Tics->x[i]);
      tmpx = Tics->xp[i] - (n/2) * CHARWIDTH;
      tmpy = max(0, YMINP-2*CHARHEIGHT);
      PMove(tmpx, tmpy); Text(rp,tmpstr,n);
   }

   XITLabel.IText=Pict->XLabel;
   n=IntuiTextLength(&XITLabel);
   tmpx=( (LMARGIN + (XMAXP-XMINP)/2) - (n/2) );
   tmpy = (MAXVERT-xfont.ta_YSize-CHARHEIGHT);
   PrintIText(rp,&XITLabel,tmpx,tmpy);

   TITLabel.IText=Pict->Title;
   n=IntuiTextLength(&TITLabel);
   tmpx=( (MAXHORIZ/2) - (n/2) );
   tmpy = (CHARHEIGHT);
   PrintIText(rp,&TITLabel,tmpx,tmpy);

   if (Pict->YLabel != NULL)
   PrintVText(rp,&YITLabel,2,(MAXVERT/2)-( (strlen(Pict->YLabel)/2)*(yfont.ta_YSize+1)));

   for (i=0; i < Tics->NY; i++) {
      n=GetString(&tmpstr,Tics->y[i]);
 /*     n = min( n, (LMARGIN/CHARWIDTH)-1 );  */
      tmpx = max(0, XMINP-(n+1)*CHARWIDTH);
      PMove(tmpx, Tics->yp[i]-CHARHEIGHT/2); Text(rp,tmpstr,n);
   }
}


/*****************/
void DrawPlot(Pict)
struct Pict *Pict;
{
   struct Plot *Plot;
   short *x, *y, *e, n, m, i;
   void PDrawBox(), PDrawPlus(), PDrawEggs(), PDrawKite(), PDrawTri();

   SetDrMd(rp, JAM1);

   Plot = Pict->Plot;
   while (Plot) {
      if (Plot->Enabled) {
         SetAPen(rp, Plot->Color);

         /* PLOT POINTS */
         if (Plot->PointSize != 0) {
            n = Plot->PointSize;
            if (abs(n) > 2) {m = (abs(n)/2)-1; SetOPen(rp, Plot->Color);}
            x = Plot->xp; y = Plot->yp;
            for (i=0; i<Plot->NPts; i++, x++, y++) {
               switch (abs(n)) {
               case 0: break;
               case 1: PWritePixel(*x, *y); break;
               case 2: PWritePixel(*x, *y); PWritePixel(*x, *y+1); break;
               case 3: PRectFill(*x, *y, *x+1, *y+1); break;
               default:
                 switch (Plot->PointType) {
                 case 1:  PDrawTri(m,*x,*y);  break;
                 case 2:  PDrawKite(m,*x,*y); break;
                 case 3:  PDrawEggs(m,*x,*y); break;
                 case 4:  PDrawEggs(m,*x,*y); /* Fall through (fancy, huh?) */
                 case 5:  PDrawPlus(m,*x,*y); break;
                 case 6:
                 default: PDrawBox(m,*x,*y); break;
                 }
               break;
               }
            }
         }

         /* PLOT LINES */
         if (Plot->Lines != FALSE) {
            PMove(*Plot->xp, *Plot->yp);
            x = &(Plot->xp[1]); y = &(Plot->yp[1]);
            for (i=1; i<Plot->NPts; i++, x++, y++) PDraw(*x, *y);
         }

         /* PLOT ERROR BARS */
         if (Pict->ShowErr) {
            x = Plot->xp; y = Plot->yp; e = Plot->ep;
            for (i=0; i < Plot->NPts; i++, x++, y++, e++) {
              if (*e>FFPSMALL)
                {
                   PMove(*x, *y - *e); PDraw(*x, *y + *e);
                   PMove(*x-ERR_DELIM,*y - *e); PDraw(*x+ERR_DELIM, *y - *e);
                   PMove(*x-ERR_DELIM,*y + *e); PDraw(*x+ERR_DELIM, *y + *e);
                }
            }
         }

      }
      Plot = Plot->NextPlot;
   }
   SetDrMd(rp, COMPLEMENT);
}


/*************************************************************/
extern int CheckUser();
extern int GetHowTo();
extern void CleanUp();
extern USHORT chip WaitSprite[];
extern struct Window *window;


void plot(Pict)
struct Pict *Pict;
{
   struct PlotRegion *Reg;
   struct Tics *Tics;
   int req = GETDATALIMITS;

   GetHowTo(Pict);
   Tics = Pict->Tics;
   InitWind();

   do {
      switch(req) {
      case GETHOWTO:
         GetHowTo(Pict); /* fall thru */
      case GETDATALIMITS:
         GetDataLimits(Pict);
      case REPLOT:
         Reg = Pict->CurrReg;
         if (!Pict->XRegionLock)
            AdjustForTics(&Reg->XMin, &Reg->XMax, Tics->NX, Tics->x);
         if (!Pict->YRegionLock)
            AdjustForTics(&Reg->YMin, &Reg->YMax, Tics->NY, Tics->y);
         AllowForText(Pict);
      case REDRAW:
      default: ;
      }
      SetPointer(window,WaitSprite,26,14,-4,-4);
      Scale(Pict);
      SetRast(rp,0);
      if (Pict->Axes) DrawAxes(Pict->Tics,Pict->Grid);
      DrawPlot(Pict);
      ClearPointer(window);

      req = CheckUser(Pict);
   } while (req != QUIT);

   CleanUp();
}


/***************************/
void PToU(Pict, xp, yp, x, y)
struct Pict *Pict;
short xp, yp;
FFP *x, *y;
{
   *x = Pict->CurrReg->XMin + (FFP)(xp-XMINP) / Pict->XScale;
   *y = Pict->CurrReg->YMin + (FFP)(yp-YMINP) / Pict->YScale;
}



PrintVText(rp,PText,X,Y)
struct RastPort *rp;
struct IntuiText *PText;
int X;
int Y;
{
struct TextFont *f;
int height;
int i,n,x,y;
struct TextAttr *at;
static struct TextAttr old_at;
char c;

   AskFont(rp,&old_at);

   while (PText)
     {
        SetAPen(rp,PText->FrontPen);
        SetBPen(rp,PText->BackPen);
        SetDrMd(rp,PText->DrawMode);
        at=PText->ITextFont;
        f=NULL;
        f=(struct TextFont *) OpenFont(at);
        if (f) {
           SetFont(rp,f);
           height=at->ta_YSize+1;
        }
        else height=9;
        x=X+PText->LeftEdge+TextLength(rp,"M",1);
        y=Y+PText->TopEdge;

        n=strlen(PText->IText);
        for (i=0;i<n;i++)
          {
             c=PText->IText[i];
             Move(rp,abs(x-(TextLength(rp,&c,1)/2)),y+(i*height));
             Text(rp,&c,1);
          }
        CloseFont(f);
        PText=PText->NextText;
     }
   f=(struct TextFont *) OpenFont(&old_at);
   SetFont(rp,f);
   return(0);
}


void PDrawPlus(m,x,y)
SHORT m,x,y;
{
   Move(rp,x-m,MAXVERT-y);
   Draw(rp,x+m,MAXVERT-y);
   Move(rp,x,MAXVERT-y-m);
   Draw(rp,x,MAXVERT-y+m);
}

void PDrawBox(m,x,y)
SHORT m,x,y;
{
   SHORT points[10];

   points[0]=points[2]=points[8]= x-m;
   points[1]=points[7]=points[9]=MAXVERT-y-m;
   points[3]=points[5]=MAXVERT-y+m,
   points[4]=points[6]=x+m;
   Move(rp,x-m,MAXVERT-y-m);
   PolyDraw(rp,5,points);
}

void PDrawEggs(m,x,y)
SHORT m,x,y;
{
   Move(rp,x-m,MAXVERT-y+m);
   Draw(rp,x+m,MAXVERT-y-m);
   Move(rp,x+m,MAXVERT-y+m);
   Draw(rp,x-m,MAXVERT-y-m);
}


void PDrawKite(m,x,y)
SHORT m,x,y;
{
   SHORT points[10];

   points[0]=points[4]=points[8]= x;
   points[1]=points[9]=MAXVERT-y+m;
   points[3]=points[7]=MAXVERT-y,
   points[2]=x+m;
   points[6]=x-m;
   points[5]=MAXVERT-y-m;

   Move(rp,x,MAXVERT-y+m);
   PolyDraw(rp,5,points);
}


void PDrawTri(m,x,y)
SHORT m,x,y;
{
   SHORT points[8];

   points[0]=points[6]= x;
   points[3]=points[5]=MAXVERT-y+m;
   points[1]=points[7]=MAXVERT-y-m,
   points[2]=x+m;
   points[4]=x-m;

   Move(rp,x,MAXVERT-y-m);
   PolyDraw(rp,4,points);
}


GetString(tmpstr,x)
char *tmpstr;
FFP x;
{
int n;

  if ((abs(x)>0.001)&&(abs(x)<99999))
     {
        n=sprintf(tmpstr,"%-.3f",x);
        while (tmpstr[n-1] == '0') n--;
        if (tmpstr[n-1] == '.') n--;
     }
  else
     {
        n=sprintf(tmpstr,"%-.2g",x);
        if (n>7) n=sprintf(tmpstr,"%-.1g",x);
        if (n>7) n=sprintf(tmpstr,"%-.0g",x);
     }
  if (n<2) {strcat(tmpstr,".0"); n=n+2;}
  tmpstr[n]='\0';
  return(n);
}

