/*
    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:	generate.c
 *
 ***************************************************************************/

# include <X11/Intrinsic.h>
# include <stdlib.h>
# include "allocate.h"
# include "dialog.h"
# include "fe.h"
# include "ElementList.h"
# include "Constraint.h"
# include "Material.h"
# include "Trimesh.h"
# include "Canvas.h"
# include "Drawing.h"
# include "vfe.h"
# include "procedures.h"
# include "problem.h"
# include "globals.h"
# include "misc.h"
# include "error.h"

extern ConstraintDialog constraint_d;
extern MaterialDialog	material_d;
extern ElementList	element_l;

static unsigned		op_count;
static struct _grid	grid;
static TriMesh          trimesh;
static unsigned		numholes;
static unsigned		maxvc;
static unsigned		curr_vc;
static unsigned		curr_curve;
static Figure		marker [100];
static unsigned		mk_count;

typedef double	dbl_pair [2];

void GenerateElements ( ) 
{ 
   Definition	active_definition;


   if (ConstraintDialogActive (constraint_d) == NULL) {
      error ("No active constraint defined.");
      return;
   }
   if (MaterialDialogActive (material_d) == NULL) {
      error ("No active material defined.");
      return;
   }

   active_definition = ElementListDefinition (element_l);
   if (active_definition == NULL) {
      error ("No active element type defined.");
      return;
   } 

   op_count = 0;
   if (active_definition -> numnodes == 2) 
      SetupGridGeneration ( );
   else if (active_definition -> numnodes == 3)
      SetupTriangleGeneration ( );
   else 
      error ("The current type of element cannot be generated."); 
}

void SetupGridGeneration ( )
{
   SetEditMode ();

   XtRemoveAllCallbacks (drawing, XtNbuttonCallback);
   XtAddCallback (drawing, XtNbuttonCallback, GridCB, NULL);

   XtOverrideTranslations (entry, 
      XtParseTranslationTable ("<Key>Return: GridAP()"));

   ChangeStatusLine ("bottom left corner x,y:",True); 
} 

void GridCB (w, clientData, callData)
   Widget	w;
   XtPointer	clientData;
   XtPointer	callData;
{
   DrawingReport	*report;
   float		x, y;

   report = (DrawingReport *) callData;

   if (report -> event -> type == ButtonPress) {
  
      if (report -> event -> xbutton.button == 3) {
         QuitEdit ();
         return;
      }
      else if (report -> event -> xbutton.button != 1)
         return;

      switch (op_count) {

      case 0:
         x = report -> snapped.x;
         y = report -> snapped.y;

         grid.xs = x;
         grid.ys = y;
         grid.zs = 0.0;
         op_count++;
         ChangeStatusLine ("top right corner x,y:",True);
         return;
      
      case 1:   
         x = report -> snapped.x;
         y = report -> snapped.y;

         grid.xe = x;
         grid.ye = y;
         grid.ze = 0.0;
         op_count++;
         ChangeStatusLine ("grid size (x number, y number):", True);
         return;
      
      }
   }
}
   
void GridAP ()
{
   
   float		x,y;
   char			*status;


   switch (op_count) {

   case 0:
      status = GetTextCoordinates (&x, &y, NULL);       
      if (status != NULL)
         return;

      grid.xs = x;
      grid.ys = y;
      grid.zs = 0.0;
      op_count++;
      ChangeStatusLine ("top left corner x,y:",True);
      return;

   case 1: 
      status = GetTextCoordinates (&x, &y, NULL);
      if (status != NULL)
         return;

      grid.xe = x;
      grid.ye = y;
      grid.ze = 0.0;
      op_count++;
      ChangeStatusLine ("grid size (x number, y number):", True);
      return;

   case 2:
      status = GetTextCoordinates (&x, &y, NULL);
      if (status != NULL)
         return;

      grid.xnumber = (unsigned) x;
      grid.ynumber = (unsigned) y;
      grid.znumber = 0;
      op_count = 0;
      DoGrid ();
      SetNormalMode ();
      return;
   }      
}

void DoGrid ()
{
   Definition		active_definition;
   Element		mxelt;
   Node			mxnode;
   Element		*element;
   Node			*node;
   unsigned		mxnode_number,
			mxelt_number;
   unsigned		nn, ne;
   unsigned		i;

   mxnode = (Node) TreeMaximum (problem.node_tree);
   if (mxnode != NULL)
      mxnode_number = mxnode -> number;
   else
      mxnode_number = 0;

   mxelt = (Element) TreeMaximum (problem.element_tree);
   if (mxelt != NULL)
      mxelt_number = mxelt -> number;
   else
      mxelt_number = 0;

   active_definition = ElementListDefinition (element_l);


   if (GenerateGrid (&grid,&element,&node, active_definition,
                     &ne,&nn,mxnode_number,mxelt_number))
      return;

   for (i = 1 ; i <= ne ; i++) {
      element [i] -> definition = active_definition;
      element [i] -> material = MaterialDialogActive (material_d);

      DrawElement (element [i], (i == 1 ? True : False));
   }

   for (i = 1 ; i <= nn ; i++) {
      node[i] -> constraint = ConstraintDialogActive (constraint_d);

      DrawNode (node[i], (i == 1 ? True : False));
   }

   error ("Generated %d nodes and %d elements.", nn, ne);
   changeflag = True;
}
   
void SetupTriangleGeneration ( )
{
   static unsigned		flag = 1;
   static TrimeshDialog		trimesh_d;
   unsigned			i;
  
   if (flag) {
      trimesh_d = TrimeshDialogCreate(toplevel,"trimeshDialog","Trimesh Form");
      flag = 0;
   }

   trimesh = TrimeshDialogPopup (trimesh_d); 
   if (trimesh == NULL) 
      return;

   trimesh -> curves = Allocate (Curve, trimesh -> numcurves);

   maxvc = 20;

   for (i = 0 ; i < trimesh -> numcurves ; i++) {
      trimesh -> curves [i] = Allocate (struct _curve, 1);
   
      trimesh -> curves [i] -> vcl = Allocate (dbl_pair, maxvc);
      trimesh -> curves [i] -> numvc = 0;
   }

   AssignQuitAbort (FinishCurve, "FinishCurve", AbortTriMesh, "AbortTriMesh");

   XtOverrideTranslations (entry,
      XtParseTranslationTable ("Shift<Key>BackSpace: BackupOnePoint()"));

   XtOverrideTranslations (entry,
       XtParseTranslationTable ("<Key>Return: AddCurvePointAP()"));

   XtRemoveAllCallbacks (drawing, XtNbuttonCallback);

   XtAddCallback (drawing, XtNbuttonCallback, AddCurvePointCB, NULL);

   if (DW_SetForeground (drawing, canvas -> tool_color) == False)
      (void) DW_SetForeground (drawing, "black");

   mk_count = 0;

   op_count = 0;   
   curr_curve = 0;
   curr_vc = 0;
   ChangeStatusLine ("Select first boundary point:", True);
   SetEditMode ();
}

void AddCurvePointAP ()
{
   char		*status;
   float	x,y;

   status = GetTextCoordinates (&x, &y, NULL);
   if (status != NULL)
      return;

   DoAddCurvePoint (x,y);
}

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

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

   if (report -> event -> xbutton.button == 3) {
      FinishCurve ( );
      return;
   } 
   else if (report -> event -> xbutton.button != 1)
      return;

   DoAddCurvePoint (report -> snapped.x, report -> snapped.y);
}

static char *ordinals [ ] = {"", "first", "second", "third", "fourth", "fifth",
                        "sixth", "seventh", "eighth", "ninth", "tenth",
                        "eleventh", "twelfth", "thirteenth", "fourteenth"};

void DoAddCurvePoint (x,y)
   float	x,y;
{
   static char 		message [80];

   trimesh -> curves [curr_curve] -> vcl [curr_vc][0] = x;
   trimesh -> curves [curr_curve] -> vcl [curr_vc][1] = y;

   marker [mk_count++] =  DW_FillArc (drawing, False, x, y,6.0,6.0,0.0,360.0);

   curr_vc++;
   if (curr_vc >= maxvc) {
      trimesh -> curves [curr_curve] -> vcl = 
         Reallocate (trimesh -> curves [curr_curve] -> vcl, dbl_pair, 20);

      maxvc += 20;
   }

   sprintf (message, "Select %s %s point:", 
            (curr_vc > 13 ? "next" : ordinals [curr_vc + 1]),
            (curr_curve == 0 ? "boundary" : "hole"));

   ChangeStatusLine (message, True);
}

void DoTriMeshGeneration ()
{
   Definition	active_definition;
   Element	*element;
   Node		*node;
   unsigned	ne,nn;
   Element	mxelt;
   Node		mxnode;
   unsigned	maxnode;
   unsigned	maxelt;
   unsigned	i;

  
   mxnode = (Node) TreeMaximum (problem.node_tree);
   if (mxnode != NULL)
      maxnode = mxnode -> number;
   else
      maxnode = 0;

   mxelt = (Element) TreeMaximum (problem.element_tree);
   if (mxelt != NULL)
      maxelt = mxelt -> number; 
   else
      maxelt = 0;
 
   DW_SetAutoRedraw (drawing, False);
    
   for (i = 0 ; i < mk_count ; i++)
      DW_RemoveFigure (drawing, marker [i]);
 
   DW_SetAutoRedraw (drawing, True);

   active_definition = ElementListDefinition (element_l);
      
   if (GenerateTriMesh (trimesh,&element,&node,active_definition,
                        &ne,&nn,maxnode,maxelt)) {
      for (i = 0 ; i < trimesh -> numcurves ; i++) {
         Deallocate (trimesh -> curves [i] -> vcl);
         Deallocate (trimesh -> curves [i]);
      }
      Deallocate (trimesh -> curves);

      return;
   }

   for (i = 1 ; i <= ne ; i++) {
      element [i] -> definition = active_definition;
      element [i] -> material = MaterialDialogActive (material_d);

      DrawElement (element [i], (i == 1 ? True : False));
   }     
 
   for (i = 1 ; i <= nn ; i++) {
      node[i] -> constraint = ConstraintDialogActive (constraint_d);

      DrawNode (node[i], (i == 1 ? True : False));
   }
  
   for (i = 0 ; i < trimesh -> numcurves ; i++) {
      Deallocate (trimesh -> curves [i] -> vcl);
      Deallocate (trimesh -> curves [i]);
   }

   Deallocate (trimesh -> curves);

   error ("Generated %d nodes and %d elements.", nn, ne);
   changeflag = True;
}

void FinishCurve ()
{
   trimesh -> curves [curr_curve] -> numvc = curr_vc;
   maxvc = 20;
   curr_vc = 0;
   curr_curve++; 

   if (curr_curve >= trimesh -> numcurves) {
      DoTriMeshGeneration ();
      XtOverrideTranslations (entry,
         XtParseTranslationTable ("Shift<Key>BackSpace: no-op()"));

      SetNormalMode ();
   }
   else 
      ChangeStatusLine ("Select first hole point:", True);
}

void AbortTriMesh ()
{
   unsigned	i;

   for (i = 0 ; i < trimesh -> numcurves ; i++) {
      Deallocate (trimesh -> curves [i] -> vcl);
      Deallocate (trimesh -> curves [i]);
   }

   Deallocate (trimesh -> curves);  

   DW_SetAutoRedraw (drawing, False);

   for (i = 0 ; i < mk_count ; i++)
      DW_RemoveFigure (drawing, marker [i]);

   DW_SetAutoRedraw (drawing, True);

   XtOverrideTranslations (entry,
      XtParseTranslationTable ("Shift<Key>BackSpace: no-op()"));

   SetNormalMode ();
}

void BackupOnePoint ()
{
   char		message [80];

   if (curr_vc == 0)
      return;

   curr_vc--;
   sprintf (message,"Deleted. Select %s %s point:",
      (curr_vc > 13 ? "next" : ordinals [curr_vc + 1]),
      (curr_curve == 0 ? "boundary" : "hole"));

   ChangeStatusLine (message, True);

   mk_count --;
   DW_RemoveFigure (drawing, marker [mk_count]);
}
