// 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.

// ************************************************************************* //
//                        avtAUXFileFileFormat.C                             //
// ************************************************************************* //

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>


#include <avtAUXFileFileFormat.h>

#include <string>

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

#include <avtDatabaseMetaData.h>

#include <Expression.h>
#include <StringHelpers.h>
#include <avtDatabase.h>

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

#include <FileFunctions.h>

using     std::string;


// ****************************************************************************
//  Method: avtAUXFile constructor
//
//  Programmer: miller -- generated by xml2avt
//  Creation:   Tue Mar 15 08:29:20 PDT 2005
//
//  Modifications:
//    Jeremy Meredith, Thu Jan  7 12:30:10 EST 2010
//    Refactored out file reading.  Initialize the flag for it to false.
//
// ****************************************************************************

avtAUXFileFileFormat::avtAUXFileFileFormat(const char *filename)
    : avtSTSDFileFormat(filename)
{
    fileName = filename;
    fileBuf = 0;
    sizeX = -1;
    sizeY = -1;
    fluence = 0;
    fileRead = false;
}


// ****************************************************************************
//  Method: avtAUXFileFileFormat::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: miller -- generated by xml2avt
//  Creation:   Tue Mar 15 08:29:20 PDT 2005
//
// ****************************************************************************

void
avtAUXFileFileFormat::FreeUpResources(void)
{
    if (fileBuf)
    {
        delete [] fileBuf;
        fileBuf = 0;
    }
    if (fluence)
    {
        fluence->Delete();
        fluence = 0;
    }
}


// ****************************************************************************
//  Method: avtAUXFileFileFormat::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: miller -- generated by xml2avt
//  Creation:   Tue Mar 15 08:29:20 PDT 2005
//
//  Modifications:
//    Jeremy Meredith, Thu Jan  7 12:30:10 EST 2010
//    Refactored out file reading.  Read it before populating the metadata.
//
// ****************************************************************************

void
avtAUXFileFileFormat::PopulateDatabaseMetaData(avtDatabaseMetaData *md)
{
    if (!fileRead)
        ReadFile();

    AddMeshToMetaData(md, "mesh", AVT_RECTILINEAR_MESH, NULL, 1, 0, 2, 2);

    AddScalarVarToMetaData(md, "fluence", "mesh", AVT_NODECENT);
}


// ****************************************************************************
//  Method: avtAUXFileFileFormat::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: miller -- generated by xml2avt
//  Creation:   Tue Mar 15 08:29:20 PDT 2005
//
//  Modifications:
//    Brad Whitlock, Fri Jun 17 14:53:02 PST 2005
//    Modified so it works on Windows.
//
//    Mark C. Miller, Thu Mar 30 16:45:35 PST 2006
//    Made it use VisItStat instead of stat
//
//    Jeremy Meredith, Thu Jan  7 12:31:54 EST 2010
//    Refactored out file reading code.
//
// ****************************************************************************

vtkDataSet *
avtAUXFileFileFormat::GetMesh(const char *meshname)
{
    if (!fileRead)
        ReadFile();

    int i, j;
    char dummyChar;
    int dummyInt;

    vtkFloatArray   *coords[3];

    const char *p = fileBuf;

    //
    // Line 1: scan size information
    //
    if (sscanf(p, "%d %d %d %d %d %d %d \n", &sizeX, &sizeY,
            &dummyInt, &dummyInt, &dummyInt, &dummyInt, &dummyInt) != 7)
    {
        EXCEPTION1(InvalidFilesException, fileName.c_str());
    }
    while (*p != '\n')
        p++;
    p++;

    //
    // Line 2: scan 'X='
    //
    if ((sscanf(p, "X%c\n", &dummyChar) != 1) || (dummyChar != '='))
    {
        EXCEPTION1(InvalidFilesException, fileName.c_str());
    }
    while (*p != '\n')
        p++;
    p++;

    //
    // scan X values
    //
    coords[0] = vtkFloatArray::New();
    coords[0]->SetNumberOfTuples(sizeX);
    for (i = 0; i < sizeX; i++)
    {
        float xVal;
        if (sscanf(p, "%f", &xVal) != 1)
        {
            EXCEPTION1(InvalidFilesException, fileName.c_str());
        }
        coords[0]->SetComponent(i, 0, xVal);
        while (isspace(*p))
            p++;
        while (!isspace(*p))
            p++;
    }
    while (*p != '\n')
        p++;
    p++;

    //
    // Loop to scan y values
    //
    coords[1] = vtkFloatArray::New();
    coords[1]->SetNumberOfTuples(sizeY);
    fluence = vtkFloatArray::New(); 
    fluence->SetNumberOfTuples(sizeX * sizeY);
    for (i = 0; i < sizeY; i++)
    {
        //
        // Line i'th Y entry: scan 'Y='
        //
        float yVal;
        if (sscanf(p, " Y= %f\n", &yVal) != 1)
        {
            EXCEPTION1(InvalidFilesException, fileName.c_str());
        }
        coords[1]->SetComponent(i, 0, yVal);
        while (*p != '\n')
            p++;
        p++;

        //
        // scan fluence values for this Y entry
        //
        for (j = 0; j < sizeX; j++)
        {
            float tmp;
            if (sscanf(p, "%f", &tmp) != 1)
            {
                EXCEPTION1(InvalidFilesException, fileName.c_str());
            }
            fluence->SetComponent(i * sizeX + j, 0, tmp);
            while (isspace(*p))
                p++;
            while (!isspace(*p))
                p++;
        }
        while (*p != '\n')
            p++;
        p++;
    }

    //
    // Deal with the z coordinate
    //
    coords[2] = vtkFloatArray::New();
    coords[2]->SetNumberOfTuples(1);
    coords[2]->SetComponent(0, 0, 0.0);

    //
    // Build rectilinear grid
    //
    vtkRectilinearGrid *rgrid = vtkRectilinearGrid::New();
    int dims[3] = {sizeX, sizeY, 1};
    rgrid->SetDimensions(dims);
    rgrid->SetXCoordinates(coords[0]);
    coords[0]->Delete();
    rgrid->SetYCoordinates(coords[1]);
    coords[1]->Delete();
    rgrid->SetZCoordinates(coords[2]);
    coords[2]->Delete();

    return rgrid;
}


// ****************************************************************************
//  Method: avtAUXFileFileFormat::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: miller -- generated by xml2avt
//  Creation:   Tue Mar 15 08:29:20 PDT 2005
//
// ****************************************************************************

vtkDataArray *
avtAUXFileFileFormat::GetVar(const char *varname)
{
    return fluence;
}


// ****************************************************************************
// Method:  avtAUXFileFileFormat::ReadFile
//
// Purpose:
//   Read the file.
//
// Arguments:
//   none
//
// Programmer:  Jeremy Meredith
// Creation:    January  6, 2010
//
// ****************************************************************************
void
avtAUXFileFileFormat::ReadFile()
{
    //
    // Read the whole file's contents into a buffer
    //
    FileFunctions::VisItStat_t statbuf;
    FileFunctions::VisItStat(fileName.c_str(), &statbuf);
    FileFunctions::VisItOff_t fileSize = statbuf.st_size;

    // for the metadata server, only check the first 10k bytes
    if (avtDatabase::OnlyServeUpMetaData())
        fileSize = 10000;

    FILE *fd = fopen(fileName.c_str(), "r");
    if(fd == 0)
    {
        EXCEPTION1(InvalidFilesException, fileName.c_str());
    }

    fileBuf = new char[fileSize];
    if (fread((void *)fileBuf, 1, fileSize, fd) != (size_t)fileSize)
    {
        fclose(fd);
        delete [] fileBuf;
        fileBuf = 0;

        EXCEPTION1(InvalidFilesException, fileName.c_str());
    }
    fclose(fd);

    if (avtDatabase::OnlyServeUpMetaData())
    {
        if (!StringHelpers::IsPureASCII(fileBuf, fileSize))
            EXCEPTION2(InvalidFilesException, fileName.c_str(), "Not ASCII.");
    }
}
