/*
    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 <unistd.h>
# include <X11/Intrinsic.h>
# include <X11/StringDefs.h>
# include <X11/Xaw/Simple.h>
# include "Canvas.h" 
# include "Element.h"
# include "Node.h"
# include "Solution.h"
# include "dialog.h"
# include "fe.h"
# include "Tree.h"
# include "problem.h"
# include "globals.h"
# include "Drawing.h"
# include "vfe.h"
# include "procedures.h"
# include "error.h"

extern ElementDialog element_d;
extern NodeDialog    node_d;


/****************************************************************************
 *
 * Function:	SolveProblem
 *
 * Description:	Basically main from the Felt application ... pretty 
 *		straightforward
 *
 ***************************************************************************/

int SolveProblem (element, node, numelts, numnodes, title)
    Element	*element;
    Node	*node;
    unsigned	numelts,
		numnodes;
    char	*title;
{
    int		 i;			/* loop variable		*/
    unsigned	 count;			/* number of affected dofs	*/
    Matrix	 K,			/* global stiffness matrix	*/
		 Kcond;			/* condensed stiffness matrix	*/
    Vector	 F,			/* force vector			*/
		 Fcond,			/* condensed force vector	*/
		 d;			/* displacement vector		*/
    unsigned	 dofs [7];		/* degrees of freedom		*/
    int		 status;		/* return status		*/
    Reaction	 *R;			/* reaction force vector	*/
    unsigned	 numreactions;		/* the number of reactions	*/
   
    char	*temp_out;
    FILE	*output;

        /*
         * Do everything
         */

    ClearNodes (node, numnodes);

    count = FindDOFS (element, numelts, dofs);

    K = AssembleStiffness (element, numelts, numnodes, count, dofs, &status);

    if (status) {
	error ("%d fatal errors in element stiffness definitions.", status);

        DestroyMatrix (K);
        return 1;
    }

    F = ConstructForceVector (node, numnodes, count, dofs);

    ResolveConstraints (K, F, &Kcond, &Fcond, node, numnodes, count, dofs);

    d = SolveForDisplacements (Kcond, Fcond, node, numnodes, count, dofs);

    if (d == NullVector) {
       DestroyMatrix (Kcond);
       DestroyMatrix (K);
       DestroyVector (F);
       DestroyVector (Fcond);

       return 1;
    }    

    numreactions = SolveForReactions (K, d, node, numnodes, count, dofs, &R);

    status = EltStresses (element, numelts);
    if (status) {
       error ("%d errors found computing element stresses.",status);
       return 1;
    } 

    temp_out = tempnam (NULL,"velv");
    output = fopen (temp_out,"w");
    if (output == NULL) {
       error ("Could not open temporary file for output.");
       return 1;
    }

    WriteOutput (output,title,element,node,R,numelts,numnodes,numreactions,0);
    fclose (output);

    OutputDialogView (output_dialog, temp_out, 20, 80);

    unlink (temp_out);
    XtFree ((char *) temp_out);

    if (solution -> felt) 
       OutputWindowPopup ( );

    if (solution -> stress) 
       SetupStresses (False);
       
    if (solution -> structure)
       SetupStructure (False);

    DestroyMatrix (K);
    DestroyMatrix (Kcond);
    DestroyVector (F);
    DestroyVector (Fcond);

    if (numreactions) {
	for (i = 1; i <= numreactions; i ++)
	    XtFree ((char *) R [i]);

	XtFree ((char *) (R + 1));
    }

    return 0;
}


static unsigned count;
static FigureAttributes attr;
static char buffer [10];
static Boolean renumber_redraw;


static int SetNodeArray (item)
    Item item;
{
    Node n = (Node) item;
    Drawn drawn = (Drawn) n -> aux;


    node [++ count] = n;

    if (n -> number != count) {
	n -> number = count;
	if (drawn -> label != NULL) {
	    if (renumber_redraw == True)
		DW_SetAutoRedraw (drawing, renumber_redraw = False);
	    sprintf (attr.text = buffer, " %d", count);
	    DW_SetAttributes (drawing, drawn -> label, DW_FigureText, &attr);
	}
    }

    return 0;
}


static int SetElementArray (item)
    Item item;
{
    Element e = (Element) item;
    Drawn drawn = (Drawn) e -> aux;


    element [++ count] = e;

    if (e -> number != count) {
	e -> number = count;
	if (drawn -> label != NULL) {
	    if (renumber_redraw == True)
		DW_SetAutoRedraw (drawing, renumber_redraw = False);
	    ComputeCenter (element, &attr.x, &attr.y);
	    sprintf (attr.text = buffer, "%d", count);
	    DW_SetAttributes (drawing, drawn -> label, DW_FigureText |
						DW_FigureLocation, &attr);
	}
    }

    return 0;
}


void CompactOperation (output)
    FILE *output;
{
    numnodes = TreeSize (problem.node_tree);
    numelts = TreeSize (problem.element_tree);

    if (numnodes == 0) {
	error ("No nodes defined.");
	return;
    }

    if (numelts == 0) {
	error ("No elements defined.");
	return;
    }

    node = (Node *) XtMalloc (sizeof (Node) * numnodes);
    element = (Element *) XtMalloc (sizeof (Element) * numelts);
    node --;
    element --;

    (void) TreeSetIterator (problem.node_tree, SetNodeArray);
    (void) TreeSetIterator (problem.element_tree, SetElementArray);

    count = 0;
    renumber_redraw = True;
    (void) TreeIterate (problem.node_tree);
    count = 0;
    (void) TreeIterate (problem.element_tree);

    if (renumber_redraw == False && (canvas -> node_numbers || 
                                     canvas -> element_numbers))
	DW_SetAutoRedraw (drawing, True);

    if (output == NULL) {
	BufferErrors (True);
	SolveProblem (element, node, numelts, numnodes, solution -> title);
	BufferErrors (False);
	NodeDialogDisplay (node_d, NodeDialogActive (node_d));
	ElementDialogDisplay (element_d, ElementDialogActive (element_d));
    } else
	WriteFeltFile (output,element,node,numelts,numnodes,solution -> title);

    element ++;
    XtFree ((char *) element);
    node ++;
    XtFree ((char *) node);
}

void SetupStresses (build_elt)
    Boolean	build_elt;
{
    unsigned	nd,i;
    int		depth;
    Arg		arglist [1];

	/*
	 * technically someone on a 1-but screen should never get here ...
	 * but just to be sure.
	 */

    XtSetArg (arglist[0], XtNdepth, &depth);
    XtGetValues (toplevel, arglist, 1);
    if (depth != 8) {
        error ("you must have an 8-bit deep screen to do stress plots");
        return;
    }

    if (build_elt) {
       numelts = TreeSize (problem.element_tree);

       if (numelts == 0)
   	  return;

       element = (Element *) XtMalloc (sizeof (Element) * numelts);
       element --;

       (void) TreeSetIterator (problem.element_tree, SetElementArray);

       count = 0;
       (void) TreeIterate (problem.element_tree);
    }

    nd = 0;
    for (i = 1; i <= numelts ; i++) {
         if (element[i] -> definition -> shape != Planar) {
            error ("cannot plot stresses for non-planar elements");
            return;
         }
         if (element[i] -> stress == NULL) {
            error ("could not get stresses for all elements");
            return;
         }
         if (4 > element[i] -> definition -> numstresses) {
            error ("invalid stress component for element %d",i);
            return;
         }
         nd += element[i] -> ninteg;
    }

    PlotStressField (element, numelts, nd);

    element ++;
    XtFree ((char *) element);
}

void SetupStructure (build_elt) 
    Boolean	build_elt;
{
    unsigned	i,j;
    double	z_plane;
    Boolean	draw3d;

    if (build_elt) {
       numelts = TreeSize (problem.element_tree);

       if (numelts == 0)
   	  return;

       element = (Element *) XtMalloc (sizeof (Element) * numelts);
       element --;

       (void) TreeSetIterator (problem.element_tree, SetElementArray);

       count = 0;
       (void) TreeIterate (problem.element_tree);
    }

    draw3d = False;
    z_plane = element[1] -> node[1] -> z;

    for (i = 1; i <= numelts ; i++) {
       for (j = 1 ; j <= element[i] -> definition -> numnodes ; j++) {
       
          if (element[i] -> node[j] -> z != z_plane) {
             draw3d = True;
             break;
          }
       }
       if (draw3d)
          break;
    }
 
    if (draw3d)
       VisualizeStructure3D (element, numelts);
    else
       VisualizeStructure (element, numelts);

    element ++;
    XtFree ((char *) element);
}
