// Copyright (c) Lawrence Livermore National Security, LLC and other VisIt
// Project developers.  See the top-level LICENSE file for dates and other
// details.  No copyright assignment is required to contribute to VisIt.

// ************************************************************************* //
//                            avtXmdvFileFormat.C                            //
// ************************************************************************* //

#include <avtXmdvFileFormat.h>

#include <string>
#include <visitstream.h>

#include <vtkCellType.h>
#include <vtkFloatArray.h>
#include <vtkRectilinearGrid.h>
#include <vtkUnstructuredGrid.h>

#include <avtDatabaseMetaData.h>

#include <Expression.h>

#include <DebugStream.h>
#include <InvalidFilesException.h>
#include <InvalidVariableException.h>


using     std::string;

#if defined(_WIN32)
// Define >> for strings since Windows does not seem to have it.
ifstream &operator >> (ifstream &s, string &str)
{
    char tmp[1000];
    s >> tmp;
    str = string(tmp);
    return s;
}
#endif

// ****************************************************************************
//  Method: avtXmdv constructor
//
//  Programmer: childs -- generated by xml2avt
//  Creation:   Tue Jul 19 17:12:44 PST 2005
//
// ****************************************************************************

avtXmdvFileFormat::avtXmdvFileFormat(const char *filename, 
                                     DBOptionsAttributes *)
    : avtSTSDFileFormat(filename)
{
    readInData = false;
}


// ****************************************************************************
//  Method: avtXmdvFileFormat::FreeUpResources
//
//  Purpose:
//      When VisIt is done focusing on a particular timestep, it asks that
//      timestep to free up any resources (memory, file descriptors) that
//      it has associated with it.  This method is the mechanism for doing
//      that.
//
//  Programmer: childs -- generated by xml2avt
//  Creation:   Tue Jul 19 17:12:44 PST 2005
//
// ****************************************************************************

void
avtXmdvFileFormat::FreeUpResources(void)
{
    varnames.clear();
    values.clear();
    readInData = false;
}


bool
avtXmdvFileFormat::ReadXmdvFile(bool readData)
{
    if (readInData)
        return true;

    int  i;

    ifstream ifile(filename);
    if (ifile.fail())
    {
        debug1 << "Cannot open Xmdv file" << endl;
        return false;
    }

    int nvars = 0;
    ifile >> nvars;
    if (nvars <= 0)
    {
        debug1 << "Cannot establish number of variables" << endl;
        return false;
    }
    varnames.resize(nvars);

    ifile >> ncells;
    if (ncells <= 0)
    {
        debug1 << "Cannot establish number of cells" << endl;
        return false;
    }
  
    int random_int;
    ifile >> random_int;
 
    for (i = 0 ; i < nvars ; i++)
    {
        ifile >> varnames[i];
    }

    for (i = 0 ; i < nvars ; i++)
    {
        float min, max;
        ifile >> min;
        ifile >> max;
        int cardinality;
        ifile >> cardinality;
    }

    if (!readData)
        return true;

    int nvals = nvars*ncells;
    values.resize(nvals);
    for (i = 0 ; i < nvals ; i++)
        ifile >> values[i];

    readInData = true;
    return true;
}



// ****************************************************************************
//  Method: avtXmdvFileFormat::PopulateDatabaseMetaData
//
//  Purpose:
//      This database meta-data object is like a table of contents for the
//      file.  By populating it, you are telling the rest of VisIt what
//      information it can request from you.
//
//  Programmer: childs -- generated by xml2avt
//  Creation:   Tue Jul 19 17:12:44 PST 2005
//
// ****************************************************************************

void
avtXmdvFileFormat::PopulateDatabaseMetaData(avtDatabaseMetaData *md)
{
    if (!ReadXmdvFile(false))
        EXCEPTION1(InvalidFilesException, filename);

    string meshname = "points";
    
    avtMeshType mt = AVT_POINT_MESH;
    int nblocks = 1;
    int block_origin = 0;
    int spatial_dimension = 3;
    int topological_dimension = 0;
    double *extents = NULL;

    AddMeshToMetaData(md, meshname, mt, extents, nblocks, block_origin,
                      spatial_dimension, topological_dimension);

    for (size_t i = 0 ; i < varnames.size() ; i++)
        AddScalarVarToMetaData(md, varnames[i], meshname, AVT_NODECENT, NULL);

    AddArrayVarToMetaData(md, "all_vars", varnames, meshname, AVT_NODECENT);
}


// ****************************************************************************
//  Method: avtXmdvFileFormat::GetMesh
//
//  Purpose:
//      Gets the mesh associated with this file.  The mesh is returned as a
//      derived type of vtkDataSet (ie vtkRectilinearGrid, vtkStructuredGrid,
//      vtkUnstructuredGrid, etc).
//
//  Arguments:
//      meshname    The name of the mesh of interest.  This can be ignored if
//                  there is only one mesh.
//
//  Programmer: childs -- generated by xml2avt
//  Creation:   Tue Jul 19 17:12:44 PST 2005
//
// ****************************************************************************

vtkDataSet *
avtXmdvFileFormat::GetMesh(const char *meshname)
{
    if (!readInData)
        if (!ReadXmdvFile(true))
            EXCEPTION1(InvalidFilesException, filename);

    vtkUnstructuredGrid *ugrid = vtkUnstructuredGrid::New();
    vtkPoints *pts = vtkPoints::New();
    pts->SetNumberOfPoints(ncells);
    size_t nvars = varnames.size();
    for (int i = 0 ; i < ncells ; i++)
    {
        float pt[3];
        pt[0] = (nvars > 0 ? values[nvars*i] : 0.);
        pt[1] = (nvars > 1 ? values[nvars*i+1] : 0.);
        pt[2] = (nvars > 2 ? values[nvars*i+2] : 0.);
        pts->SetPoint(i, pt);
    }
    ugrid->Allocate(2*ncells);
    vtkIdType onevertex[1];
    for (int i = 0 ; i < ncells ; i++)
    {
        onevertex[0] = i;
        ugrid->InsertNextCell(VTK_VERTEX, 1, onevertex);
    }

    ugrid->SetPoints(pts);
    pts->Delete();

    return ugrid;
}


// ****************************************************************************
//  Method: avtXmdvFileFormat::GetVar
//
//  Purpose:
//      Gets a scalar variable associated with this file.  Although VTK has
//      support for many different types, the best bet is vtkFloatArray, since
//      that is supported everywhere through VisIt.
//
//  Arguments:
//      varname    The name of the variable requested.
//
//  Programmer: childs -- generated by xml2avt
//  Creation:   Tue Jul 19 17:12:44 PST 2005
//
// ****************************************************************************

vtkDataArray *
avtXmdvFileFormat::GetVar(const char *varname)
{
    if (!readInData)
        if (!ReadXmdvFile(true))
            EXCEPTION1(InvalidFilesException, filename);

    int varIdx = -1;
    int nvars = (int)varnames.size();
    for (int i = 0 ; i < nvars ; i++)
        if (varnames[i] == varname)
            varIdx = i;

    if (varIdx < 0)
        EXCEPTION1(InvalidVariableException, varname);

    vtkFloatArray *rv = vtkFloatArray::New();
    rv->SetNumberOfTuples(ncells);
    for (int i = 0 ; i < ncells ; i++)
    {
        rv->SetTuple1(i, values[i*nvars + varIdx]);
    }

    return rv;
}


// ****************************************************************************
//  Method: avtXmdvFileFormat::GetVectorVar
//
//  Purpose:
//      Gets a vector variable associated with this file.  Although VTK has
//      support for many different types, the best bet is vtkFloatArray, since
//      that is supported everywhere through VisIt.
//
//  Arguments:
//      varname    The name of the variable requested.
//
//  Programmer: childs -- generated by xml2avt
//  Creation:   Tue Jul 19 17:12:44 PST 2005
//
// ****************************************************************************

vtkDataArray *
avtXmdvFileFormat::GetVectorVar(const char *varname)
{
    if (strcmp(varname, "all_vars") != 0)
        EXCEPTION1(InvalidVariableException, varname);

    if (!readInData)
        if (!ReadXmdvFile(true))
            EXCEPTION1(InvalidFilesException, filename);

    int nvars = (int)varnames.size();
    vtkFloatArray *rv = vtkFloatArray::New();
    rv->SetNumberOfComponents(nvars);
    rv->SetNumberOfTuples(ncells);
    float *ptr = rv->GetPointer(0);
    memcpy(ptr, &(values[0]), ncells*nvars*sizeof(float));

    return rv;
}
