/*
    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.
*/

# include <stdio.h>
# include <X11/Intrinsic.h>
# include <X11/StringDefs.h>
# include <X11/Xaw/Viewport.h>
# include <X11/Shell.h>
# include "fe.h"
# include "FileDialog.h"
# include "OutputDialog.h"
# include "ElementList.h"
# include "Constraint.h"
# include "Element.h"
# include "Node.h"
# include "Material.h"
# include "Force.h"
# include "Load.h"
# include "Canvas.h"
# include "Solution.h"
# include "dialog.h"
# include "problem.h"
# include "globals.h"
# include "procedures.h"
# include "objects.h"
# include "Drawing.h"
# include "vfe.h"

# ifndef LIBDIR
# define LIBDIR "/usr/local/lib/felt"
# endif

extern FileDialog 	filed;
extern ConstraintDialog constraint_d;
extern ForceDialog	force_d;
extern LoadDialog	load_d;
extern MaterialDialog	material_d;
extern ElementList	element_l;
extern CanvasDialog	canvas_d;
extern SolutionDialog	solution_d;
extern ElementDialog	element_d;
extern NodeDialog	node_d;

void OpenFile ()
{
   String	ans;
   static char	suggestion [256] = "";

   if (changeflag == True)
      if (!QuerySave ()) return;

   strcpy (suggestion, filename);
         
   CenterOnScreen (XtNameToWidget (toplevel, "fileDialog"), True);
   WarpToCenter (XtNameToWidget (toplevel, "fileDialog"));
   FileDialogSelect (filed, "Open Problem","Problem name:", suggestion, &ans);

   if (ans != NULL) {
      changeflag = False;
      strcpy (filename, ans);
      if (!ReadFeltFile ()) {
	 UpdateFilenameDisplay ();
         ReadVelvetFile ();
      }
   } 
}

void RestoreOriginal ()
{
   String	selected;

   if (changeflag == True) {
      OutputDialogPrintf (proceed_dialog, "Are you sure?");
      CenterOnWidget (OutputDialogShell(proceed_dialog), toplevel, True);
      WarpToCenter (OutputDialogShell (proceed_dialog));
      selected = OutputDialogSelect (proceed_dialog, "Restore Problem", "okay");
 
      if (selected && strcmp (selected,"okay"))
	 return;

      changeflag = False;
   }

   if (!ReadFeltFile ( )) {
      UpdateFilenameDisplay ( );
      ReadVelvetFile ( );
   }
} 
   
void WriteNamedFile ()
{
   String	ans;

   CenterOnScreen (XtNameToWidget (toplevel, "fileDialog"), True);
   WarpToCenter (XtNameToWidget (toplevel, "fileDialog"));
   FileDialogSelect (filed, "Save Problem","Problem name:", filename, &ans);

   if (ans != NULL) {
      strcpy (filename, ans);
      UpdateFilenameDisplay ();

      if (!WriteVFeltFile ())
         WriteVelvetFile ();

      changeflag = False;
   }
}


int WriteVFeltFile ()
{
    FILE *output;


    if ((output = fopen (filename, "w")) == NULL) {
	error ("Could not create file %s.", filename);
	return 1;
    }

    CompactOperation (output);
    (void) fclose (output);

    return 0;
}


int ReadFeltFile ( )
{
    int   status, i;
    FILE *input;
    int   usecpp = 1;
    char  cpp_command [256];
    char *cpp = "/lib/cpp";
    Element elt;

    if (freopen (filename, "r", stdin) == NULL) {
	error ("Could not access file %s.", filename);
	return 1;
    }

    if (usecpp) {
	sprintf (cpp_command, "%s -I%s", cpp, LIBDIR);
	if ((input = popen (cpp_command, "r")) == NULL) {
	    error ("Could not execute %s.", cpp);
	    return 1;
	}
    } else
	input = stdin;

    DW_RemoveAll (drawing);
    BufferErrors (True);
    status = GetProblem (input, &element, &node, 
                         &numelts, &numnodes, &(solution -> title));
    (void) pclose (input);
    BufferErrors (False);

    if (status) {
	StartNew ( );
	return 1;
    }

    DrawProblem (0.0);

    ConstraintDialogUpdate (constraint_d, problem.constraint_tree);
    ForceDialogUpdate	   (force_d, problem.force_tree);
    LoadDialogUpdate	   (load_d, problem.distributed_tree);
    MaterialDialogUpdate   (material_d, problem.material_tree);
    NodeDialogUpdate	   (node_d, problem.node_tree, problem.force_tree,
			    problem.constraint_tree);
    ElementDialogUpdate	   (element_d, problem.element_tree,
			    problem.material_tree, problem.distributed_tree,
			    problem.node_tree);

    SolutionDialogUpdate (solution_d);

    elt = (Element) TreeMinimum (problem.element_tree);
    if (elt != NULL) 
        ElementListSet (element_l, elt -> definition);
    else 
        ElementListSet (element_l, NULL);

    return 0;
}

static FILE	*vfile;

static int FigureWriteIterator (item)
   Item		item;
{
   FigureAttributes	attr;
   Figure		figure;
   float		fdata [20];
   int			length;
   unsigned		j;

   figure = (Figure) item;

   DW_GetAttributes (drawing, figure, &attr);
   fwrite ((FigureType *) &(attr.type), sizeof(FigureType), 1, vfile);
   
   switch (attr.type) {

   case LineFigure:
      fdata [0] = attr.points [0].x;
      fdata [1] = attr.points [0].y;
      fdata [2] = attr.points [1].x;
      fdata [3] = attr.points [1].y;
      fwrite ((float *) fdata, sizeof(float), 4, vfile);
      break;

   case RectangleFigure:
      fdata [0] = attr.x;
      fdata [1] = attr.y;
      fdata [2] = attr.width;
      fdata [3] = attr.height;
      fwrite ((float *) fdata, sizeof(float), 4, vfile);
      break;

   case TextFigure:
      fdata [0] = attr.x;
      fdata [1] = attr.y;
      length = strlen(attr.text);
      fwrite ((float *) fdata, sizeof(float), 2, vfile);
      fwrite ((int *) &length, sizeof(int), 1, vfile);
      fwrite ((char *) attr.text, sizeof(char), length, vfile);
      break;

   case ArcFigure:
      fdata [0] = attr.x;
      fdata [1] = attr.y;
      fdata [2] = attr.width;
      fdata [3] = attr.height;
      fdata [4] = attr.arc_start;
      fdata [5] = attr.arc_length;
      fwrite ((float *) fdata, sizeof(float), 6, vfile);
      break;

   case PolygonFigure:
      fwrite ((int *) (&attr.npoints), sizeof(int), 1, vfile);
      for (j = 0 ; j < attr.npoints ; j++)
         fwrite ((Point *) (&attr.points[j]), sizeof(Point), 1, vfile); 
      break;

   case GroupFigure:
      break;
   }   

   return 0;
}

String FormVelvetFilename ()
{
   static char	name [2048];
   int		length;

   length = strlen (filename);
   if (length >= 4) {
      if (strcmp (&(filename[length-4]), ".flt") == 0) {
         strcpy (name, filename);
         strcpy (name + length - 4, ".vlv");
      } else {
         strcpy (name, filename);
         strcat (name, ".vlv");  
      }
   }
   else {
      strcpy (name, filename);
      strcat (name, ".vlv");  
   }
   return name;
}
   
void WriteVelvetFile ()
{
   Arg			arglist [3];
   Cardinal		count;
   Position		x_pos,y_pos;
   Dimension		width, height;
   float		scale;
   int			idata [7];
   float		fdata [7];
   int			num_figs;

   vfile = fopen (FormVelvetFilename(), "w");

   if (vfile == NULL) {
      error ("Could not write settings file.");
      return;
   }

   count = 0;
   XtSetArg (arglist [count], XtNx, &x_pos); count++;
   XtSetArg (arglist [count], XtNy, &y_pos); count++;
   XtSetArg (arglist [count], XtNxScale, &scale); count++;
   XtGetValues (drawing, arglist, count);

   count = 0;
   XtSetArg (arglist [count], XtNwidth, &width); count++;
   XtSetArg (arglist [count], XtNheight, &height); count++;
   XtGetValues (viewport, arglist, count);

   idata[0] = canvas -> node_numbers | (canvas -> element_numbers << 1);
   idata[1] = canvas -> snap;
   idata[2] = canvas -> grid;
   idata[3] = x_pos;
   idata[4] = y_pos;
   idata[5] = height;
   idata[6] = width;

   fwrite ((int *) idata, sizeof (int), 7, vfile); 

   fdata[0] = canvas -> xmin;
   fdata[1] = canvas -> xmax;
   fdata[2] = canvas -> ymin;
   fdata[3] = canvas -> ymax;
   fdata[4] = scale;
   fdata[5] = canvas -> grid_size;
   fdata[6] = canvas -> snap_size;

   fwrite ((float *) fdata, sizeof (float), 7, vfile);
 
	/*
	 * write out all the tools, switching on type so we know which	
	 * things to write ... we'll just do the reverse on the way back in
	 */

   num_figs = TreeSize (figure_tree);
   fwrite ((int *) &num_figs, sizeof (int), 1, vfile);

   TreeSetIterator (figure_tree, FigureWriteIterator);
   TreeIterate (figure_tree);

   fclose (vfile); 
}

void ReadVelvetFile ()
{
   Arg		arglist [10];
   Cardinal	count;
   Position	x_pos,y_pos;
   float	scale;
   int		idata [7];
   float	fdata [7];
   Dimension	width,height;
   Boolean	number_state,
		grid_state,
		snap_state;
   int		num_figs;
   int		length;
   char		text [256];
   unsigned	i,j;
   FigureType	type;
   Figure	figure;
   int		npoints;
   Point	point [100];

   vfile = fopen (FormVelvetFilename(), "r");

   if (vfile == NULL) 
      return;

   fread ((int *) idata, sizeof (int), 7, vfile); 

   number_state = idata[0];
   snap_state = idata[1];
   grid_state = idata[2];

   if ((number_state & 1) != canvas -> node_numbers) 
      ToggleNodeNumberStatus ();
   if ((number_state & 2) != canvas -> element_numbers)
      ToggleEltNumberStatus ();
   if (grid_state != canvas -> grid) 
      ToggleGridStatus ();
   if (snap_state != canvas -> snap)
      ToggleSnapStatus ();
   
   x_pos = idata[3];
   y_pos = idata[4];
   height = idata[5];
   width = idata[6];

   fread ((float *) fdata, sizeof (float), 7, vfile);

   canvas -> xmin = fdata[0];
   canvas -> xmax = fdata[1];
   canvas -> ymin = fdata[2];
   canvas -> ymax = fdata[3];
   scale = fdata[4];
   canvas -> grid_size = fdata[5];
   canvas -> snap_size = fdata[6];

	/*
	 * read in all the tools, switching on type so we know which	
	 * things to read ... 
	 */

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

   if (DW_SetFont (drawing, canvas -> tool_font) == False)
	(void) DW_SetFont (drawing, "fixed");

   fread ((int *) &num_figs, sizeof(int), 1, vfile);
   for (i = 0 ; i < num_figs ; i++) {
      fread ((FigureType *) &type, sizeof(FigureType), 1, vfile);
      switch (type) {

      case LineFigure:
         fread ((float *) fdata, sizeof(float), 4, vfile);
         figure = DW_DrawLine (drawing, fdata [0],fdata [1],
                               fdata [2],fdata [3]);
         break;

      case RectangleFigure:
         fread ((float *) fdata, sizeof(float), 4, vfile);
         figure = DW_DrawRectangle (drawing, True, fdata [0], fdata [1], 
                                    fdata [2], fdata [3]);
         break;

      case TextFigure:
         fread ((float *) fdata, sizeof(float), 2, vfile);
         fread ((int *) &length, sizeof(int), 1, vfile);
         fread ((char *) text, sizeof(char), length, vfile);  
         figure = DW_DrawText (drawing, True, fdata[0], fdata[1], text);
         break;

      case ArcFigure:
         fread ((float *) fdata, sizeof(float), 6, vfile);
         figure = DW_DrawArc (drawing, True, fdata [0], fdata [1],
                              fdata [2], fdata [3], fdata [4], fdata [5]);
         break;

      case PolygonFigure:
         fread ((int *) &npoints, sizeof(int), 1, vfile);
         for (j = 0 ; j < npoints ; j++) 
            fread ((Point *) &(point[j]), sizeof(Point), 1, vfile);
 
         figure = DW_DrawPolygon (drawing, True, point, npoints);
         break;

      case GroupFigure:
	 break;
      }
      TreeInsert (figure_tree, figure);
   }

   fclose (vfile); 

   CanvasDialogSet (canvas_d);

   count = 0;
   XtSetArg (arglist [count], XtNxScale, Float2Arg(scale)); count++;
   XtSetArg (arglist [count], XtNyScale, Float2Arg(scale)); count++;
   XtSetValues (drawing, arglist, count);

   count = 0;
   XtSetArg (arglist [count], XtNheight, height); count++;
   XtSetArg (arglist [count], XtNwidth, width); count++;
   XtSetValues (viewport, arglist, count);

   XawViewportSetCoordinates (viewport, -x_pos, -y_pos);
}


void StartNew ()
{
   if (changeflag) 
      if (!QuerySave ()) return; 

   changeflag = False;
   filename [0] = 0;
   UpdateFilenameDisplay ();
   solution -> title = XtNewString ("Unnamed");
   SolutionDialogUpdate (solution_d);

   DW_RemoveAll (drawing);
   DestroyProblem ((ItemDestructor) DestroyMaterial);
   TreeDestroy (figure_tree);

   ElementListSet (element_l, NULL);

   problem.node_tree        = TreeCreate (node_cmp);
   problem.element_tree     = TreeCreate (element_cmp);
   problem.force_tree       = TreeCreate (force_cmp);
   problem.material_tree    = TreeCreate (material_cmp);
   problem.constraint_tree  = TreeCreate (constraint_cmp);
   problem.distributed_tree = TreeCreate (distributed_cmp);
   figure_tree		    = TreeCreate (figure_cmp);

    ConstraintDialogUpdate (constraint_d, problem.constraint_tree);
    ForceDialogUpdate	   (force_d, problem.force_tree);
    LoadDialogUpdate	   (load_d, problem.distributed_tree);
    MaterialDialogUpdate   (material_d, problem.material_tree);
    NodeDialogUpdate	   (node_d, problem.node_tree, problem.force_tree,
			    problem.constraint_tree);
    ElementDialogUpdate	   (element_d, problem.element_tree,
			    problem.material_tree, problem.distributed_tree,
			    problem.node_tree);
}

Boolean QuerySave ()
{
    String	selected;

    OutputDialogPrintf (qsave_dialog,"Changes not saved, save now?");
    CenterOnWidget (OutputDialogShell(qsave_dialog), toplevel, True);
    WarpToCenter (OutputDialogShell (qsave_dialog));
    selected = OutputDialogSelect(qsave_dialog,"Are You Sure?","no");

    if (selected == NULL || strcmp (selected,"cancel") == 0) 
        return False;
    else if (strcmp (selected,"yes") == 0) {
       if (!WriteVFeltFile ())
          WriteVelvetFile ();

       return True;
    }
  
    return True;
}
    
void QuitVelvet ()
{
   if (changeflag) 
      if (!QuerySave ()) return;

   XtUnmapWidget (toplevel);
   XtDestroyApplicationContext (app_context);
   exit (0);
}

Problem     saved;

static int UpdateMaterial (item)
    Item item;
{
    Item     found;
    Material old;
    Material new;


    found = TreeInsert (saved.material_tree, item);
    if (found != item) {
	new = (Material) item;
	old = (Material) found;
	old -> E = new -> E;
	old -> A = new -> A;
	old -> Ix = new -> Ix;
	old -> Iy = new -> Iy;
	old -> Iz = new -> Iz;
	old -> J = new -> J;
	old -> G = new -> G;
	old -> nu = new -> nu;
	old -> t = new -> t;
	old -> rho = new -> rho;
	old -> kappa = new -> kappa;
	DestroyMaterial (new);
    }
    return 0;
}


void OpenMaterialFile ( )
{
    FILE       *fp;
    String	ans;
    String      title;
    static char suggestion [256] = "";
    int         status;
    int         usecpp = 1;
    char       *cpp = "/lib/cpp";


    CenterOnScreen (XtNameToWidget (toplevel, "fileDialog"), True);
    WarpToCenter (XtNameToWidget (toplevel, "fileDialog"));
    FileDialogSelect (filed, "Open Material Database","Database name:",
                      suggestion, &ans);

    if (ans == NULL)
	return;


    if (freopen (ans, "r", stdin) == NULL) {
	error ("Could not access file %s.", ans);
	return;
    }

    if (usecpp) {
	if ((fp = popen (cpp, "r")) == NULL) {
	    error ("Could not execute %s.", cpp);
	    return;
	}
    } else
	fp = stdin;

    saved = problem;
    BufferErrors (True);
    status = GetProblem (fp, &element, &node, &numelts, &numnodes, &title);
    BufferErrors (False);
    (void) pclose (fp);

    if (status)
	DestroyProblem ((ItemDestructor) DestroyMaterial);
    else {
	(void) TreeSetIterator (problem.material_tree, UpdateMaterial);
	(void) TreeIterate (problem.material_tree);
    }

    DestroyProblem (NULL);
/*
    ZeroOffset (element);
    Deallocate (element);
    ZeroOffset (node);
    Deallocate (node);
*/
    problem = saved;

    MaterialDialogUpdate (material_d, problem.material_tree);
    changeflag = True;
}


static FILE *fp;

static int WriteMaterial (item)
    Item item;
{
    Material material = (Material) item;


    if (strpbrk (material -> name, "- \t\n=[]\",+*/()#"))
	fprintf (fp, "\"%s\"  ", material -> name);
    else
	fprintf (fp, "%s  ", material -> name);

    fprintf (fp, "E=%g A=%g ", material -> E, material -> A);
    fprintf (fp, "Ix=%g Iy=%g ", material -> Ix, material -> Iy);
    fprintf (fp, "Iz=%g J=%g ", material -> Iz, material -> J);
    fprintf (fp, "G=%g nu=%g ", material -> G, material -> nu);
    fprintf (fp, "t=%g rho=%g ", material -> t, material -> rho);
    fprintf (fp, "kappa=%g\n", material -> kappa);
    return 0;
}


void WriteMaterialFile ( )
{
    String 	ans;
    static char suggestion [256] = "";


    CenterOnScreen (XtNameToWidget (toplevel, "fileDialog"), True);
    WarpToCenter (XtNameToWidget (toplevel, "fileDialog"));
    FileDialogSelect (filed, "Update Material Database","Database name:",
                      suggestion, &ans);

    if (ans == NULL)
	return;

    if ((fp = fopen (ans, "w")) == NULL) {
	error ("Unable to update file %s.\n", ans);
	return;
    }

    fprintf (fp, "material properties\n");
    (void) TreeSetIterator (problem.material_tree, WriteMaterial);
    (void) TreeIterate (problem.material_tree);
    fprintf (fp, "\nend\n");
    (void) fclose (fp);
}

void UpdateFilenameDisplay ()
{
   Arg	arglist [1];

   if (filename [0])
      XtSetArg (arglist [0], XtNtitle, filename);
   else
      XtSetArg (arglist [0], XtNtitle, "UnNamed");

   XtSetValues (toplevel, arglist, 1);
}

void SaveWidgetXWD (widget)
   Widget	widget;
{
   String	xwd_file;
   FILE		*output;

   CenterOnScreen (XtNameToWidget (toplevel, "fileDialog"), True);
   WarpToCenter (XtNameToWidget (toplevel, "fileDialog"));
   FileDialogSelect (filed, "XWD Dump", "XWD file name:", "", &xwd_file);

   if (xwd_file == NULL)
      return;

   output = fopen (xwd_file, "w");
   if (output == NULL) {
      error ("could not open file for writing");
      return;
   }
 
   if (DumpWidget (widget, output)) {
      fclose (output);
      unlink (xwd_file);
      return;
   }

   fclose (output); 

   return;
}
