#include "ccmio.h"
#include "ccmioutility.h"
#include <vector>
#include <iostream>

// If you are unfamiliar with the Standard Template Library (STL), please read.
//   vector:	A dynamic array.  vector<int> is an array of ints.  Suppose
//		we have vector<int> iarr;
//		    iarr.push_back( ... ); adds an item to the end of the
//					   array, resizing if necessary.
//		    iarr.size();	   Returns the number of items.
//		    iarr[10];		   The 11th item in the array.
//					   No bounds checking is performed.
//		    iarr.resize(10);	   Makes the array 10 items long.

using namespace std;

struct Face {
    int nVerts;
    int vertices[4];
    int cells[2];
};
static const int nCells = 8;
int cells[] = { 1, 1, 1, 2, 1, 1, 1, 1 };
static const int nInternalFaces = 12;
Face internalFaces[] = { { 4, {2, 5, 14, 11}, {2, 1} },
			 { 4, {4, 5, 14, 13}, {1, 3} },
			 { 4, {5, 6, 15, 14}, {2, 4} },
			 { 4, {5, 8, 17, 14}, {4, 3} },
			 { 4, {10, 11, 14, 13}, {5, 1} },
			 { 4, {11, 12, 15, 14}, {6, 2} },
			 { 4, {11, 14, 23, 20}, {6, 5} },
			 { 4, {13, 14, 17, 16}, {7, 3} },
			 { 4, {13, 14, 23, 22}, {5, 7} },
			 { 4, {14, 15, 18, 17}, {8, 4} },
			 { 4, {14, 15, 24, 23}, {6, 8} },
			 { 4, {14, 17, 26, 23}, {8, 7} } };

static const int nBoundaryFaces = 24;
Face boundaryFaces[] = { { 4, {1, 2, 5, 4}, {1, 0} },
			 { 4, {1, 10, 11, 2}, {1, 0} },
			 { 4, {1, 4, 13, 10}, {1, 0} },
			 { 4, {2, 3, 6, 5}, {2, 0} },
			 { 4, {2, 11, 12, 3}, {2, 0} },
			 { 4, {3, 12, 15, 6}, {2, 0} },
			 { 4, {4, 5, 8, 7}, {3, 0} },
			 { 4, {4, 7, 16, 13}, {3, 0} },
			 { 4, {5, 6, 9, 8}, {4, 0} },
			 { 4, {6, 15, 18, 9}, {4, 0} },
			 { 4, {7, 8, 17, 16}, {3, 0} },
			 { 4, {8, 9, 18, 17}, {4, 0} },
			 { 4, {10, 19, 20, 11}, {5, 0} },
			 { 4, {10, 13, 22, 19}, {5, 0} },
			 { 4, {11, 20, 21, 12}, {6, 0} },
			 { 4, {12, 21, 24, 15}, {6, 0} },
			 { 4, {13, 16, 25, 22}, {7, 0} },
			 { 4, {15, 24, 27, 18}, {8, 0} },
			 { 4, {16, 17, 26, 25}, {7, 0} },
			 { 4, {17, 18, 27, 26}, {8, 0} },
			 { 4, {19, 22, 23, 20}, {5, 0} },
			 { 4, {20, 23, 24, 21}, {6, 0} },
			 { 4, {22, 25, 26, 23}, {7, 0} },
			 { 4, {23, 26, 27, 24}, {8, 0} } };
int nVertices = 27;
float vertices[] = { 0, 0, 0, 0, 0.5, 0, 0, 1, 0, 0.5, 0, 0, 0.5, 0.5, 0, 0.5, 1, 0, 1, 0, 0, 1, 0.5, 0, 1, 1, 0, 0, 0, 0.5, 0, 0.5, 0.5, 0, 1, 0.5, 0.5, 0, 0.5, 0.5, 0.5, 0.5, 0.5, 1, 0.5, 1, 0, 0.5, 1, 0.5, 0.5, 1, 1, 0.5, 0, 0, 1, 0, 0.5, 1, 0, 1, 1, 0.5, 0, 1, 0.5, 0.5, 1, 0.5, 1, 1, 1, 0, 1, 1, 0.5, 1, 1, 1, 1 };
int mapData[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36 };

float postData[] = { 100.0, 100.0, 100.0, 200.0, 100.0, 100.0, 100.0, 100.0 };

static int nDroplets = 8;
float positions[] = { 0, 0, 0,  1, 0, 0,  0, 1, 0,  0, 0, 1,  0, 1, 1,  1, 0, 1,
		      1, 1, 0,  1, 1, 1 };
int types[] = { 1, 1, 2, 1, 2, 1, 2, 2 };

static const char kStateName[] = "default";
static const char kDataName[] = "Temperature";
static const char kShortDataName[] = "Temp";
static const char kUnitsName[] = "Units";
static const int kInc = 10;

int main( int argc, char *argv[] )
{
    int i = 0;
    CCMIOID stateID, processorID, rootID, verticesID, topologyID, solutionID;
    CCMIOID mapID;
    CCMIOID id, cellMapID;
    CCMIOError error = kCCMIONoErr, *err = &error;

    if (argc < 2)
    {
	cout << "Usage:  " << argv[0] << " outputFile" << endl;
	return(0);
    }

    CCMIOOpenFile(err, argv[1], kCCMIOWrite, &rootID);

    // Create a new state (or re-use an existing one).  If you want to
    // re-use the problem description, check if there is a state first,
    // because CCMIONewState() may destroy it:
    //     if (CCMIOGetState(NULL, rootID, kStateName, &stateID) != kCCMIONoErr)
    //         CCMIONewState(err, rootID, kStateName, NULL, &stateID);
    CCMIONewState(err, rootID, kStateName, NULL, NULL, &stateID);

    // Attempt to find the first processor (i has already been initialized to 0)
    // If this was not a brand-new state, then we should reuse this processor
    // (if we just create a processor, we will have more than one processor,
    // and one of them will no longer be meaningful).  If we were writing
    // a multiprocessor mesh we should loop through all the processors, delete
    // them, and then create however many processors we need.
    // Note that we pass NULL for the error because it is not an error for
    // us if there is not a processor;  it just means that we need to create
    // one.  If we pass in 'err' then all the rest of the functions will
    // needlessly fail because we encountered an expected error.
    if (CCMIONextEntity(NULL, stateID, kCCMIOProcessor, &i, &processorID) != kCCMIONoErr)
	CCMIONewEntity(err, stateID, kCCMIOProcessor, NULL, &processorID);

    // Get rid of any data that may be in this processor (if the state was
    // not new).
    CCMIOClearProcessor(err, stateID, processorID, TRUE, TRUE, TRUE, TRUE,
			TRUE);

    // Write the vertices (the vertices are in Fortran order, so we need to
    // make sure we specify that).  First we will need to create the mapping
    // from the index in the data array to the vertex ID.  Then we can write
    // the actual data.
    CCMIONewEntity(err, rootID, kCCMIOMap, "Vertex map", &mapID);
    CCMIOWriteMap(err, mapID, nVertices, mapData[nVertices], mapData,
		  kCCMIOStart, kCCMIOEnd);
    CCMIONewEntity(err, rootID, kCCMIOVertices, "Vertices", &verticesID);
    // Write the vertices piecemeal by way of illustration
    for (i = 0;  i < nVertices;  i += kInc)
	CCMIOWriteVerticesf(err, verticesID, 3, 1.0, mapID, vertices + 3 * i,
			    i, i + kInc);

    // Write the cells
    CCMIONewEntity(err, rootID, kCCMIOMap, "Cell map", &cellMapID);
    CCMIOWriteMap(err, cellMapID, nCells, mapData[nVertices], mapData,
		  kCCMIOStart, kCCMIOEnd);
    CCMIONewEntity(err, rootID, kCCMIOTopology, "Topology", &topologyID);
    CCMIONewEntity(err, topologyID, kCCMIOCells, "Cells", &id);
    for (i = 0;  i < nCells;  i += 3)
	CCMIOWriteCells(err, id, cellMapID, cells + i, i, i + 3);

    // Write the faces.  There are two kinds of faces, internal faces and
    // boundary faces.  The procedure is the same for each except that
    // internal faces' cell array is a two dimensional array because internal
    // faces have a cell on both sides.  Boundary faces, obviously, only
    // have one cell.
    vector<int> v, c;

    CCMIONewEntity(err, rootID, kCCMIOMap, NULL, &mapID);
    CCMIOWriteMap(err, mapID, nInternalFaces,
		mapData[nBoundaryFaces + nInternalFaces],
		&mapData[nBoundaryFaces], kCCMIOStart, kCCMIOEnd);
    CCMIONewEntity(err, topologyID, kCCMIOInternalFaces, "Internal faces", &id);
    for (int f = 0;  f < nInternalFaces;  ++f)
    {
	v.push_back(internalFaces[f].nVerts);
	for (int i = 0;  i < internalFaces[f].nVerts;  ++i)
	    v.push_back(internalFaces[f].vertices[i]);
	c.push_back(internalFaces[f].cells[0]);
	c.push_back(internalFaces[f].cells[1]);  // Our other cell
    }
    CCMIOWriteFaces(err, id, kCCMIOInternalFaces, mapID, v.size(), &v[0],
		    kCCMIOStart, kCCMIOEnd);
    CCMIOWriteFaceCells(err, id, kCCMIOInternalFaces, mapID, &c[0], kCCMIOStart,
			kCCMIOEnd);
	
    v.clear();
    c.clear();
    CCMIONewEntity(err, rootID, kCCMIOMap, NULL, &mapID);
    CCMIOWriteMap(err, mapID, nBoundaryFaces, mapData[nBoundaryFaces], mapData,
		  kCCMIOStart, kCCMIOEnd);
    CCMIONewIndexedEntity(err, topologyID, kCCMIOBoundaryFaces, 0,
			  "Boundary faces", &id);
    for (int f = 0;  f < nBoundaryFaces;  ++f)
    {
	v.push_back(boundaryFaces[f].nVerts);
	for (int i = 0;  i < boundaryFaces[f].nVerts;  ++i)
	    v.push_back(boundaryFaces[f].vertices[i]);
	c.push_back(boundaryFaces[f].cells[0]);
	// Boundary faces only have one cell, so cells[1] is unused.
    }
    CCMIOWriteFaces(err, id, kCCMIOBoundaryFaces, mapID, v.size(), &v[0],
		    kCCMIOStart, kCCMIOEnd);
    CCMIOWriteFaceCells(err, id, kCCMIOBoundaryFaces, mapID, &c[0], kCCMIOStart,
			kCCMIOEnd);

    // Write out a dummy problem description.  If we happen to know that
    // there already is a problem description previously recorded that
    // is valid we could skip this step.
    CCMIOID problem, constants;

    CCMIONewEntity(err, rootID, kCCMIOProblemDescription, "Dummy description",
		 &problem);
    CCMIONewIndexedEntity(err, problem, kCCMIOCellType, 1, "Dummy celltypes", &id);
    CCMIOWriteOptstr(err, id, "MaterialType", "solid");
    CCMIONewIndexedEntity(err, problem, kCCMIOCellType, 2, "Dummy celltypes", &id);
    CCMIOWriteOptstr(err, id, "MaterialType", "solid");
    
    CCMIONewEntity(err, problem, kCCMIOModelConstants, "Constant values",
		   &constants);
    CCMIOWriteOptf(err, constants, "Gravity", 9.82);
    CCMIOWriteOptf(err, constants, "B.P. of water", 373);

    // We have problem description recorded but our state does not know
    // about it.  So tell the state that it has a problem description.
    CCMIOWriteState(err, stateID, problem, "Example state");

    // Write out some simple solution data
    CCMIOID phase, field;
    CCMIONewEntity(err, rootID, kCCMIOFieldSet, "Dummy post data", &solutionID);
    CCMIONewIndexedEntity(err, solutionID, kCCMIOFieldPhase, 0, NULL, &phase);
    CCMIONewField(err, phase, kDataName, kShortDataName, kCCMIOScalar, &field);
    CCMIOWriteOptstr(err, field, kUnitsName, "°F");
    CCMIONewEntity(err, field, kCCMIOFieldData, NULL, &id);
    CCMIOWriteFieldDataf(err, id, cellMapID, kCCMIOCell, postData,
			 kCCMIOStart, kCCMIOEnd);

    // Write out a piece of vector data (which also illustrates constant data)
    CCMIOID vectorField;
    CCMIONewField(err, phase, "Velocity", "VELO", kCCMIOVector, &vectorField);

    CCMIONewField(err, phase, "Velocity (U)", "U", kCCMIOScalar, &field);
    CCMIOWriteOptstr(err, field, kUnitsName, "m/s");
    CCMIONewEntity(err, field, kCCMIOFieldData, NULL, &id);
    CCMIOWriteConstantFieldDataf(err, id, cellMapID, kCCMIOCell, 0.0);
    CCMIOWriteMultiDimensionalFieldData(err, vectorField, kCCMIOVectorX, field);

    CCMIONewField(err, phase, "Velocity (V)", "V", kCCMIOScalar, &field);
    CCMIOWriteOptstr(err, field, kUnitsName, "m/s");
    CCMIONewEntity(err, field, kCCMIOFieldData, NULL, &id);
    CCMIOWriteConstantFieldDataf(err, id, cellMapID, kCCMIOCell, 1.0);
    CCMIOWriteMultiDimensionalFieldData(err, vectorField, kCCMIOVectorY, field);

    CCMIONewField(err, phase, "Velocity (W)", "W", kCCMIOScalar, &field);
    CCMIOWriteOptstr(err, field, kUnitsName, "m/s");
    CCMIONewEntity(err, field, kCCMIOFieldData, NULL, &id);
    CCMIOWriteConstantFieldDataf(err, id, cellMapID, kCCMIOCell, 0.5);
    CCMIOWriteMultiDimensionalFieldData(err, vectorField, kCCMIOVectorZ, field);

    // Add in some sample restart info
    int iteration = 0;
    float time = 0.0, startAngle = 0.0;
    CCMIOID restart, restartData;
    CCMIONewEntity(err, solutionID, kCCMIORestart, NULL, &restart);
    CCMIOWriteRestartInfo(err, restart, "writeexample", iteration, time, NULL,
			  startAngle);
    // The solver part can have anything the solver wants
    CCMIONewEntity(err, restart, kCCMIORestartData, NULL, &restartData);
    CCMIOWriteOptf(err, restartData, "Convergence", 100);

    // Now we have the mesh (vertices and topology) and the post data written.
    // Since we now have their IDs, we can write out the processor information.
    CCMIOWriteProcessor(err, processorID, NULL, &verticesID, NULL, &topologyID,
		      NULL, NULL, NULL, &solutionID);

    // Write out some simple Lagrangian data
    CCMIONewEntity(err, rootID, kCCMIOMap, "Positions map", &mapID);
    CCMIOWriteMap(err, mapID, nDroplets, mapData[nDroplets], mapData,
		  kCCMIOStart, kCCMIOEnd);
    CCMIONewEntity(err, rootID, kCCMIOVertices, "Positions", &verticesID);
    CCMIOWriteVerticesf(err, verticesID, 3, 1.0, mapID, positions,
			kCCMIOStart, kCCMIOEnd);

    CCMIONewEntity(err, rootID, kCCMIOFieldSet, "Dummy Lagrangian post data",
		   &solutionID);
    CCMIONewIndexedEntity(err, solutionID, kCCMIOFieldPhase, 0, NULL, &phase);
    CCMIONewField(err, phase, "Droplet type", "droplet", kCCMIOScalar, &field);
    CCMIONewEntity(err, field, kCCMIOFieldData, NULL, &id);
    CCMIOWriteOptstr(err, field, kUnitsName, "none");
    CCMIOWriteFieldDatai(err, id, cellMapID, kCCMIOCell, types,
			 kCCMIOStart, kCCMIOEnd);

    CCMIOID lagrangian;
    CCMIONewEntity(err, processorID, kCCMIOLagrangianData, NULL, &lagrangian);
    CCMIOWriteLagrangianData(err, lagrangian, NULL, &verticesID,
			     NULL, &solutionID);

    CCMIOCloseFile(err, rootID);

    if (*err != kCCMIONoErr)
    {
	cout << "Error " << *err << " writing mesh" << endl;
	return(0);
    }

    // The CCMIO library uses ADF to store the actual data.  Unfortunately,
    // ADF leaks disk space;  deleting a node does not recover all the disk
    // space.  Now that everything is successfully written it might be useful
    // to call CCMIOCompress() here to ensure that the file is as small as
    // possible.  Please see the Core API documentation for caveats on its
    // usage.
    if (CCMIOCompress(NULL, argv[1]) != kCCMIONoErr)
    {
	cout << "Error compressing file.  Check that you have "
	     << "adequate disk space " << endl << "and that you have write "
	     << "permission to the current directory." << endl;
	return(0);
    }

    return(1);
}
