#include "MeshNGEOM.h"
#include <polyfile.h>
#include <map>
#include <iostream>

using namespace std;

MeshNGEOM::MeshNGEOM( const char* filename ) : Mesh()
{
    int ierr = 0;
    int mode = 1;
    int flen = strlen(filename);
    int numverts = 0;
    int numfaces = 0;
    int numinternalfaces = 0;
    int numboundaryfaces = 0;
    int numcells = 0;
    int maxvertid = 0;
    int maxfaceid = 0;
    int maxcellid = 0;
    MeshVertex vertex;
    MeshCell cell;
    MeshFace face;
    map<int, int> boundaries;

    this->filename = filename;

    for(int pass = 0; pass < 2; pass++)
    {
	polyfile_open((char *)filename, &flen, &mode, &ierr);
	if (ierr != 0)
	{
	    cerr << "Failed to open '" << filename << "'!" << endl;
	    exit(1);
	}
	       
	int type, tag, nitem, maxid;
	while(ierr == 0)
	{
	    polyfile_readheader(&type, &tag, &nitem, &maxid, &ierr);
	    
	    if (ierr != 0)
		break;
	    
	    switch(type)
	    {
	    case 1: { 
		// vertices
		if (pass == 0)
		{
		    numverts += nitem;
		    maxvertid = (maxvertid > maxid) ? maxvertid : maxid;
		}

		for(int i=0;i<nitem;i++)
		{
		    polyfile_readvertex(&vertex.id, &vertex.coord[0],
					&vertex.coord[1], &vertex.coord[2],
					&ierr);
		    if(ierr != 0) 
			break;
		    if (pass == 1)
		    {
			this->vertices[vertex.id] = vertex;
			this->packedVerts.push_back(vertex);
		    }
		}	       
		break;
	    }
	    case 2: {	
		// shells -- skip?
		int shellid;
		int nverts;
		int *verts;
		for(int i=0;i<nitem;i++)
		{
		    polyfile_readshell(&shellid, &nverts, &verts, &ierr);
		    if(ierr != 0) 
			break;
		}
		break;
	    }
	    case 3: {
		// boundary
		if (pass == 0)
		{
		    numfaces += nitem;
		    maxfaceid = (maxfaceid > maxid) ? maxfaceid : maxid;
		    numboundaryfaces += nitem;
		}
		int boundary;
		int prostarid;
		int rpatch;
		int *verts;
		
		for(int i=0;i<nitem;i++)
		{
		    polyfile_readboundary(&face.id, &face.cells[0],
					  &prostarid, &boundary,
					  &rpatch, &face.nVerts, &verts, &ierr);
		    if (ierr != 0) 
			break;
		    
		    if (pass == 1)
		    {
			face.isBoundary = true;
			face.boundary = tag;
//			cout << "id: " << face.id << "    cell: "
//			     << face.cells[0] << "   prostarID: "
//			     << prostarid << "   boundary: "
//			     << face.boundary << "   patch: "
//			     << rpatch << "   tag: " << tag << endl;
			for(int j=0; j< face.nVerts; j++)
			    face.verts[j] = verts[j];
			this->boundaryFaces.push_back(face);
			boundaries[tag] = 1;
		    }		    
		}
		break;
	    }
	    case 4: {
		// face
		if (pass == 0)
		{
		    numfaces += nitem;
		    maxfaceid = (maxfaceid > maxid) ? maxfaceid : maxid;
		    numinternalfaces += nitem;
		}
		
		int prostarid1,prostarid2;
		int *verts;
		
		for(int i=0;i<nitem;i++)
		{
		    polyfile_readfaces(&face.id, &face.cells[0],
				       &prostarid1, &face.cells[1],
				       &prostarid2, &face.nVerts, &verts,
				       &ierr);
		    if (ierr != 0) 
			break;

		    if (pass == 1)
		    {
			for(int j=0; j < face.nVerts; j++)
			    face.verts[j] = verts[j];
			this->internalFaces.push_back(face);
		    }
		}
		break;
	    }
	    case 5: {
		// cells
		if (pass == 0)
		{
		    numcells += nitem;
		    maxcellid = (maxcellid > maxid) ? maxcellid : maxid;
		}
		
//		if (pass == 1)
//		    cout << "Cells:  (" << nitem << " cells, maxID: " << maxid
//			 << ")" << endl;
		for(int i=0;i<nitem;i++)
		{
		    polyfile_readcell(&cell.id,&ierr);
		    cell.type = tag;  // Is cell.id the cell number or the
				      // cell type?
		    if (ierr != 0) 
			break;
		    if (pass == 1)
		    {
			this->cells[cell.id] = cell;
			this->cellUsed[cell.id] = true;
//			cout << cell.id << "    ";
		    }
		}
//		if (pass == 1)
//		    cout << endl;
		break;
	    }
	    case 6: {
		// vertex map
		int vertexid;
		char meaning[512];
		int mlen;
		for(int i=0; i<nitem; i++)
		{
		    polyfile_readvmap(&vertexid, meaning, &mlen, &ierr);
		    if (ierr != 0) 
			break;
		}
		break;
	    }
	    case 7: {
		// shell map
		int shellid;
		char meaning[512];
		int mlen;
		for(int i=0; i<nitem; i++)
		{
		    polyfile_readsmap(&shellid, meaning, &mlen, &ierr);
		    if (ierr != 0) 
			break;
		}
		break;
	    }
	    case 8: {
		// boundary map
		int boundaryid;
		char meaning[512];
		int mlen;
		for(int i=0; i<nitem; i++)
		{
		    polyfile_readbmap(&boundaryid, meaning, &mlen, &ierr);
		    if (ierr != 0) 
			break;
		}
		break;
	    }
	    case 9: {
		// face map
		int faceid;
		char meaning[512];
		int mlen;
		for(int i=0; i<nitem; i++)
		{
		    polyfile_readfmap(&faceid, meaning, &mlen, &ierr);
		    if (ierr != 0) 
			break;
		}
		break;
	    }
	    case 10: {
		// cell map
		int cellid;
		char meaning[512];
		int mlen;
		for(int i=0; i<nitem; i++)
		{
		    polyfile_readcmap(&cellid, meaning, &mlen, &ierr);
		    if (ierr != 0) 
			break;
		}
		break;
	    }
	    case 11: {
		// double precision vertices
		if (pass == 0)
		{
		    numverts += nitem;
		    maxvertid = (maxvertid > maxid) ? maxvertid : maxid;
		}
		
		double xyz[3];
		for(int i=0;i<nitem;i++)
		{
		    polyfile_readdvertex(&vertex.id,&xyz[0],&xyz[1],
					 &xyz[2],&ierr);
		    if(ierr != 0) 
			break;
		    if (pass == 1)
		    {
			for(int j=0; j<3; j++)
			    vertex.coord[j] = (float)xyz[j];
			this->vertices[vertex.id] = vertex;
			this->packedVerts.push_back(vertex);
		    }
		}
		break;
	    }
	    case 12: {
		// interface
		int faceid1, bound1, patch1;
		int faceid2, bound2, patch2;
		for(int i=0;i<nitem;i++)
		{
		    polyfile_readinterface(&faceid1, &bound1, &patch1,
					   &faceid2, &bound2, &patch2,
					   &ierr);
		    if (ierr != 0) 
			break;
		}
		break;
	    }
	    case 13:
	    {
		float scale;
		int maxUserVertId;
		polyfile_readscalar(&scale, &maxUserVertId, &ierr);
		if (ierr != 0) 
		    break;
		break;
	    }
	    }
	}	    

	int ierr2;
	polyfile_close(&ierr2);
	
	if (ierr != 9)
	{
	    cerr << "ierr != 9 while after reading NGEOM format." << endl;
	    exit(1);
	}
	this->err = kCCMIONoErr;

	if (pass == 0)
	{
	    this->vertices.resize(maxvertid+1);
	    this->nVertices = numverts;
	    this->cells.resize(maxcellid+1);
	    this->cellUsed.resize(maxcellid+1, false);
	    this->nCells = numcells;
	    this->internalFaces.reserve(numinternalfaces);
	    this->boundaryFaces.reserve(numboundaryfaces);
	}
	
	if (pass == 1)
	    this->nBoundaries = boundaries.size();
    }
}
