/*
Main program commands

Orion Sky Lawlor, olawlor@acm.org, 11/28/2001
*/
#ifndef __OSL_CCSVIEW_MAIN_H
#define __OSL_CCSVIEW_MAIN_H

#include "netfem_data.h"
#include "vtkSystemIncludes.h"
#include "vtkPolyData.h"
#include "vtkPolygon.h"
#include "vtkPoints.h"
#include "vtkCell.h"
#include "vtkPolyDataNormals.h"
#include "vtkStripper.h"
#include "vtkPolyDataMapper.h"

//Run this TCL command
void tcl_eval(const char *string);

//Abort with this error code
extern "C" void bad(const char *why);

class chunk; //Forward declaration

//A range (min, max) of floating-point values
class floatRange {
	float r[2];
	static float BIG;
public:
	floatRange() {reset();}
	void reset(void) {r[0]=BIG; r[1]=-BIG;}
	void add(const float *n) {
		if (r[0]>n[0]) r[0]=n[0];
		if (r[1]<n[1]) r[1]=n[1];
	}
	void apply(vtkPolyDataMapper *map) const;
};

//------------------------ Chunk: ------------------------
class outputFilter;

//Represents a portion of the FEM mesh
class chunk {
	int number; //Chunk number
	NetFEM_update *u; //Currently viewed data about the mesh
	
	//Last (and persistent) stages of C++ viz. pipeline
	vtkPolyDataNormals *norm;
	vtkPolyDataMapper *map;
	
	//Cached versions of the mesh data:
	
public:
	chunk(void);
	~chunk(void);
	void setNumber(int n) {number=n;}
	int getNumber(void) const {return number;}
	void setUpdate(NetFEM_update *uNew);
	const NetFEM_update *getUpdate(void) const {return u;}
	
	void addGeometry(outputFilter *o);
	void updateRange(outputFilter *o);
	
	vtkPolyDataMapper *getMap(void) {return map;}
};

//Global list of active chunks
class chunks {
	static chunk *chunkList;
public:
	static int n; //Number of chunks
	static void init(int nChunks);//Called just after connection established
	static chunk &get(int chunkNo) {return chunkList[chunkNo];}
	
};


//------------------ Geometry Filters: -----------------
//Shows how to output the surface geometry
class geomFilter {
	static geomFilter *cur;
public:
	virtual vtkPolyData *addGeometry(chunk &c,int &nCopies)=0;
	virtual int getType(void) =0;
	
	//Change/access the current filter
	static void set(geomFilter *nu) {cur=nu;}
	static geomFilter *get(void) {return cur;}
};

//Abstract class: Write out the nodes (without any connectivity yet)
class nodeGeomFilter : public geomFilter {
protected:
	virtual void addConnectivity(vtkPolyData *poly,int nNodes,chunk &c)=0;
public:
	virtual vtkPolyData *addGeometry(chunk &c,int &nCopies);
	virtual int getType(void) {return -1;}
};

//Write out a cloud of point nodes
class cloudGeomFilter : public nodeGeomFilter {
protected:
	virtual void addConnectivity(vtkPolyData *poly,int nNodes,chunk &c);
};

//Write out a mesh of triangles, using the elements to connect the nodes
class triGeomFilter : public nodeGeomFilter {
	int elemType;
protected:
	virtual void addConnectivity(vtkPolyData *poly,int nNodes,chunk &c);
public:
	triGeomFilter(int eType) :elemType(eType) {}
};
//Write out mesh by outlining elements
class outlineGeomFilter : public nodeGeomFilter {
	int elemType;
protected:
	virtual void addConnectivity(vtkPolyData *poly,int nNodes,chunk &c);
public:
	outlineGeomFilter(int eType) :elemType(eType) {}
};


//Write out a mesh of elements
class elemGeomFilter : public geomFilter {
	int elemType;
public:
	elemGeomFilter(int eType) :elemType(eType) {}
	virtual vtkPolyData *addGeometry(chunk &c,int &nCopies);
	virtual int getType(void) {return elemType;}
};

//-------------------- Displacement Filter: ---------------
class dispFilter {
	double scale; //Magnify displacements by this much
	static dispFilter *cur;
public:
	dispFilter() {scale=1.0;}
	virtual ~dispFilter();
	void setScale(double s) {scale=s;}
	double getScale(void) const {return scale;}
	
	virtual void setUpdate(const NetFEM_update *u) =0;
	virtual const double *getDisp(int nodeNo) =0;

	static dispFilter *get(void) {return cur;}
	static void set(dispFilter *f) {cur=f;}
};

//Default case: displacements all zero
class noDispFilter : public dispFilter {
	double zeros[3];
public:
	noDispFilter() {zeros[0]=zeros[1]=zeros[2]=0.0;}
	void setUpdate(const NetFEM_update *u) {}
	const double *getDisp(int nodeNo) {return zeros;}
};

//Displacements from a node field
class nodeDispFilter : public dispFilter {
	int fieldNo;
	int stride;
	const double *data;
public:
	nodeDispFilter(int fNo) :fieldNo(fNo) {}
	virtual void setUpdate(const NetFEM_update *u);
	const double *getDisp(int nodeNo) {return &data[stride*nodeNo];}
};

//-------------------- Field loc: ------------------
// Extract a single field from an update structure
class fieldLoc {
	virtual const NetFEM_doubleField &getField(const NetFEM_update *u) const =0;
	int valueNo;//entry within the field
public:
	fieldLoc(int v) :valueNo(v) {}
	virtual ~fieldLoc();
	int getStride(const NetFEM_update *u) const {return getField(u).getStride();}
	const double *getData(const NetFEM_update *u) const {return getField(u).getData(0)+valueNo;}
};

class nodeFieldLoc : public fieldLoc {
	int fieldNo;
	virtual const NetFEM_doubleField &getField(const NetFEM_update *u) const;
public:
	nodeFieldLoc(int f,int v) :fieldLoc(v), fieldNo(f) {}
};

class elemFieldLoc : public fieldLoc {
	int elemType,fieldNo;
	virtual const NetFEM_doubleField &getField(const NetFEM_update *u) const;
public:
	elemFieldLoc(int e,int f,int v) :fieldLoc(v), elemType(e), fieldNo(f) {}
};

//-------------------- Field filters: ------------------
// Convert a doubleField into a single color
class fieldFilter {
public:
	virtual ~fieldFilter();
	virtual void setScalars(vtkScalars *vColor,int n,int copies,chunk &c) =0;
};

//Color by the a single scalar
class scalarFieldFilter : public fieldFilter {
	fieldLoc *loc;
public:
	scalarFieldFilter(fieldLoc *loc_) :loc(loc_) {}
	virtual void setScalars(vtkScalars *vColor,int n,int copies,chunk &c);
};

//Color by the vector magnitude
class magFieldFilter : public fieldFilter {
	fieldLoc *loc;
public:
	magFieldFilter(fieldLoc *loc_) :loc(loc_) {}
	virtual void setScalars(vtkScalars *vColor,int n,int copies,chunk &c);
};

//Color by the chunk number
class patchworkFieldFilter : public fieldFilter {
public:
	virtual void setScalars(vtkScalars *vColor,int n,int copies,chunk &c);
};

//-------------------- Output filters: -------------------
//Converts some portion of the mesh to triangles.
//  This class is independent of the chunk (this way, the
//chunk can be replaced with the next timestep).
class outputFilter {
	static outputFilter *cur;

	floatRange r;
protected:
	void addToRange(const float *n) {r.add(n);}
public:
	virtual ~outputFilter();
	virtual void beginRendering(void);
	virtual vtkPolyData *addGeometry(chunk &c) =0;
	virtual const floatRange &updateRange(chunk &c);
	
	//Change/access the current output filter
	static void set(outputFilter *nu) {cur=nu;}
	static outputFilter *get(void) {return cur;}
};

//Writes out a surface colored by the nodes
class nodeOutputFilter : public outputFilter {
protected:
	fieldFilter *f;
public:
	nodeOutputFilter(fieldFilter *f_) :f(f_) {}
	virtual vtkPolyData *addGeometry(chunk &c);
};

//Writes out a surface colored by the elements
class elemOutputFilter : public outputFilter {
protected:
	int elemType;
	fieldFilter *f;
public:
	elemOutputFilter(fieldFilter *f_,int eType) :elemType(eType),f(f_) {}
	virtual vtkPolyData *addGeometry(chunk &c);
};

#endif /*def(thisHeader)*/
