/*****************************************************************************
*
* Copyright (c) 2000 - 2017, 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.
*
*****************************************************************************/

// ************************************************************************* //
//                            avtWellLogsFileFormat.C                           //
// ************************************************************************* //

#include <avtWellLogsFileFormat.h>

#include <string>
#include <fstream>
#include <iostream>
#include <iomanip>
#include <map>

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

#include <avtDatabaseMetaData.h>
#include <avtMaterial.h>

#include <DBOptionsAttributes.h>
#include <Expression.h>

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


using     std::string;


// ****************************************************************************
//  Method: avtWellLogsFileFormat constructor
//
//  Programmer: hari -- generated by xml2avt
//  Creation:   Fri Apr 11 23:22:06 PST 2014
//
// ****************************************************************************

avtWellLogsFileFormat::avtWellLogsFileFormat(const char *filename)
    : avtSTSDFileFormat(filename)
{
    wlFilename = filename;
    haveReadFile = false;
}


// ****************************************************************************
//  Method: avtWellLogsFileFormat::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: hari -- generated by xml2avt
//  Creation:   Fri Apr 11 23:22:06 PST 2014
//
// ****************************************************************************

void
avtWellLogsFileFormat::FreeUpResources(void)
{
    X.clear();
    Y.clear();
    Z1.clear();
    Z2.clear();
    haveReadFile = false;
}


// ****************************************************************************
//  Method: avtWellLogsFileFormat::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: hari -- generated by xml2avt
//  Creation:   Fri Apr 11 23:22:06 PST 2014
//
// ****************************************************************************

void
avtWellLogsFileFormat::PopulateDatabaseMetaData(avtDatabaseMetaData *md)
{
    if (!haveReadFile)
        ReadFile();

    string meshname = "wells";
    avtMeshType mt = AVT_UNSTRUCTURED_MESH;

    int nblocks = 1; 
    int block_origin = 0;
    int spatial_dimension = 3;
    int topological_dimension = 1;
    double *extents = NULL;
    AddMeshToMetaData(md, meshname, mt, extents, nblocks, block_origin,
                      spatial_dimension, topological_dimension);

/*
    AddScalarVarToMetaData(md, "pressure", meshname, AVT_NODECENT);
    AddScalarVarToMetaData(md, "density", meshname, AVT_NODECENT);
    AddScalarVarToMetaData(md, "temperature", meshname, AVT_NODECENT);
 */

    string matname = "wellID";
    int nmats = (int)wellNames.size();
    AddMaterialToMetaData(md, matname, meshname, nmats, wellNames);
}

// ****************************************************************************
//  Method: avtWellLogsFileFormat::ReadFile
//
//  Purpose:
//      Actually reads a CSV file and pulls out the well names and locations.
//
//  Programmer: Hank Childs and Jennifer Horsman
//  Creation:   October 19, 2010
//
// ****************************************************************************

void
avtWellLogsFileFormat::ReadFile(void)
{
    if (haveReadFile)
        return;

    std::ifstream ifile(wlFilename.c_str());
    ifile >> setprecision(9);
    if (ifile.fail())
    {
        EXCEPTION1(InvalidFilesException, wlFilename.c_str());
    }   

    char line[1024];
    ifile.getline(line, 1024);

    int wellId = -1;
    int utm83EId = -1;
    int utm83NId = -1;
    int wellBottomId = -1;
    int groundElevationId = -1;
    int depenvId = -1;

    char *s = line;
    int idx = 0;
    do 
    {
        char *next = strstr(s, ",");
        bool atEnd = false;
        if (next == NULL || next[0] == '\0')
            atEnd = true;
        if (next != NULL)
           *(next) = '\0';
        if (!atEnd)
            next++;
        if (strcmp(s, "WELL_ID") == 0 || strcmp(s, "site_id") == 0)
        {
            filetype = strcmp(s, "WELL_ID") == 0 ? 1 : 2; //assign which type of file is being read in..
            wellId = idx;
        }
        else if (strcmp(s, "UTM83E") == 0 || strcmp(s, "UTME") == 0)
            utm83EId = idx;
        else if (strcmp(s, "UTM83N") == 0 || strcmp(s, "UTMN") == 0)
            utm83NId = idx;
        else if (strcmp(s, "WELL_BOTTOM") == 0 || strcmp(s, "interval_bottom") == 0)
            wellBottomId = idx;
        else if (strcmp(s, "GROUND_ELEVATION") == 0)
            groundElevationId = idx;
        else if (strcmp(s, "dep_env") == 0)
            depenvId = idx;

        s = next;
        idx++;
    } while (s != NULL && *s != '\0');

    if (filetype == 1)
    {
        if (wellId < 0 || utm83EId < 0 || utm83NId < 0 || wellBottomId < 0 ||
            groundElevationId < 0)
        {
            EXCEPTION1(InvalidFilesException, wlFilename.c_str())
        }
    }
    else
    {
        if (wellId < 0 || utm83EId < 0 || utm83NId < 0 || wellBottomId < 0 ||
            depenvId < 0)
        {
            EXCEPTION1(InvalidFilesException, wlFilename.c_str())
        }
    }

    std::cerr << setprecision(9);
    std::cerr << "Ids = " << wellId << ", " << utm83EId << ", " << utm83NId << ", "
         << wellBottomId << ", " << groundElevationId << " " << depenvId << " " << filetype << endl;

    std::string lastWellName = "";
    float lastWellBottom = 0.0f,lasttD = 0.0f;

    std::string twellName = "";
    float tX = -1,tY = -1,tZ = -1,tD = -1;
    float prevtX = -1,prevtY = -1,prevtZ = -1,prevtD = -1;

    std::map<int,int> idmap;

    while (! ifile.eof())
    {
        ifile.getline(line, 1024);
        int idx = 0;
        char *s = line;
        if (s[0] == '\0')
            continue;
        do 
        {
            char *next = strstr(s, ",");
            bool atEnd = false;
            if (next == NULL || next[0] == '\0')
                atEnd = true;
            if (next != NULL)
               *(next) = '\0';
            if (!atEnd)
                next++;
 
            if(filetype == 1)
            {
                if (wellId == idx)
                {
                    wellNames.push_back(s);
                    wellIds.push_back((int)wellNames.size()-1);
                }
                else if (utm83EId == idx)
                    X.push_back(atof(s));
                else if (utm83NId == idx)
                    Y.push_back(atof(s));
                else if (wellBottomId == idx)
                    Z1.push_back(atof(s));
                else if (groundElevationId == idx || depenvId == idx)
                    Z2.push_back(atof(s));
            }
            else
            {
                //store all variables..
                if (wellId == idx)
                    twellName = s;
                else if (utm83EId == idx)
                    tX = (atof(s));
                else if (utm83NId == idx)
                    tY = (atof(s));
                else if (wellBottomId == idx)
                    tZ = (atof(s));
                else if (depenvId == idx)
                    tD = (atof(s));
            }
            s = next;
            idx++;
        } while (s != NULL && *s != '\0');

        if(filetype == 2)
        {
            //now that everything has been read in, record it..
//            if(twellName != lastWellName)
//            {
//                //start of a new well..
//                lastWellName = twellName;
//                lastWellBottom = tZ;
//                //std::cout << lastWellName << " " << lastWellBottom << std::endl;
//            }
//            else
//            {
//                wellIds.push_back(tD);
//                X.push_back(tX);
//                Y.push_back(tY);
//                Z1.push_back(lastWellBottom);
//                Z2.push_back(tZ);
//                //std::cout << twellName << " " << tD << " " << tX << " " << tY << " " << lastWellBottom << " " << tZ << std::endl;
//                lastWellBottom = tZ;
//            }

            if(twellName != lastWellName || tD != lasttD)
            {
                if(lastWellBottom != -1 && prevtD != -1)
                {

                    int id = -1;
                    if(idmap.count(prevtD) == 0)
                    {

                        char buffer[1024];
                        sprintf(buffer,"DEP_%d",(int)prevtD);
                        wellNames.push_back(buffer);
                        id = (int)wellNames.size() - 1;
                        idmap[prevtD] = id;

                    }
                    else
                    {
                        id = idmap[prevtD];
                    }

                    //wellNames.push_back(buffer);
                    wellIds.push_back(id); //prevtD
                    X.push_back(prevtX);
                    Y.push_back(prevtY);
                    Z1.push_back(lastWellBottom);
                    Z2.push_back(prevtZ);
                    //std::cout << prevtD << " " << prevtX << " " << prevtY << " " << lastWellBottom << " " << prevtZ << std::endl;
                }

                if(tD != lasttD && twellName == lastWellName)
                {
                    lastWellBottom = prevtZ;
                }
                else
                {
                    lastWellBottom = tZ;
                }

                //start of a new well..
                lastWellName = twellName;
                lasttD = tD;
                //std::cout << lastWellName << " " << lastWellBottom << " " << lasttD << std::endl;
            }
            else
            {
                prevtD = tD;
                prevtX = tX;
                prevtY = tY;
                prevtZ = tZ;
            }
        }
    }

    //end of file write last line out if it exists
    if(lastWellBottom != -1 && prevtD != -1)
    {
        int id = -1;
        if(idmap.count(prevtD) == 0)
        {

            char buffer[1024];
            sprintf(buffer,"DEP_%d",(int)prevtD);
            wellNames.push_back(buffer);
            id = (int)wellNames.size() - 1;
            idmap[prevtD] = id;

        }
        else
        {
            id = idmap[prevtD];
        }

        wellIds.push_back(id);
        X.push_back(prevtX);
        Y.push_back(prevtY);
        Z1.push_back(lastWellBottom);
        Z2.push_back(prevtZ);
    }
    //std::cout << "Sizes " << wellIds.size() << std::endl;
    haveReadFile = true;
}

// ****************************************************************************
//  Method: avtWellLogsFileFormat::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: hari -- generated by xml2avt
//  Creation:   Fri Apr 11 23:22:06 PST 2014
//
// ****************************************************************************

vtkDataSet *
avtWellLogsFileFormat::GetMesh(const char *meshname)
{
    int  i;

    // Return all well locations.
    vtkUnstructuredGrid *ugrid = vtkUnstructuredGrid::New();
    vtkPoints *pts = vtkPoints::New();
   
    int nWells = (int)X.size();
    int pointsPerWell = 2;

    pts->SetNumberOfPoints(nWells*pointsPerWell);

    for (i = 0 ; i < nWells ; i++)
    {
        double pt[3] = { X[i], Y[i], Z1[i] };
        pts->SetPoint(2*i, pt);
        pt[2] = Z2[i];
        pts->SetPoint(2*i+1, pt);
    }
      
    ugrid->SetPoints(pts);
    pts->Delete();

    int nCellsPerWell = 1;
    int totalCells = nCellsPerWell*nWells;
    int connSizePerCell = 1+2;  // 1 to declare number of points (2)
                                // 2 for the actual points
    int connSize = connSizePerCell*totalCells;
    ugrid->Allocate(connSize);

    for (i = 0 ; i < nWells ; i++)
    {
        vtkIdType ids[2];
        ids[0] = 2*i;
        ids[1] = 2*i+1;
        ugrid->InsertNextCell(VTK_LINE, 2, ids);
    }
      
    return ugrid;
}


// ****************************************************************************
//  Method: avtWellLogsFileFormat::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: hari -- generated by xml2avt
//  Creation:   Fri Apr 11 23:22:06 PST 2014
//
// ****************************************************************************

vtkDataArray *
avtWellLogsFileFormat::GetVar(const char *varname)
{
/*
    int ntuples = 1000; 
    vtkFloatArray *rv = vtkFloatArray::New();
    rv->SetNumberOfTuples(ntuples);
    if (strcmp(varname, "pressure") == 0)
        for (int i = 0 ; i < ntuples ; i++)
            rv->SetTuple1(i, i%10);
    if (strcmp(varname, "density") == 0)
        for (int i = 0 ; i < ntuples ; i++)
            rv->SetTuple1(i, (i/10)%10);
    if (strcmp(varname, "temperature") == 0)
        for (int i = 0 ; i < ntuples ; i++)
            rv->SetTuple1(i, i/100);
    return rv;
 */
    EXCEPTION0(ImproperUseException);
}



void *
avtWellLogsFileFormat::GetAuxiliaryData(const char *var, const char *type,
                                        void *args, DestructorFunction &df)
{
    if (strcmp(type, AUXILIARY_DATA_MATERIAL) == 0)
    {
        if (strcmp(var, "wellID") != 0)
        {
            EXCEPTION1(InvalidVariableException, var);
        }

        int nWells = (int)wellIds.size();
        int numberOfSegments = (int)wellIds.size(); // may be different in future
        avtMaterial *mat = new avtMaterial(nWells, wellNames, numberOfSegments,
                                           &(wellIds[0]), 0, NULL, NULL, NULL, NULL);
        df = avtMaterial::Destruct;
        return (void *) mat;
    }

    return NULL;
}


// ****************************************************************************
//  Method: avtWellLogsFileFormat::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: hari -- generated by xml2avt
//  Creation:   Fri Apr 11 23:22:06 PST 2014
//
// ****************************************************************************

vtkDataArray *
avtWellLogsFileFormat::GetVectorVar(const char *varname)
{
    return NULL;

    //
    // If you have a file format where variables don't apply (for example a
    // strictly polygonal format like the STL (Stereo Lithography) format,
    // then uncomment the code below.
    //
    // EXCEPTION1(InvalidVariableException, varname);
    //

    //
    // If you do have a vector variable, here is some code that may be helpful.
    //
    // int ncomps = YYY;  // This is the rank of the vector - typically 2 or 3.
    // int ntuples = XXX; // this is the number of entries in the variable.
    // vtkFloatArray *rv = vtkFloatArray::New();
    // int ucomps = (ncomps == 2 ? 3 : ncomps);
    // rv->SetNumberOfComponents(ucomps);
    // rv->SetNumberOfTuples(ntuples);
    // float *one_entry = new float[ucomps];
    // for (int i = 0 ; i < ntuples ; i++)
    // {
    //      int j;
    //      for (j = 0 ; j < ncomps ; j++)
    //           one_entry[j] = ...
    //      for (j = ncomps ; j < ucomps ; j++)
    //           one_entry[j] = 0.;
    //      rv->SetTuple(i, one_entry); 
    // }
    //
    // delete [] one_entry;
    // return rv;
    //
}
