/*****************************************************************************
*
* Copyright (c) 2000 - 2018, Lawrence Livermore National Security, LLC
* Produced at the Lawrence Livermore National Laboratory
* LLNL-CODE-442911
* All rights reserved.
*
* This file is  part of VisIt. For  details, see https://visit.llnl.gov/.  The
* full copyright notice is contained in the file COPYRIGHT located at the root
* of the VisIt distribution or at http://www.llnl.gov/visit/copyright.html.
*
* Redistribution  and  use  in  source  and  binary  forms,  with  or  without
* modification, are permitted provided that the following conditions are met:
*
*  - Redistributions of  source code must  retain the above  copyright notice,
*    this list of conditions and the disclaimer below.
*  - Redistributions in binary form must reproduce the above copyright notice,
*    this  list of  conditions  and  the  disclaimer (as noted below)  in  the
*    documentation and/or other materials provided with the distribution.
*  - Neither the name of  the LLNS/LLNL nor the names of  its contributors may
*    be used to endorse or promote products derived from this software without
*    specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT  HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR  IMPLIED WARRANTIES, INCLUDING,  BUT NOT  LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND  FITNESS FOR A PARTICULAR  PURPOSE
* ARE  DISCLAIMED. IN  NO EVENT  SHALL LAWRENCE  LIVERMORE NATIONAL  SECURITY,
* LLC, THE  U.S.  DEPARTMENT OF  ENERGY  OR  CONTRIBUTORS BE  LIABLE  FOR  ANY
* DIRECT,  INDIRECT,   INCIDENTAL,   SPECIAL,   EXEMPLARY,  OR   CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT  LIMITED TO, PROCUREMENT OF  SUBSTITUTE GOODS OR
* SERVICES; LOSS OF  USE, DATA, OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER
* CAUSED  AND  ON  ANY  THEORY  OF  LIABILITY,  WHETHER  IN  CONTRACT,  STRICT
* LIABILITY, OR TORT  (INCLUDING NEGLIGENCE OR OTHERWISE)  ARISING IN ANY  WAY
* OUT OF THE  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
*****************************************************************************/

// ************************************************************************* //
//                            avtGHOSTFileFormat.C                           //
// ************************************************************************* //

#include <avtGHOSTFileFormat.h>

#include <vtkFloatArray.h>
#include <vtkRectilinearGrid.h>
#include <avtDatabaseMetaData.h>
#include <InvalidVariableException.h>
#include <InvalidFilesException.h>
#include <avtParallel.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#ifdef _WIN32
#include <io.h>
#define FSEEK _fseeki64
#define CLOSE ::_close
#define OPEN  ::_open
#define READ  ::_read
typedef __int64 off64_t; 
#else
#include <unistd.h>
#define FSEEK fseek
#define O_BINARY 0
#define CLOSE ::close
#define OPEN  ::open
#define READ  ::read
#ifdef __APPLE__
#define off64_t off_t 
#endif
#endif

using namespace std;

// ****************************************************************************
//  Method: avtGHOSTFileFormat constructor
//
//  Programmer: pugmire -- generated by xml2avt
//  Creation:   Mon May 6 14:21:26 PST 2013
//
// ****************************************************************************

avtGHOSTFileFormat::avtGHOSTFileFormat(const char *filename)
    : avtSTMDFileFormat(&filename, 1)
{
    metaDataFile = filename;
    nBlocks = PAR_Size();
    initialized = false;

    for (int i = 0; i < 3; i++)
        meshSz[i] = 0;
    
    ReadMetaData();
}


// ****************************************************************************
//  Method: avtGHOSTFileFormat::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: pugmire -- generated by xml2avt
//  Creation:   Mon May 6 14:21:26 PST 2013
//
// ****************************************************************************

void
avtGHOSTFileFormat::PopulateDatabaseMetaData(avtDatabaseMetaData *md)
{
    ReadMetaData();
    
    AddMeshToMetaData(md, "mesh", AVT_RECTILINEAR_MESH, NULL, nBlocks, 0, 3, 3);
    for (size_t i = 0; i < vars.size(); i++)
        AddScalarVarToMetaData(md, vars[i].first.c_str(), "mesh", AVT_NODECENT);
}


// ****************************************************************************
//  Method: avtGHOSTFileFormat::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:
//      domain      The index of the domain.  If there are NDomains, this
//                  value is guaranteed to be between 0 and NDomains-1,
//                  regardless of block origin.
//      meshname    The name of the mesh of interest.  This can be ignored if
//                  there is only one mesh.
//
//  Programmer: pugmire -- generated by xml2avt
//  Creation:   Mon May 6 14:21:26 PST 2013
//
// ****************************************************************************

vtkDataSet *
avtGHOSTFileFormat::GetMesh(int domain, const char *meshname)
{
    vtkRectilinearGrid *rv = vtkRectilinearGrid::New();

    int r0[3], r1[3];
    GetRange(domain, r0, r1);
    
    vtkFloatArray *coords[3];
    int dims[3];
    for (int i = 0; i < 3; i++)
    {
        dims[i] = r1[i]-r0[i];
        coords[i] = vtkFloatArray::New();
        coords[i]->SetNumberOfTuples(dims[i]);

        for (int j = 0; j < dims[i]; j++)
            coords[i]->SetTuple1(j, r0[i]+j);
    }

    // Data are FORTRAN ordered, so swap the X,Z coords.
    int swapDims[3] = {dims[2], dims[1], dims[0]};
    rv->SetDimensions(swapDims);
    rv->SetXCoordinates(coords[2]);
    rv->SetYCoordinates(coords[1]);
    rv->SetZCoordinates(coords[0]);    

    for (int i = 0; i < 3; i++)
        coords[i]->Delete();
        
    return rv;   
}


// ****************************************************************************
//  Method: avtGHOSTFileFormat::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:
//      domain     The index of the domain.  If there are NDomains, this
//                 value is guaranteed to be between 0 and NDomains-1,
//                 regardless of block origin.
//      varname    The name of the variable requested.
//
//  Programmer: pugmire -- generated by xml2avt
//  Creation:   Mon May 6 14:21:26 PST 2013
//
// ****************************************************************************

vtkDataArray *
avtGHOSTFileFormat::GetVar(int domain, const char *varname)
{
    int r0[3], r1[3];
    GetRange(domain, r0, r1);
    
    int fd = -1;
    for (size_t i = 0; i < vars.size(); i++)
        if (vars[i].first == varname)
        {
            fd = OPEN(vars[i].second.c_str(), O_RDONLY|O_BINARY);
            break;
        }
    
    if (fd < 0)
    {
        EXCEPTION1(InvalidVariableException, varname);
    }

    off64_t nX = r1[0]-r0[0];
    off64_t nY = r1[1]-r0[1];
    off64_t nZ = r1[2]-r0[2];
    int nVals = nX*nY*nZ;

    //off64_t offset = (nX*nY*r0[2]*sizeof(float));

    vtkDataArray *arr = vtkFloatArray::New();
    arr->SetNumberOfTuples(nVals);
    
    float *out = (float *)arr->GetVoidPointer(0);
    size_t nRead = READ(fd, out, nVals*sizeof(float)); (void) nRead;

    CLOSE(fd);
    return arr;
}


//****************************************************************************
// Method:  avtGHOSTFileFormat::ReadMetaData
//
// Purpose:
//   Read meta data
//
// Programmer:  Dave Pugmire
// Creation:    May  8, 2013
//
// Modifications:
//
//****************************************************************************

void
avtGHOSTFileFormat::ReadMetaData()
{
    if (initialized)
        return;

    char line[1024], varNm[1024], varFile[1024];
    ifstream ifile(metaDataFile.c_str());
    
    if (!ifile.is_open())
    {
        EXCEPTION1(InvalidFilesException, metaDataFile);
    }
    
    while (! ifile.eof())
    {
        ifile.getline(line, 1024);
        if (line[0] == '#')
            continue;
        if (strncmp(line, "MESH", 4) == 0)
            sscanf(line, "MESH %d %d %d", &meshSz[0], &meshSz[1], &meshSz[2]);
        else if (strncmp(line, "VAR", 3) == 0)
        {
            sscanf(line, "VAR %s %s", varNm, varFile);
            vars.push_back(pair<string,string>(varNm, varFile));
        }
    }

    initialized = true;
}


//****************************************************************************
// Method:  avtGHOSTFileFormat::GetRange
//
// Purpose:
//   Get X,Y,Z extents for spatial domain.
//
// Programmer:  Dave Pugmire
// Creation:    May  8, 2013
//
// Modifications:
//
//****************************************************************************

void
avtGHOSTFileFormat::GetRange(int dom, int *r0, int *r1)
{
    r0[0] = r0[1] = r0[2] = 0;
    r1[0] = meshSz[0];
    r1[1] = meshSz[1];
    r1[2] = meshSz[2];

    if (nBlocks > 1)
    {
        int zPer = (r1[2]-r0[2])/nBlocks;
        r0[2] = dom*zPer;
        r1[2] = r0[2]+zPer+1;
        if (dom == nBlocks-1)
            r1[2] = meshSz[2];
    }
}
