/*
    This file is part of the FElt finite element analysis package.
    Copyright (C) 1993 Jason I. Gobat and Darren C. Atkinson

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

/*****************************************************************************
*
* File:		tools.c
*
* Description:	contains functionality for the construction tools routines
*
*****************************************************************************/

# include <stdio.h>
# include <math.h>
# include <X11/Intrinsic.h>
# include <X11/StringDefs.h>
# include <X11/Xaw/Simple.h>
# include "fe.h"
# include "Drawing.h"
# include "Canvas.h"
# include "dialog.h"
# include "procedures.h"
# include "misc.h"
# include "allocate.h"
# include "Tree.h"
# include "globals.h"


static unsigned op_count;

static FigureAttributes null_attrib = {0};

Tree figure_tree = NULL;


int figure_cmp (item1, item2)
    Item item1;
    Item item2;
{
    return ((int) item1) - ((int) item2);
}


void ToolsDeleteFigure ()
{
   Arg		arglist [1];

   SetEditMode ( );
   XtSetArg (arglist[0], XtNcursorName, "dotbox");
   XtSetValues (drawing, arglist, 1);

   ChangeStatusLine ("- Select figure -", False);
  
   XtRemoveAllCallbacks (drawing, XtNbuttonCallback);
   XtAddCallback (drawing, XtNbuttonCallback, DeleteToolCB, NULL);
}


void DeleteFigureGroup (figures, nfigures)
    Figure  *figures;
    unsigned nfigures;
{
    unsigned         i;
    Figure           fig;
    Boolean          firsttime;
    FigureAttributes attr;

    firsttime = True;
    for (i = 0; i < nfigures; i ++) {
	fig = figures [i];
	DW_GetAttributes (drawing, fig, &attr);
	if (attr.user_data != NULL)
	    continue;

	if (firsttime == True) {
	    firsttime = False;
	    DW_SetAutoRedraw (drawing, False);
	}

        TreeDelete (figure_tree, fig);
	DW_RemoveFigure (drawing, fig);
    }

    if (firsttime == False) {
	DW_SetAutoRedraw (drawing, True);
	changeflag = True;
    }

    XtFree ((char *) figures);
}


void DeleteToolCB (w, clientData, callData)
   Widget	w;
   XtPointer	clientData,
		callData;
{
   DrawingReport		*report;
   Figure			figure;
   FigureAttributes		attributes;

   report = (DrawingReport *) callData;

   if (report -> event -> type != ButtonPress)
	return;

   if (report -> event -> xbutton.button == 3)
	QuitEdit ( );

   if (report -> event -> xbutton.button == 2) {
	SelectGroup (callData, DeleteFigureGroup);
	return;
   }

   if (report -> event -> xbutton.button != 1)
	return;
 
   figure = DW_FindFigure (w, report -> unsnapped.x, report -> unsnapped.y);
                              
   if (figure != NULL) {
      DW_GetAttributes (w, figure, &attributes);
      if (attributes.user_data == NULL) {
         DW_RemoveFigure (w, figure);
	 TreeDelete (figure_tree, figure);
      }
   }
}

void ToolsDrawLine ()
{
   Cardinal	count;
   Arg		arglist [2];

   SetEditMode ( );

   count = 0;
   XtSetArg (arglist [count], XtNcursorName, "pencil"); count++;
   XtSetValues (drawing, arglist, count);

   if (!(DW_SetForeground (drawing, canvas -> tool_color)))
      DW_SetForeground (drawing, "black");
  
   ChangeStatusLine ("Select first point:", True);

   op_count = 0;

   XtRemoveAllCallbacks (drawing, XtNbuttonCallback);
   XtAddCallback (drawing, XtNbuttonCallback, DoLineCB, NULL);
   XtOverrideTranslations(entry, 
       XtParseTranslationTable ("<Key>Return: DoLineAP()"));
}

void DoLineAP ()
{
   static float 	xs,xe,
			ys,ye;
   char		 	*status;
   static Figure	line;
  
   if (op_count == 0) {
      status = GetTextCoordinates (&xs, &ys, NULL);
      if (status == NULL) {
         op_count++;
         ChangeStatusLine ("Select second point:", True);
      }
   }
   else if (op_count == 1) {
      status = GetTextCoordinates (&xe, &ye, NULL);
      if (status == NULL) {
         line = DW_DrawLine (drawing, xs, ys, xe, ye);
         DW_SetAttributes (drawing, line, DW_FigureUserData, &null_attrib);
         TreeInsert (figure_tree, line);
         SetNormalMode ();
	 changeflag = True;
         op_count = 0;
      }
   }
}

void DoLineCB (w, clientData, callData)
   Widget	w;
   XtPointer	clientData;
   XtPointer	callData;
{
   static Point			start;
   static Figure		line;
   static FigureAttributes	attributes;
   DrawingReport		*report;

   report = (DrawingReport *) callData;

   switch (report -> event -> type) {
   case ButtonPress:
      start = report -> snapped;
      ChangeStatusLine ("- Select second point -", False);
      DW_SetInteractive (w, True);
      line = DW_DrawLine (w, start.x, start.y, start.x, start.y);
      DW_GetAttributes (w, line, &attributes);
      break;

   case MotionNotify:
      attributes.points [1] = report -> snapped;
      DW_SetAttributes (w, line, DW_FigurePoints, &attributes);
      break;

   case ButtonRelease:
      DW_RemoveFigure (w, line); 
      DW_SetInteractive (w, False);
      line = DW_DrawLine (w, start.x, start.y, report->snapped.x, report->snapped.y);
      DW_SetAttributes (w, line, DW_FigureUserData, &null_attrib);
      TreeInsert (figure_tree, line);
      SetNormalMode ();
      changeflag = True;
      break;
   }
}
   
void ToolsDrawRectangle ()
{
   Cardinal	count;
   Arg		arglist [2];

   SetEditMode ( );

   count = 0;
   XtSetArg (arglist [count], XtNcursorName, "pencil"); count++;
   XtSetValues (drawing, arglist, count);

   if (!(DW_SetForeground (drawing, canvas -> tool_color)))
      DW_SetForeground (drawing, "black");
  
   ChangeStatusLine ("Select first corner:", True);

   op_count = 0;

   XtRemoveAllCallbacks (drawing, XtNbuttonCallback);
   XtAddCallback (drawing, XtNbuttonCallback, DoRectangleCB, NULL);
   XtOverrideTranslations(entry, 
       XtParseTranslationTable ("<Key>Return: DoRectangleAP()"));
}

void DoRectangleAP ()
{
   static float 	xl,xr,
			yb,yt;
   char		 	*status;
   static Figure	rect;
  
   if (op_count == 0) {
      status = GetTextCoordinates (&xl, &yb, NULL);
      if (status == NULL) {
         op_count ++;
         ChangeStatusLine ("Select second corner:", True);
      }
   }
   else if (op_count == 1) {
      status = GetTextCoordinates (&xr, &yt, NULL);
      if (status == NULL) {
         rect = DW_DrawRectangle (drawing, True, xl, yb, xr - xl, yt - yb);
         DW_SetAttributes (drawing, rect, DW_FigureUserData, &null_attrib); 
         SetNormalMode ();
         changeflag = True;
         op_count = 0;
      }
   }
}

void DoRectangleCB (w, clientData, callData)
   Widget	w;
   XtPointer	callData;
   XtPointer	clientData;
{
   static Point			corner;
   static Figure		rect;
   static FigureAttributes	attributes;
   DrawingReport		*report;

   report = (DrawingReport *) callData;

   switch (report -> event -> type) {
   case ButtonPress:
      ChangeStatusLine ("- Select second point -", False);
      corner = report -> snapped;
      DW_SetInteractive (w, True);
      rect = DW_DrawRectangle (w, True, corner.x, corner.y, 0.0, 0.0);
      break;

   case MotionNotify:
      attributes.width  = report -> snapped.x - corner.x;
      attributes.height = report -> snapped.y - corner.y;
      DW_SetAttributes (w, rect, DW_FigureSize, &attributes);
      break;

   case ButtonRelease:
      DW_RemoveFigure (w, rect); 
      DW_SetInteractive (w, False);
      rect = DW_DrawRectangle (w, True, corner.x, corner.y, 
                                 attributes.width, attributes.height);
      DW_SetAttributes (w, rect, DW_FigureUserData, &null_attrib); 
      TreeInsert (figure_tree, rect);
      SetNormalMode ();
      changeflag = True;
      break;
   }
} 

void ToolsDrawCircle ()
{
   Cardinal	count;
   Arg		arglist [2];

   SetEditMode ( );

   count = 0;
   XtSetArg (arglist [count], XtNcursorName, "pencil"); count++;
   XtSetValues (drawing, arglist, count);

   if (!(DW_SetForeground (drawing, canvas -> tool_color)))
      DW_SetForeground (drawing, "black");
  
   ChangeStatusLine ("Select center:", True);

   op_count = 0;

   XtRemoveAllCallbacks (drawing, XtNbuttonCallback);
   XtAddCallback (drawing, XtNbuttonCallback, DoCircleCB, NULL);
   XtOverrideTranslations(entry, 
       XtParseTranslationTable ("<Key>Return: DoCircleAP()"));
}

void DoCircleCB (w, clientData, callData)
   Widget	w;
   XtPointer	callData;
   XtPointer	clientData;
{
   static Point			center;
   static Figure		circle;
   static FigureAttributes	attributes;
   DrawingReport		*report;
   float			radius;

   report = (DrawingReport *) callData;

   switch (report -> event -> type) {
   case ButtonPress:
      ChangeStatusLine ("- Select point on edge -", False);
      center = report -> snapped; 
      DW_SetInteractive (w, True);
      circle = DW_DrawArc (w, True, center.x, center.y, 0.0, 0.0, 0.0, 360.0);
      break;
 
   case MotionNotify:
      radius = sqrt ((report -> snapped.x - center.x)*
                     (report -> snapped.x - center.x) +
                     (report -> snapped.y - center.y)*
                     (report -> snapped.y - center.y));
      attributes.width  = 2.0*radius;
      attributes.height = 2.0*radius;
      DW_SetAttributes (w, circle, DW_FigureSize, &attributes);
      break;

   case ButtonRelease:
      DW_RemoveFigure (w, circle); 
      DW_SetInteractive (w, False);
      circle = DW_DrawArc (w, True, center.x, center.y, 
             attributes.width, attributes.height, 0.0, 360.0);
      DW_SetAttributes (w, circle, DW_FigureUserData, &null_attrib);
      TreeInsert (figure_tree, circle);
      SetNormalMode ();
      changeflag = True;
      break;
   }
}

void DoCircleAP ()
{
   static float 	xc,yc,
			radius;
   char 		*status;
   static Figure	circle;
  
   if (op_count == 0) {
      status = GetTextCoordinates (&xc, &yc, NULL);
      if (status == NULL) {
         op_count++;
         ChangeStatusLine ("Circle radius:", True);
      }
   }
   else if (op_count == 1) {
      status = GetTextCoordinates (&radius, NULL, NULL);
 
      if (status == NULL) {
         circle = DW_DrawArc (drawing, True, xc, yc, 2*radius, 2*radius, 0.0, 360.0);
         DW_SetAttributes (drawing, circle, DW_FigureUserData, &null_attrib);
         TreeInsert (figure_tree, circle);
         SetNormalMode ();
         changeflag = True;
         op_count = 0;
      }
   }
}

void ToolsDrawArc ()
{
   Cardinal	count;
   Arg		arglist [2];

   SetEditMode ( );

   count = 0;
   XtSetArg (arglist [count], XtNcursorName, "pencil"); count++;
   XtSetValues (drawing, arglist, count);

   ChangeStatusLine ("Select first endpoint:", True);

   if (!(DW_SetForeground (drawing, canvas -> tool_color)))
      DW_SetForeground (drawing, "black");
  
   XtRemoveAllCallbacks (drawing, XtNbuttonCallback);
   XtAddCallback (drawing, XtNbuttonCallback, DoArcCB, NULL);
/*
   XtOverrideTranslations(entry, 
       XtParseTranslationTable ("<Key>Return: DoArcAP()"));
*/
}

void DoArcCB (w, clientData, callData)
   Widget	w;
   XtPointer	clientData;
   XtPointer	callData;
{
   static Point			edge;
   static Figure		arc;
   static FigureAttributes	attributes;
   DrawingReport		*report;
   static float			start;
   static float			x;
   static float			y;
   unsigned long		mask;

   report = (DrawingReport *) callData;

   switch (report -> event -> type) {
   case ButtonPress:
      ChangeStatusLine ("- Select second endpoint -", False);
      edge  = report -> snapped; 
      DW_SetInteractive (w, True);
      arc = DW_DrawArc (w, True, edge.x, edge.y, 0.0, 0.0, 0.0, 90.0);
      break;
 
   case MotionNotify:
      x = edge.x - report -> snapped.x;
      y = edge.y - report -> snapped.y;
      if (x >= 0 && y < 0)
         start = 0.0;
      else if (x < 0 && y < 0)
         start = 90.0;
      else if (x >= 0 && y >= 0)
         start = 270.0;
      else if (x < 0 && y >= 0)
         start = 180.0;

      mask = DW_FigureX | DW_FigureSize | DW_FigureArcStart;
      attributes.x = edge.x - x;
      attributes.width = 2 * x;
      attributes.height = 2 * y;
      attributes.arc_start = start;

      DW_SetAttributes (w, arc, mask, &attributes);
      break;

   case ButtonRelease:
      DW_RemoveFigure (w, arc); 
      DW_SetInteractive (w, False);
      arc = DW_DrawArc (w, True, edge.x-x, edge.y, 2*x, 2*y, start, 90.0);
      DW_SetAttributes (w, arc, DW_FigureUserData, &null_attrib);
      TreeInsert (figure_tree, arc);
      SetNormalMode ();
      changeflag = True;
      break;
   }
}


static int    currline;
static Figure lines  [100];
static FigureAttributes attributes;

void ToolsDrawPolygon ()
{
   Cardinal	count;
   Arg		arglist [2];

   SetEditMode ( );

   count = 0;
   XtSetArg (arglist [count], XtNcursorName, "pencil"); count++;
   XtSetValues (drawing, arglist, count);

   if (!(DW_SetForeground (drawing, canvas -> tool_color)))
      DW_SetForeground (drawing, "black");
  
   currline = -1;
   DW_SetInteractive (drawing, True);
   XtRemoveAllCallbacks (drawing, XtNbuttonCallback);
   XtRemoveAllCallbacks (drawing, XtNmotionCallback);
   XtAddCallback (drawing, XtNmotionCallback, DoPolygonMotionCB, NULL);
   XtAddCallback (drawing, XtNbuttonCallback, DoPolygonButtonCB, NULL);
}


void DoPolygonMotionCB (w, clientData, callData)
   Widget	w;
   XtPointer	clientData;
   XtPointer	callData;
{
   DrawingReport	*report;


   report = (DrawingReport *) callData;

   if (currline >= 0) {
	attributes.points [1] = report -> snapped;
	DW_SetAttributes (w, lines [currline], DW_FigurePoints, &attributes);
   }
}


void DoPolygonButtonCB (w, clientData, callData)
   Widget	w;
   XtPointer	clientData;
   XtPointer	callData;
{
   int   i;
   Point points [100];
   DrawingReport *report;
   static Figure	poly;

   report = (DrawingReport *) callData;

   if (report -> event -> type == ButtonPress) {
	switch (report -> event -> xbutton.button) {
	case 1:
	    lines [++ currline] = DW_DrawLine (w, report -> snapped.x,
		report -> snapped.y, report -> snapped.x, report -> snapped.y);
	    DW_GetAttributes (w, lines [currline], &attributes);
	    break;

	case 2:
	    DW_GetAttributes (w, lines [0], &attributes);
	    lines [++currline] = DW_DrawLine (w, report -> snapped.x,
	    report -> snapped.y, attributes.points[0].x,attributes.points[0].y);

	case 3:
	    currline ++;
	    DW_GetAttributes (w, lines [0], &attributes);
	    points [0] = attributes.points [0];
	    for (i = 0; i < currline; i ++) {
		DW_GetAttributes (w, lines [i], &attributes);
		points [i + 1] = attributes.points [1];
		DW_RemoveFigure (w, lines [i]);
	    }

	    DW_SetInteractive (w, False);
	    poly = DW_DrawPolygon (w, True, points, currline + 1);
            DW_SetAttributes (w, poly, DW_FigureUserData, &null_attrib);
            TreeInsert (figure_tree, poly);
	    SetNormalMode ( );
            changeflag = True;
	    break;
	}
   }
}

void ToolsDrawText ()
{
   SetEditMode ( );

   ChangeStatusLine ("Text string:", True);

   op_count = 0;

   if (!(DW_SetFont (drawing, canvas -> tool_font)))
      DW_SetFont (drawing, "fixed"); 

   if (!(DW_SetForeground (drawing, canvas -> tool_color)))
      DW_SetForeground (drawing, "black");
  
   XtOverrideTranslations(entry, 
       XtParseTranslationTable ("<Key>Return: DoTextAP()"));
}

void DoTextAP ()
{
   static String	result;
   float		x,y;
   char			*status;
   static Figure	text;
   
   if (op_count == 0) {
      result = GetText ();

      if (result != NULL) {
         ChangeStatusLine ("Select text location:", True);
         XtRemoveAllCallbacks (drawing, XtNbuttonCallback);
         XtAddCallback (drawing, XtNbuttonCallback, DoTextCB, &result);
         op_count ++;
      }
      else 
         SetNormalMode ();
   } else {
      status = GetTextCoordinates (&x,&y, NULL);
      if (status == NULL) {
         text = DW_DrawText (drawing, True, x, y, result);
         DW_SetAttributes (drawing, text, DW_FigureUserData, &null_attrib);
         TreeInsert (figure_tree, text);
         SetNormalMode ();
         changeflag = True;
      }
   }
}

void DoTextCB (w, clientData, callData)
   Widget	w;
   XtPointer	clientData,
		callData;
{
   DrawingReport	*report;
   String		result;
   static Figure	text;
 
   report = (DrawingReport *) callData;
   result = (*(String *) clientData);

   text = DW_DrawText (drawing, True, report -> snapped.x, 
                               report -> snapped.y, result);
   DW_SetAttributes (w, text, DW_FigureUserData, &null_attrib);
   TreeInsert (figure_tree, text);

   SetNormalMode ();
   changeflag = True;
}
