/*****************************************************************************
*
* Copyright (c) 2000 - 2017, Lawrence Livermore National Security, LLC
* Produced at the Lawrence Livermore National Laboratory
* All rights reserved.
*
* This file is part of VisIt. For details, see http://www.llnl.gov/visit/. 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 materials provided with the distribution.
*  - Neither the name of the UC/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  THE  REGENTS  OF  THE  UNIVERSITY OF
* CALIFORNIA, 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.
*
*****************************************************************************/

// ************************************************************************* //
//                            avtparaDISFileFormat.C                           //
// ************************************************************************* //
#define NO_BOOST 1
#include <avtparaDISFileFormat.h>
#include <avtparaDISOptions.h>
#include <DBOptionsAttributes.h>
#include <DebugStream.h>
#include "stringutil.h"
#include "debugutil.h"
#include "pathutil.h"
#include "version.h"
#include "paradis.h"
#include <string>
#include <vector>

#ifdef PARALLEL
#include <avtParallel.h>
#endif

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

#include <InvalidVariableException.h>

using     namespace std;
using     rclib::Point; 


// ****************************************************************************
//  Method: avtparaDIS constructor
//
//  Programmer: rcook -- generated by xml2avt
//  Creation:   Tue Jan 30 14:56:34 PST 2007
//  Note the presence of DBOptionsAttributes means there is an options dialog
//  and the user sets rdatts values through this dialog. 
//
// ****************************************************************************
 
avtparaDISFileFormat::avtparaDISFileFormat(const char *filename,
                                           DBOptionsAttributes *rdatts)
  : avtSTSDFileFormat(filename), mParallelData(filename), mDumpfile(filename, rdatts) {

  if (filename) {
    mFilename = filename; 
  }
  mFormat = PARADIS_NO_FORMAT;
  mVerbosity = 0; 

  if (mParallelData.ParseMetaDataFile()) {  
    mFormat = PARADIS_PARALLEL_FORMAT;
  } else if (mDumpfile.FileIsValid()) {
    mFormat = PARADIS_DUMPFILE_FORMAT; 
  }
  debug1 << "avtparaDISFileFormat, filename="<<filename<<";  development code" << endl;

  return; 
}


void avtparaDISFileFormat::Clear(void) {
  mFilename = ""; 
  mFormat = PARADIS_NO_FORMAT;
  mVerbosity = 0; 
  mDumpfile.Clear(); 
  mParallelData.Clear(); 

    
  return; 
}

// ****************************************************************************
//  Method: avtparaDISFileFormat::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: rcook -- generated by xml2avt
//  Creation:   Tue Jan 30 14:56:34 PST 2007
//
// ****************************************************************************

void
avtparaDISFileFormat::FreeUpResources(void)
{
  debug1 << "avtparaDISFileFormat::FreeUpResources(void)" << endl; 
  return; 
}



// ****************************************************************************
//  Method: avtparaDISFileFormat::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: rcook -- generated by xml2avt
//  Creation:   Tue Jan 30 14:56:34 PST 2007
//
// ****************************************************************************

void
avtparaDISFileFormat::PopulateDatabaseMetaData(avtDatabaseMetaData *md)
{
  static bool haveIssuedVersionInfo = false;

  if (!haveIssuedVersionInfo)
      cerr << "using avtparaDISFileFormat::avtparaDISFileFormat version 2.3.4" << endl; 
  haveIssuedVersionInfo = true;
  debug1 << "using avtparaDISFileFormat::avtparaDISFileFormat version 2.3.4" << endl; 
  debug1 << "starting populateDatabaseMetaData" << endl; 
  debug1 << "avtparaDISFileFormat version " << PARADIS_READER_VERSION_NUMBER << ", " << PARADIS_READER_VERSION_DATE << endl;
  md->SetMustRepopulateOnStateChange(true); 

  // Create a "segments" mesh and a "nodes" mesh
  int nblocks = 1; /* This is for multi-domain, we are single-domain self-decomposing */ 
  int block_origin = 0; 
  int spatial_dimension = 3; /* 3D space */ 
  //double *extents = NULL; 

  
  if (mFormat == PARADIS_PARALLEL_FORMAT) {
    // mParallelData.ParseMetaDataFile()) {  
    debug1 << "populateDatabaseMetaData using the newer parallelizable data format... " << endl;
    
   /*!
      =======================================================
      populate the segments mesh for parallel data files
      =======================================================
    */ 
    string meshname = "segments"; 
    int topological_dimension = 1; /* lines */ 
    avtMeshType meshtype = AVT_UNSTRUCTURED_MESH; 
    
    AddMeshToMetaData(md, meshname, meshtype, NULL, nblocks, block_origin,
                      spatial_dimension, topological_dimension);
    
    int numvars = mParallelData.mSegmentFiles.mDataArrayNames.size(); 
    int varnum = 0; 
    for (varnum = 0; varnum < numvars; ++varnum) {
      string name =  mParallelData.mSegmentFiles.mDataArrayNames[varnum];
      int dim = mParallelData.mSegmentFiles.mComponentsPerVar[varnum];
      if (name == "StartPos" || name == "EndPos" || name == "NodePos") continue; 
      if (dim > 1) {
        AddVectorVarToMetaData(md, name, "segments", AVT_ZONECENT, dim); 
        debug2 << "AddVectorVarTo segments mesh: name=" << name << ", dim=" << dim << endl; 
      } else {      
        AddScalarVarToMetaData(md, name, "segments", AVT_ZONECENT); 
        debug2 << "AddScalarVarTo segments mesh: name=" << name  << endl; 
      }
    }
    
    // Add burgers vector scalars to the segments mesh
    AddMaterialToMetaData(md, "Burgers type", "segments", 
                          mParallelData.mBurgersTypes.size(), 
                          mDumpfile.mBurgersTypes);
    
    /*!
      =======================================================
      populate the nodes mesh for parallel data files
      =======================================================
    */ 
    
    meshname = "nodes"; 
    topological_dimension = 0; /* only points */ 
    meshtype = AVT_POINT_MESH; 
    AddMeshToMetaData(md, meshname, meshtype, NULL, nblocks, block_origin,
                      spatial_dimension, topological_dimension);
    numvars = mParallelData.mNodeFiles.mDataArrayNames.size(); 
    for (varnum = 0; varnum < numvars; ++varnum) { 
      string name =  mParallelData.mNodeFiles.mDataArrayNames[varnum];
      int dim = mParallelData.mNodeFiles.mComponentsPerVar[varnum];
      if (dim > 1) {
        AddVectorVarToMetaData(md, name, "nodes", AVT_NODECENT, dim); 
        debug2 << "AddVectorVarTo nodes mesh: name=" << name << ", dim=" << dim << endl; 
      } else {      
        AddScalarVarToMetaData(md, name, "nodes", AVT_NODECENT); 
        debug2 << "AddScalarVarTo nodes mesh: name=" << name  << endl; 
      }
    }    
    
    md->SetFormatCanDoDomainDecomposition(true);  
  } // end of parallel data format mesh creation 
  else if (mFormat == PARADIS_DUMPFILE_FORMAT) { 
    /*! 
      ==============================================
      //  for dumpfile (serial)
      ==============================================
    */
    debug1 << " populateDatabaseMetaData detected dumpfile-based dataset" << endl;  
    
#ifdef PARALLEL
    EXCEPTION1(VisItException, "You cannot read old ParaDIS dump files in parallel.  Run VisIt in serial to do this, or create ParaDIS parallel vis files.  Contact Rich Cook at 423-9605 regarding this error.");     
#else
    
    string meshname; 
    int topological_dimension = 1; /* lines */ 
    avtMeshType meshtype; 

     
    /*!
      =======================================================
      populate the nodes mesh for dumpfile (serial) 
      =======================================================
    */ 
    meshname = "nodes"; 
    topological_dimension = 0; /* only points */ 
    meshtype = AVT_POINT_MESH; 
    AddMeshToMetaData(md, meshname, meshtype, mDumpfile.mExtents, nblocks, block_origin,
                      spatial_dimension, topological_dimension);
    
    
    // use a material for the node types 
    AddMaterialToMetaData(md, "Node-Num-Neighbors", "nodes", mDumpfile.mNodeNeighborValues.size(), mDumpfile.mNodeNeighborValues);
    
    AddScalarVarToMetaData(md, "Node-ID-Hash", "nodes", AVT_NODECENT); 
    AddScalarVarToMetaData(md, "Node-Index", "nodes", AVT_NODECENT); 

    // Can't use enumerated scalar here because nodes can have almost arbitrary types.  
    
    avtScalarMetaData *node_type =
      new avtScalarMetaData("Node-Type", "nodes", AVT_NODECENT);
    node_type->SetEnumerationType(avtScalarMetaData::ByValue);    
    node_type->AddEnumNameValue("LESS THAN -10", -11);
    for (int i = -10; i<7; i++) {
      if (i<-3 || i>0)
        node_type->AddEnumNameValue(str(boost::format("%d")%i), i); 
    }
    node_type->AddEnumNameValue("GREATER THAN 10", 10);      
    md->Add(node_type);

    avtScalarMetaData *node_loop =
      new avtScalarMetaData("Node-Is-Loop", "nodes", AVT_NODECENT);
    node_loop->SetEnumerationType(avtScalarMetaData::ByValue);
    node_loop->AddEnumNameValue("NOT LOOP", 0);
    node_loop->AddEnumNameValue("LOOP", 1);
    md->Add(node_loop);
    
    avtScalarMetaData *node_typem =
      new avtScalarMetaData("Node-Is-Type-M", "nodes", AVT_NODECENT);
    node_typem->SetEnumerationType(avtScalarMetaData::ByValue);
    node_typem->AddEnumNameValue("NOT TYPE M", 0);
    node_typem->AddEnumNameValue("TYPE M", 1);
    md->Add(node_typem);
    
    avtScalarMetaData *node_typen =
      new avtScalarMetaData("Node-Is-Type-N", "nodes", AVT_NODECENT);
    node_typen->SetEnumerationType(avtScalarMetaData::ByValue);
    node_typen->AddEnumNameValue("NOT TYPE N", 0);
    node_typen->AddEnumNameValue("TYPE N", 1);
    md->Add(node_typen);
    
     
   /*!
      =======================================================
      populate the segments mesh for dumpfile (serial) 
      =======================================================
    */ 
    
    meshname = "segments"; 
    topological_dimension = 1; /* lines */ 
    meshtype = AVT_UNSTRUCTURED_MESH; 
    
    AddMeshToMetaData(md, meshname, meshtype, mDumpfile.mExtents, nblocks, block_origin,
                      spatial_dimension, topological_dimension);
    
    AddScalarVarToMetaData(md, "Segment-Index", "segments", AVT_ZONECENT);
 
    AddMaterialToMetaData(md, "Segment-Burgers-Type", "segments",
                          mDumpfile.mSegmentBurgerTypeNames.size(), 
                          mDumpfile.mSegmentBurgerTypeNames); 
   // Enumerated types give you some more flexibility in the interface and allows arbitrary values, not just 0-indexed list.  Use if there is a palette of choices.  Stolen from avtOUTCARFileFormat
    /*    int btypes[] = {-2,-1, 0, 10,11,12,13, 20,21,22, 30,31,32, 40,41,42, 50, 60}; 
          avtScalarMetaData *burgers_smd =
          new avtScalarMetaData("Segment-Burgers-Type", "segments", AVT_ZONECENT);
          burgers_smd->SetEnumerationType(avtScalarMetaData::ByValue);
          for (int i=0; i<18; i++) {
          burgers_smd->AddEnumNameValue(BurgersTypeNames(btypes[i]), btypes[i]);
          }
          md->Add(burgers_smd);
    */ 
     
    AddScalarVarToMetaData(md, "Segment-Parent-MetaArm-ID", "segments", AVT_ZONECENT);
    AddScalarVarToMetaData(md, "Segment-Parent-Arm-ID", "segments", AVT_ZONECENT);
    AddScalarVarToMetaData(md, "Segment-Duplicates", "segments", AVT_ZONECENT);
       
    avtScalarMetaData *matype_smd =
      new avtScalarMetaData("Segment-Parent-MetaArm-Type", "segments", AVT_ZONECENT);
    matype_smd->SetEnumerationType(avtScalarMetaData::ByValue);
    for (int i=0; i<4; i++) {
      matype_smd->AddEnumNameValue(MetaArmTypeNames(i), i);
    }
    md->Add(matype_smd);    
    
    /*! 
      ==============================================
      Add a mesh for the meta-arms for dumpfile (serial) 
      ==============================================
    */
    meshname = "Meta Arms"; 
    topological_dimension = 1; /* lines */ 
    meshtype = AVT_UNSTRUCTURED_MESH; 
    AddMeshToMetaData(md, meshname, meshtype, mDumpfile.mExtents, nblocks, block_origin,
                      spatial_dimension, topological_dimension);
    
    AddMaterialToMetaData(md, "MetaArm-Type", "Meta Arms", 
                          mDumpfile.mMetaArmTypes.size(), 
                          mDumpfile.mMetaArmTypes); 
    AddScalarVarToMetaData(md, "MetaArm-ID", "Meta Arms", AVT_ZONECENT);

   /*! 
      ==============================================
      Now a special mesh for meta-arms that wraps its endpoints 
      instead of "taking a shortcut" across the data.  For dumpfile (serial)
      ==============================================
    */
    meshname = "Wrapped Meta Arms"; 
    topological_dimension = 1; /* lines */ 
    meshtype = AVT_UNSTRUCTURED_MESH; 
    AddMeshToMetaData(md, meshname, meshtype, mDumpfile.mExtents, nblocks, block_origin,
                      spatial_dimension, topological_dimension);

    AddMaterialToMetaData(md, "Wrapped-MetaArm-Type", "Wrapped Meta Arms", 
                          mDumpfile.mMetaArmTypes.size(), 
                          mDumpfile.mMetaArmTypes); 
    AddScalarVarToMetaData(md, "Wrapped-MetaArm-ID", "Wrapped Meta Arms", AVT_ZONECENT);

#endif // not PARALLEL
  } // end DumpFile format 
  else {
    EXCEPTION1(VisItException, "Cannot get paraDIS metadata from the file."); 
  }



    debug3 << " populateDatabaseMetaData complete" << endl; 
  return;

   
}

// ****************************************************************************
//  Method: avtparaDISFileFormat::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: rcook -- generated by xml2avt
//  Creation:   Tue Jan 30 14:56:34 PST 2007
//
// ****************************************************************************

vtkDataSet *
avtparaDISFileFormat::GetMesh(const char *meshname)
{  
   
  debug2 << "avtparaDISFileFormat 2.3.4::GetMesh("<<meshname<<") from file "<<mFilename<<endl;
  vtkDataSet *mesh = NULL; 
  
  if (mFormat == PARADIS_DUMPFILE_FORMAT) {
    mesh = mDumpfile.GetMesh(meshname); 
  }  else {
    mesh = mParallelData.GetMesh(meshname); 
  }
  if (!mesh) {
    EXCEPTION1(VisItException, "Could not get mesh requested"); 
  }
 return mesh;
}

// ****************************************************************************
//  Method: avtparaDISFileFormat::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: rcook -- generated by xml2avt
//  Creation:   Tue Jan 30 14:56:34 PST 2007
//
// ****************************************************************************

vtkDataArray *
avtparaDISFileFormat::GetVar(const char *varname) {
  vtkDataArray *array = NULL; 
  if (mFormat == PARADIS_DUMPFILE_FORMAT) {    
    array = mDumpfile.GetVar(varname); 
  }  else {
    array = mParallelData.GetVar(varname); 
  }
  if (!array) {
    EXCEPTION1(VisItException, "Could not get variable requested"); 
  }
  return array; 
}


// ****************************************************************************
//  Method: avtparaDISFileFormat::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: rcook -- generated by xml2avt
//  Creation:   Tue Jan 30 14:56:34 PST 2007
//
// ****************************************************************************

vtkDataArray *
avtparaDISFileFormat::GetVectorVar(const char *varname)
{
  return GetVar(varname); 

}


// NEED METHOD COMMENT

void *
avtparaDISFileFormat::GetAuxiliaryData(const char *var, const char *type,
                                       void *, DestructorFunction &df) {
  
  if (mFormat == PARADIS_DUMPFILE_FORMAT) {
    return mDumpfile.GetAuxiliaryData(var, type, df); 
  } // else {
  return mParallelData.GetAuxiliaryData(var, type, df); 
}

