/* The routines in this file are copyright (c) 1987 by Helene (Lee) Taran.
 * Permission is granted for use and free distribution as long as the
 * original author's name is included with the code.
 */

#include "spline.h"

extern DLIST_ELEMENT Control_Points;
extern struct PopUp_Menu PointMenu, CurveMenu;
extern struct Image control_image;

int DrawAFrame = TRUE;
int CurveType = OPENB_NATURAL;


/* InRange : returns TRUE iff the mouse position is in the selection
 * range of the given control point position
 */
int InRange(mouse, control)
REAL_POINT *mouse, *control;
{
   return ((mouse->x >= control->x - CONTROL_RADIUS) &&
           (mouse->x <= control->x + CONTROL_RADIUS) &&
           (mouse->y >= control->y - CONTROL_RADIUS) &&
           (mouse->y <= control->y + CONTROL_RADIUS));
}

/* Select_ControlPoint : returns the member of Control_Points that is
 * is selected at <x,y>. Returns FALSE if no point is selected.
 */
DLISTPTR Select_ControlPoint(x,y)
SHORT x,y;
{
   REAL_POINT tmp; int InRange();
   DLISTPTR Find_Element();

   tmp.x = (float) x;
   tmp.y = (float) y;
   return(Find_Element(&tmp,&Control_Points,InRange));
}


/* Print Instruction: prints a string in the upper left hand corner
 * of the window.  Assumes that the current pen color is the the
 * color you want the string to appear in.
 */
PrintInstruction(w,instr)
struct Window *w;
char *instr;
{
   Move(w->RPort,w->LeftEdge + w->BorderLeft + 1, 
                 w->TopEdge  + w->BorderTop + 1);
   Text(w->RPort,instr,strlen(instr));
}

/* GetPoint : Waits for the user to select a point in window <w> and returns its
 * x,y coordinates in terms of a REAL_POINT structure.
 */
DLISTPTR GetPoint(w) 
struct Window *w;
{
   REAL_POINT *tmp; void *calloc(); DLISTPTR p;
   struct IntuiMessage mcopy, *msg, *GetMsg();
   struct RastPort *rp = w->RPort;
   BYTE old_mode = rp->DrawMode;

   if (!((tmp = (REAL_POINT *)calloc(1,sizeof(REAL_POINT))) &&
         (p = (DLISTPTR)calloc(1,sizeof(DLIST_ELEMENT))))) {
      fprintf(stderr,"splines : trouble in paradise : can't allocate point\n");
      panic(0);
   }
 SetDrMd(rp,COMPLEMENT);
 PrintInstruction(w,"Select A Point");
 while (1) {
   Wait(1 << w->UserPort->mp_SigBit);
   while (msg = GetMsg(w->UserPort)) {
         mcopy = *msg;
         ReplyMsg(msg);
         if ((msg->Class == MOUSEBUTTONS) && (msg->Code == SELECTDOWN) &&
             Inside_Window(msg->MouseX,msg->MouseY,w)) {
            tmp->x = (float)msg->MouseX;
            tmp->y = (float)msg->MouseY;
            PrintInstruction(w,"Select A Point");
            SetDrMd(rp,old_mode);
            PlopDot(w,tmp);
            p->contents = tmp;
            return(p); 
         }
    }
  }
}


/* PrintError: prints a error string to the window's upper left hand
 * corner for approx. 1.6 seconds.  The string is drawn in the complement
 * of the background so that it doesn't destroy what's already drawn in the
 * window. After the delay, the string is complemented out again so it
 * disappears.
 */
PrintError(w,str)
struct Window *w;
char *str;
{   struct RastPort *rp = w->RPort;
    BYTE old_mode = rp->DrawMode;
    SetDrMd(rp,COMPLEMENT);
    PrintInstruction(w,str);
    Delay(80l);
    PrintInstruction(w,str);
    SetDrMd(rp,old_mode);
}


DrawConstructionLine(w,p0,p1) /* draws a dotted line from p0 to p1 */
struct Window *w;
REAL_POINT *p0,*p1;
{   struct RastPort *rp = w->RPort;
    BYTE old_pen = rp->FgPen;         /* save the old pen color */
    USHORT old_pat = rp->LinePtrn;    /* save the old line pattern */

    SetAPen(rp,AFRAME_COLOR);
    SetDrPt(rp,0xcccc);               /* use a dotted line */
    Move(rp,(SHORT)p0->x,(SHORT)p0->y);
    Draw(rp,(SHORT)p1->x,(SHORT)p1->y);
    SetDrPt(rp,old_pat);
    SetAPen(rp,old_pen);

}

/* Edit_Curve : pops up the curve style editing menu in <w> and allows
 * the user to alter the current curve style
 */
Edit_CurveStyle(w)
struct Window *w;
{
  int option = PopUp(&CurveMenu,w);
  switch (option) {
    case TOGGLEAFRAME : DrawAFrame = !DrawAFrame; Redraw(w); break;
    case REDRAW : Redraw(w); break;
    case OPENB_TRIPLE: 
      if (LENGTH(&Control_Points) < 4) {
        PrintError(w,"?Error: Triple Knot option require minimum of 4 points");
        return; }
        /* othewise fall through to action for the next case */
    case OPENB_NATURAL : case CLOSEDB : case CLOSED_INTRPL : case OPEN_INTRPL :
      if (option != CurveType) { CurveType = option; Redraw(w);}
      break;
    case QUIT : close_things(); exit(0); break;
    }
}

   
/* Edit_ControlPoint : pops up the control point editing menu in <w> and
 * allows the user to alter the given control point <p>.  Assumes
 * that <p> is the control point that the user just selected.
 */
Edit_ControlPoint(w,p) 
struct Window *w;
DLISTPTR p;
{  
   int option = PopUp(&PointMenu,w);
   switch (option) {
     case ADD_AFTER    : 
        if (LENGTH(&Control_Points) == MAXG) {
           PrintError(w,"No! Don't you think you've added enough points?");
           return;
        }
       else Insert_After(p,GetPoint(w),&Control_Points); break;

     case ADD_BEFORE   :
        if (LENGTH(&Control_Points) == MAXG) {
           PrintError(w,"No! Don't you think you've added enough points?");
           return;
        }
       else Insert_Before(p,GetPoint(w),&Control_Points); break;

     case MOVE_POINT   : 
        { DLISTPTR tmp;
          EraseDot(w,p->contents); free(p->contents);
          tmp = GetPoint(w); p->contents = tmp->contents;
	  free(tmp); break;
        }
     case REMOVE_POINT : 
     { int len = LENGTH(&Control_Points);
       if ((len == 4) && (CurveType == OPENB_TRIPLE)) {
          PrintError(w,"?Error: Triple Knot option require minimum of 4 points");
          return; }
       else if (len == 3) 
          PrintError(w,"?Error: you must have a minimum of 3 control points");
       else {
             Remove_Element(p,&Control_Points); 
             free(p->contents); free(p);
           }
       } break;
     default : return;
   }
   Redraw(w);
}


EraseDot(w,dot)
struct Window *w;
REAL_POINT *dot;
{ 
    control_image.PlanePick = 0;
    PlopDot(w,dot);
    control_image.PlanePick = 1;
}


PlopDot(w,point)
struct Window *w;
REAL_POINT *point;
{
   DrawImage(w->RPort,&control_image,(SHORT)point->x,(SHORT)point->y);
}

   
Draw_ControlPoints(w) 
struct Window *w;
{
   DLISTPTR tmp = &Control_Points;
   while ((tmp = tmp->next) != &Control_Points) 
       PlopDot(w,POINT(tmp));
}


Redraw(w)
struct Window *w;
{
   struct RastPort *rp = w->RPort;
   SetRast(rp,ERASE);
   SetAPen(rp, CURVECOLOR);
   Draw_ControlPoints(w);
   switch (CurveType) {
     case CLOSEDB :       Draw_Closed_Bspline(w,&Control_Points); break; 
     case CLOSED_INTRPL : Draw_Closed_Ispline(w,&Control_Points); break;
     case OPEN_INTRPL :   Draw_Open_Ispline(w,&Control_Points); break;
     case OPENB_TRIPLE :  Draw_TripleKnot_Bspline(w,&Control_Points); break;
     default: Draw_Natural_Bspline(w,&Control_Points); break;
      }
}

 
      
