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

// ************************************************************************* //
//                            avtMDSplusFileFormat.C                         //
// ************************************************************************* //

#include <avtMDSplusFileFormat.h>

#include <string>

#include <vtkCellTypes.h>
#include <vtkFloatArray.h>
#include <vtkRectilinearGrid.h>
#include <vtkLine.h>
#include <vtkUnstructuredGrid.h>

#include <avtDatabaseMetaData.h>

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

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

#include "mdsPlusAPI.h"

using namespace std;

// ****************************************************************************
//  Method: avtMDSplusFileFormat constructor
//
//  Programmer: allen -- generated by xml2avt
//  Creation:   Wed Aug 11 13:45:13 PST 2010
//
// ****************************************************************************

avtMDSplusFileFormat::avtMDSplusFileFormat(const char *filename, DBOptionsAttributes *readOpts)
  : avtMTSDFileFormat(&filename, 1), m_socket(-1)
{
    if (readOpts != NULL) {
      for (int i=0; i<readOpts->GetNumberOfOptions(); ++i) {
        if (readOpts->GetName(i) == "Host")
          m_host = readOpts->GetString("Host");
        else if (readOpts->GetName(i) == "Tree")
          m_tree = readOpts->GetString("Tree");
        else if (readOpts->GetName(i) == "Shot")
          m_shot = readOpts->GetInt("Shot");
        else if (readOpts->GetName(i) == "Signal")
          m_signal = readOpts->GetString("Signal");
        else if (readOpts->GetName(i) == "Mesh")
          m_mesh = readOpts->GetString("Mesh");
      }
    }

    LoadFile();
}


// ****************************************************************************
//  Method: avtEMSTDFileFormat::GetNTimesteps
//
//  Purpose:
//      Tells the rest of the code how many timesteps there are in this file.
//
//  Programmer: allen -- generated by xml2avt
//  Creation:   Wed Aug 11 13:45:13 PST 2010
//
// ****************************************************************************

int
avtMDSplusFileFormat::GetNTimesteps(void)
{
    return m_times.size();
}


// ****************************************************************************
//  Method: avtMDSplusFileFormat::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: allen -- generated by xml2avt
//  Creation:   Wed Aug 11 13:45:13 PST 2010
//
// ****************************************************************************

void
avtMDSplusFileFormat::FreeUpResources(void)
{
}


// ****************************************************************************
//  Method: avtMDSplusFileFormat::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: allen -- generated by xml2avt
//  Creation:   Wed Aug 11 13:45:13 PST 2010
//
// ****************************************************************************

void
avtMDSplusFileFormat::PopulateDatabaseMetaData(avtDatabaseMetaData *md, int timeState)
{
    avtMeshMetaData *mmd;
    avtMeshType mt = AVT_UNSTRUCTURED_MESH;
    int nblocks = 1;
    int block_origin = 0;
    int cell_origin = 0;
    int group_origin = 0;
    int spatial_dimension = 3;
    int topological_dimension = 1;
    double *extents = NULL;

    // Original meshes for the user to see.
//      AddMeshToMetaData( md, string("LinearGrid"),
//                         mt, extents, nblocks, block_origin,
//                         spatial_dimension, topological_dimension );

    mmd = new avtMeshMetaData(string("Signal"),
                              nblocks, block_origin,
                              cell_origin, group_origin,
                              spatial_dimension, topological_dimension, mt);
    mmd->hideFromGUI = true;
    md->Add(mmd);

    mmd = new avtMeshMetaData(string("Experiment"),
                              nblocks, block_origin,
                              cell_origin, group_origin,
                              spatial_dimension, topological_dimension, mt);

    mmd->hideFromGUI = true;
    md->Add(mmd);

    // Populate the scalar field vars that will be interpolate onto a
    // refined mesh.
    for ( int i = 0; i < m_fieldVarNames.size(); ++i )
    {
      string varname = m_fieldVarNames[i];
      AddScalarVarToMetaData( md, "Signals/"+varname, "Signal", AVT_NODECENT );
      AddScalarVarToMetaData( md, "Experiment/"+varname, "Experiment", AVT_NODECENT );
    }
}


// ****************************************************************************
//  Method: avtMDSplusFileFormat::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:
//      timestate   The index of the timestate.  If GetNTimesteps returned
//                  'N' time steps, this is guaranteed to be between 0 and N-1.
//      meshname    The name of the mesh of interest.  This can be ignored if
//                  there is only one mesh.
//
//  Programmer: allen -- generated by xml2avt
//  Creation:   Wed Aug 11 13:45:13 PST 2010
//
// ****************************************************************************

vtkDataSet *
avtMDSplusFileFormat::GetMesh(int timestate, const char *meshname)
{
    debug1 << "Connecting to mdsplus host: " << m_host << endl;

    if( (m_socket = MDS_Connect( m_host.c_str() )) < 0 )
    {
      EXCEPTION2( NonCompliantException, "MDSplus server connection",
                  "Unable to connect to MDS server " + m_host );
    }

    debug1 << "Connecting to mdsplus tree: " << m_tree
           << "  and shot: " << m_shot << endl;

    if( MDS_Open( m_tree.c_str(), m_shot ) < 0 )
    {
      char shotstr[16];
      sprintf( shotstr, "%d", m_shot );

      EXCEPTION2( NonCompliantException, "MDSplus open",
                  m_tree + string(" or ") + shotstr + " was not found." );
    }

    bool valid = is_valid( m_signal.c_str() );

    debug1 << "Retrieving mesh : '" << m_signal << "'  "
           << (valid ? "success" : "failed") << endl;

    if( !valid )
      EXCEPTION2( NonCompliantException, "MDSplus signal validation",
                  "The signal " + m_signal + " was not found." );

    int type = get_type( m_signal.c_str() );
    int rank = get_rank( m_signal.c_str() );
    int size = get_size( m_signal.c_str() );

    int *dims;

    rank = get_dims( m_signal.c_str(), &dims );

    void *values = 0;

    // Get the true values via mdsplus
    double origin[3] = {0,0,0};
    double spacing[3] = {1,1,1};

    if( strcmp(meshname, "Signal" ) == 0 )
      values = get_values( m_signal.c_str(), type );
    else //if( strcmp(meshname, "Experiment" ) == 0 )
    {
    }

    MDS_Disconnect();

    if( rank < 3 )
    {
      dims = (int*) realloc( dims, sizeof(int) *3 );

      for( unsigned int i=rank; i<3; ++i )
        dims[i] = 1;
    }

///////////////////
//     // Create A VTK rectilinear mesh grid for the mesh.
//    vtkFloatArray *coords[3] = {0, 0, 0};

//     for( unsigned int i=0; i<3; ++i )
//     {
//       // set each coordinate
//       coords[i] = vtkFloatArray::New();
//       coords[i]->SetNumberOfTuples(dims[i]);
//       float *coordsPtr = (float *) coords[i]->GetVoidPointer(0);

//       for (int j=0; j<dims[i]; j++)
//         coordsPtr[j] = origin[i] + j * spacing[i];
//     }

//     // create vtkRectilinearGrid objects + set dims and coords
//     vtkRectilinearGrid *grid = vtkRectilinearGrid::New();
//     grid->SetDimensions( coords[0]->GetNumberOfTuples(),
//                          coords[1]->GetNumberOfTuples(),
//                          coords[2]->GetNumberOfTuples() );

//     grid->SetXCoordinates(coords[0]);
//     grid->SetYCoordinates(coords[1]);
//     grid->SetZCoordinates(coords[2]);

//     for( unsigned int i=0; i<3; ++i )
//       coords[i]->Delete();

///////////////////
//     // Create A VTK point mesh grid for the mesh.
//     vtkUnstructuredGrid *grid = vtkUnstructuredGrid::New();

//     vtkPoints *vtkpoints = vtkPoints::New();
//     vtkpoints->Allocate(size);
//     vtkpoints->SetNumberOfPoints((vtkIdType) size);

//     float pt[3];
//     pt[0] = pt[1] = pt[2] = 0;

//     for( unsigned int i=0; i<size; ++i )
//     {
//       pt[0] = i;
//       pt[0] = pt[1] = pt[2] = i;
//       vtkpoints->InsertPoint(i, pt);
//     }

//     grid->SetPoints(vtkpoints);

//     vtkpoints->Delete();

//     grid->Allocate(size);

//     for( unsigned int i=0; i<size; ++i )
//     {
//         vtkIdType vertex = (vtkIdType) i;
//         grid->InsertNextCell(VTK_VERTEX, 1, &vertex);
//     }

//     return grid;


//     // Create a VTK grid for the mesh.
    vtkUnstructuredGrid *grid = vtkUnstructuredGrid::New();

    vtkPoints *vtkpoints = vtkPoints::New();
    vtkpoints->Allocate(size);
    vtkpoints->SetNumberOfPoints((vtkIdType) size);

    float pt[3];
    pt[0] = pt[1] = pt[2] = 0;

    for( unsigned int i=0; i<size; ++i )
    {
      pt[0] = origin[0] + i * spacing[0];

      if( strcmp(meshname, "Signal" ) == 0 )
        pt[1] = ((float *) values)[i];
      else //if( strcmp(meshname, "Experiment" ) == 0 )
      {
        pt[1] =  origin[1] + i * spacing[1];
        pt[2] =  origin[2] + i * spacing[2];
      }

      vtkpoints->InsertPoint(i, pt);
    }

    grid->SetPoints(vtkpoints);

    vtkpoints->Delete();

    grid->Allocate(size);

    vtkLine *line = vtkLine::New();
    for( int i=0; i<size-1; ++i )
    {
      line->GetPointIds()->SetId( 0, i   );
      line->GetPointIds()->SetId( 1, i+1 );
      
      grid->InsertNextCell( line->GetCellType(), line->GetPointIds() );
    }
  
    line->Delete();

    free( dims );
    free( values );

    return grid;
}


// ****************************************************************************
//  Method: avtMDSplusFileFormat::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:
//      timestate  The index of the timestate.  If GetNTimesteps returned
//                 'N' time steps, this is guaranteed to be between 0 and N-1.
//      varname    The name of the variable requested.
//
//  Programmer: allen -- generated by xml2avt
//  Creation:   Wed Aug 11 13:45:13 PST 2010
//
// ****************************************************************************

vtkDataArray *
avtMDSplusFileFormat::GetVar(int timestate, const char *varname)
{
    debug1 << "Connecting to mdsplus host: " << m_host << endl;

    if( (m_socket = MDS_Connect( m_host.c_str() )) < 0 )
    {
      EXCEPTION2( NonCompliantException, "MDSplus server connection",
                  "Unable to connect to MDS server " + m_host );
    }

    debug1 << "Connecting to mdsplus tree: " << m_tree
           << "  and shot: " << m_shot << endl;

    if( MDS_Open( m_tree.c_str(), m_shot ) < 0 )
    {
      char shotstr[16];
      sprintf( shotstr, "%d", m_shot );

      EXCEPTION2( NonCompliantException, "MDSplus tree open",
                  m_tree + string(" or ") + shotstr + " was not found." );
    }

    bool valid = is_valid( m_signal.c_str() );

    debug1 << "Retrieving var: '" << m_signal << "'  "
           << (valid ? "success" : "failed") << endl;

    if( !valid )
      EXCEPTION2( NonCompliantException, "MDSplus signal validation",
                  "The signal " + m_signal + " was not found." );

    int type = get_type( m_signal.c_str() );
    int rank = get_rank( m_signal.c_str() );
    int size = get_size( m_signal.c_str() );

    int *dims;

    rank = get_dims( m_signal.c_str(), &dims );

    void *values = get_values( m_signal.c_str(), type );

    MDS_Disconnect();

    free( dims );

    // VTK structure for the field variable on the linear mesh.
    vtkFloatArray *var = vtkFloatArray::New();

    // Set the number of components before setting the number of tuples
    // for proper memory allocation.
    var->SetNumberOfComponents( 1 );
    var->SetNumberOfTuples( size );

    float* varPtr = (float *) var->GetVoidPointer(0);

    switch( type ) {
    case DTYPE_UCHAR:
    case DTYPE_USHORT:
    case DTYPE_ULONG:
    case DTYPE_ULONGLONG:
    case DTYPE_CHAR:
    case DTYPE_SHORT:
    case DTYPE_LONG:
    case DTYPE_LONGLONG:
      for( int i=0; i<size; ++i)
        *varPtr++ = ((int *) values)[i];
      break;

    case DTYPE_FLOAT:
    case DTYPE_FS:
      for( int i=0; i<size; ++i)
        *varPtr++ = ((float *) values)[i];
      break;

    case DTYPE_DOUBLE:
    case DTYPE_FT:
      for( int i=0; i<size; ++i)
        *varPtr++ = ((double *) values)[i];
      break;

    case DTYPE_CSTRING:
      for( int i=0; i<size; ++i)
        *varPtr++ = ((char *) values)[i];
      break;

    default:
      EXCEPTION2( NonCompliantException, "MDSplus signal read",
                  "The signal " + m_signal + " was not found or wrong type." );
    }

    free( values );

    return var;
}


// ****************************************************************************
//  Method: avtMDSplusFileFormat::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:
//      timestate  The index of the timestate.  If GetNTimesteps returned
//                 'N' time steps, this is guaranteed to be between 0 and N-1.
//      varname    The name of the variable requested.
//
//  Programmer: allen -- generated by xml2avt
//  Creation:   Wed Aug 11 13:45:13 PST 2010
//
// ****************************************************************************

vtkDataArray *
avtMDSplusFileFormat::GetVectorVar(int timestate, const char *varname)
{
}

// ****************************************************************************
//  Method: avtMDSplusFileFormat::LoadFile
//
//  Purpose:
//      Opens an MDSplus database and gets a signal.
//
//  Arguments:
//
//  Programmer: allen -- generated by xml2avt
//  Creation:   Wed Aug 11 13:45:13 PST 2010
//
// ****************************************************************************

void
avtMDSplusFileFormat::LoadFile()
{
    debug1 << "Connecting to mdsplus host: " << m_host << endl;

    if( (m_socket = MDS_Connect( m_host.c_str() )) < 0 )
    {
      char serverError[128];
      sprintf( serverError, "MDSplus - Unable to connect to MDSpus server %s",
               m_host.c_str() );

      EXCEPTION1( InvalidFilesException, serverError );
    }

    debug1 << "Connecting to mdsplus tree: " << m_tree
           << "  and shot: " << m_shot << endl;

    if( MDS_Open( m_tree.c_str(), m_shot ) < 0 )
    {
      char shotstr[16];
      sprintf( shotstr, "%d", m_shot );

      EXCEPTION2( NonCompliantException, "MDSplus tree open",
                  m_tree + string(" or ") + shotstr + " was not found." );
    }

    bool valid = is_valid( m_signal.c_str() );

    char* units = (char *)
      get_value( "units_of", m_signal.c_str(), DTYPE_CSTRING );

    cerr << "units " << units << endl;

    free( units );

    double* dim = (double *)
      get_value( "dim_of", m_signal.c_str(), DTYPE_DOUBLE );

    cerr << "dim " << *dim << endl;

    free( dim );

    debug1 << "Validating signal : '" << m_signal << "'  "
           << (valid ? "success" : "failed") << endl;

    if( !valid )
      EXCEPTION2( NonCompliantException, "MDSplus signal validation",
                  "The signal " + m_signal + " was not found." );

    MDS_Disconnect();

    m_fieldVarNames.push_back( m_signal );

    m_cycles.push_back(0);
    m_times.push_back(0);
}


// ****************************************************************************
//  Method: avtMDSplusFileFormat::GetCycles
//
//  Purpose:
//      Returns the cycles
//
//  Arguments:
//      c          the cycles
//
//  Programmer: allen
//  Creation:   
//
// ****************************************************************************

void avtMDSplusFileFormat::GetCycles(std::vector<int> &cycles)
{
    cycles = m_cycles;
}


// ****************************************************************************
//  Method: avtMDSplusFileFormat::GetTimes
//
//  Purpose:
//      Returns the times
//
//  Arguments:
//      t          the times
//
//  Programmer: allen
//  Creation:   
//
// ****************************************************************************

void avtMDSplusFileFormat::GetTimes(std::vector<double> &times)
{
    times = m_times;
}
