#ifndef POST_NPOST_CPP
#define POST_NPOST_CPP

#include <ADF.h>
#include "PostNPOST.h"
#include <iostream>

using namespace std;

// global scope
static bool check_error( int error ) { return(error != -1); };

// Mostly stolen from the meshserver's NPOST reader
PostNPOST::PostNPOST( const char *filename )
{
    int i, j, k, error;
    PostField *field;

    // open file
    ADF_Database_Open(filename, "READ_ONLY", "", &this->rootID, &error);
    if (check_error(error)) throw(IOException("Can't open NPOST file"));

    // extract number of variables and their names
    int num_variables;
    ADF_Number_of_Children(this->rootID, &num_variables, &error);
    if (check_error(error)) throw(IOException("Corrupt NPOST file"));

    char *variable_names = new char[num_variables * 33];
    int num_ret;
    ADF_Children_Names(this->rootID, 1, num_variables, 32, &num_ret, 
		       variable_names, &error);
    if (check_error(error)) throw(IOException("Corrupt NPOST file"));
    
    if (!ReadCellmap())
	throw(IOException("Can't read cellmap"));
    
    for(i=0; i<num_variables; i++)
    {
	char *variableName = variable_names + 33*i;
	if (strcmp(variableName, "cellmap") == 0)
	    continue;
	
	// get the node id for the variable
	double variableID;
	ADF_Get_Node_ID(rootID, variableName, &variableID, &error);
	if (check_error(error)) throw(IOException("Corrupt NPOST file"));
	
	// get the type
	double type_id;
	char type[33];
	ADF_Get_Node_ID(variableID, "type", &type_id, &error);
	if (check_error(error)) throw(IOException("Corrupt NPOST file"));


	ADF_Get_Label(type_id, type, &error);
	if (check_error(error)) throw(IOException("Corrupt NPOST file"));
	
	if (strcmp(type,"vector") == 0 || strcmp(type,"VECTOR") == 0)
	    field = ReadVectorData(variableID, variableName);
	else if (strcmp(type,"scalar") == 0 || strcmp(type,"SCALAR") == 0)
	    field = ReadScalarData(variableID, variableName);
	else
	{
	    cout << "Ignoring tensor data '" << variableName << "' from '"
		 << filename << "'." << endl;
	    continue;
	}
	if (!field)
	    throw(IOException("Corrupt NPOST file"));
	    
	// read region information
	double regions_id;
	ADF_Get_Node_ID(variableID, "regions", &regions_id, &error);
	if (check_error(error)) throw(IOException("Corrupt NPOST file"));
	

	// extract number of variables and their names
	int num_regions;
	ADF_Number_of_Children(regions_id, &num_regions, &error);
	if (check_error(error)) throw(IOException("Corrupt NPOST file"));
	
	vector<char> region_names(num_regions * 5);
	if (num_regions > 0)
	{
	    ADF_Children_Names(regions_id, 1, num_regions, 4, 
			       &num_ret, &region_names[0], &error);
	    if (check_error(error)) throw(IOException("Corrupt NPOST file"));
	}
	
	for(j = 0; j<num_regions; j++)
	{
	    char *region_name = &region_names[5*j];
	    BoundaryData boundData;
	    
	    // get the region node
	    double region_id;
	    ADF_Get_Node_ID(regions_id, region_name, &region_id, &error);
	    if (check_error(error)) throw(IOException("Corrupt NPOST file"));
	    
	    // get the boundary data for the region
	    boundData.id = atoi(region_name);
	    
	    // read flags
	    int num_flags;
	    ADF_Number_of_Children(region_id, &num_flags, &error);
	    if (check_error(error)) throw(IOException("Corrupt NPOST file"));
		
	    vector<char> flag_names(num_flags * 33);
	    if (num_flags > 0)
	    {
		ADF_Children_Names(region_id, 1, num_flags, 32, 
				   &num_ret, &flag_names[0], &error);
		if (check_error(error)) throw(IOException("Corrupt NPOST file"));
	    }
	    for(k=0; k<num_flags; k++)
	    {
		char *flag_name = &flag_names[33*k];
		
		// get the flag node
		double flag_id;
		ADF_Get_Node_ID(region_id, flag_name, &flag_id, &error);
		if (check_error(error)) throw(IOException("Corrupt NPOST file"));
		
		if (strcmp(flag_name, "_priority") == 0)
		{
		    ADF_Read_All_Data(flag_id, (char *)&boundData.priority,
				      &error);
		    if (check_error(error))
			throw(IOException("Bad read of boundary priority"));
		}
		else if (strcmp(flag_name, "_value") == 0)
		{
		    // read and set the data value
		    ADF_Read_All_Data(flag_id, (char *)&boundData.value,
				      &error);
		    if (check_error(error))
			throw(IOException("Bad read of boundary value"));
		}
		else
		{
/*		    char flag[32];
		    ADF_Get_Label(flag_id, flag, &error);
		    if (check_error(error))
		    return error;
			
		    sbd.setFlag(flag_name, flag); */
		}
	    }
	    field->boundaryData[boundData.id] = boundData;
	}
	this->fields[variableName] = field;
    }

    delete[] variable_names;
    
    ADF_Database_Close(rootID, &error);
    if (check_error(error)) throw(IOException("Failed to close NPOST file!"));
}

bool PostNPOST::ReadCellmap()
{
    int nCells, error;
    double cellmapID;

    ADF_Get_Node_ID(rootID, "cellmap", &cellmapID, &error);	
    if (check_error(error)) return(false);
    
    ADF_Get_Dimension_Values(cellmapID, &nCells, &error);
    if (check_error(error)) return(false);
    
    this->cellmap.resize(nCells);
    ADF_Read_All_Data(cellmapID, (char *)&this->cellmap[0], &error);

    return(!check_error(error));
}

PostVectorField* PostNPOST::ReadVectorData( double variableID,
					    char *variableName )
{
    int error;
    double celldataID;
    PostVectorField *field = new PostVectorField(variableName);
	
    // get cell data node
    ADF_Get_Node_ID(variableID, "celldata", &celldataID, &error);
    if (check_error(error)) return(NULL);
	
    // read cell data (saved in C order in the file)
    field->data.resize(3 * (this->cellmap.size() + 1));
    ADF_Read_All_Data(celldataID, (char *)&field->data[3], &error);
    if (check_error(error)) return(NULL);

    MakeFieldCellmap(*field);

    return(field);
}

PostScalarField* PostNPOST::ReadScalarData( double variableID,
					    char *variableName )
{
    int error;
    double celldataID;
    PostScalarField *field = new PostScalarField(variableName);

    // get cell data node
    ADF_Get_Node_ID(variableID, "celldata", &celldataID, &error);
    if (check_error(error)) return(NULL);
    
    // read cell data
    field->data.resize(this->cellmap.size() + 1);
    ADF_Read_All_Data(celldataID, (char *)&field->data[1], &error);
    if (check_error(error)) return(NULL);

    MakeFieldCellmap(*field);

    return(field);
}

void PostNPOST::MakeFieldCellmap( PostField& field )
{
    CellmapArray::iterator cm;

    // this->cellmap is the NPOST's cellmap, which is packed.  field->cellmap
    // is unpacked.  We don't know what the max cell ID is, but it is at least
    // the size of this->cellmap.  If it turns out to be larger, we will
    // reallocate it later on.
    field.cellmap.resize(this->cellmap.size() + 1, false);
    for (cm = this->cellmap.begin();  cm != this->cellmap.end();  ++cm)
    {
	if ((unsigned int)(*cm) >= field.cellmap.size())
	    field.cellmap.resize(*cm + 1, false);
	field.cellmap[*cm] = true;
    }
}

#endif // POST_NPOST_CPP
