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

// ************************************************************************* //
//                         avtOpenFOAMFileFormat.C                           //
// ************************************************************************* //

#include <avtOpenFOAMFileFormat.h>

#include <string>

#include <vtkCellData.h>
#include <vtkDataSet.h>
#include <vtkDoubleArray.h>
#include <vtkExecutive.h>
#include <vtkInformation.h>
#include <vtkMultiBlockDataSet.h>
#include <vtkPointData.h>
#include <vtkStreamingDemandDrivenPipeline.h>

#include <avtDatabaseMetaData.h>
#include <DBOptionsAttributes.h>
#include <DebugStream.h>
#include <InvalidVariableException.h>
#include <BadIndexException.h>
#include <maptypes.h>
#include <StringHelpers.h>
#include <FileFunctions.h>

using     std::string;
using     std::pair;
using     std::map;

#include <visit_vtkPOpenFOAMReader.h>


// ****************************************************************************
//  Method: avtOpenFOAMFileFormat constructor
//
//  Programmer: biagas2 -- generated by xml2avt
//  Creation:   Tue May 21 11:07:32 PDT 2013
//
//  Modifications:
//    Kathleen Biagas, Tue Aug 27 11:51:17 PDT 2013
//    Change how we read time information, to be more VTK compliant. Patch
//    provided by Takuya Oshima.
//
//    Kathleen Biagas, Tue Nov 19 08:16:51 PST 2013
//    Ensure we pass the 'controlDict' file to the vtk reader. Assumes it is
//    in the same location as the .foam file.
//
// ****************************************************************************

avtOpenFOAMFileFormat::avtOpenFOAMFileFormat(const char *filename, 
    DBOptionsAttributes *readOpts) : avtMTMDFileFormat(filename), timeSteps()
{
    convertCellToPoint = false;
    readZones = false;

    std::string controlDict(filename);
    std::string base = FileFunctions::Basename(filename);
    if (base != "controlDict")
    {
        std::string dir = FileFunctions::Dirname(filename);
        controlDict = dir + VISIT_SLASH_STRING + "controlDict";
    }
    reader = visit_vtkPOpenFOAMReader::New();
    reader->SetFileName(controlDict.c_str());
    reader->SetCreateCellToPoint(convertCellToPoint ? 1 : 0);
    reader->DecomposePolyhedraOn();

    // turn off all arrays until needed.
    reader->DisableAllCellArrays();
    reader->DisableAllPointArrays();
    reader->DisableAllLagrangianArrays();
    reader->DisableAllPatchArrays();

    if (readOpts) 
    {
        if (readOpts->FindIndex("Case Type") >= 0)
        {
            int index = readOpts->GetEnum("Case Type");
            reader->SetCaseType(index);
        }
        if (readOpts->FindIndex("Convert Cell Data To Point Data") >= 0)
        {
            convertCellToPoint = readOpts->GetBool("Convert Cell Data To Point Data");
            reader->SetCreateCellToPoint(convertCellToPoint);
        }
        if (readOpts->FindIndex("Read Zones") >= 0)
        {
            readZones = readOpts->GetBool("Read Zones");
        }
#if 0
        // this option is available in the reader, but I don't have sample
        // data to verify if this is necessary.
        if (readOpts->FindIndex("Positions in 1.3 Format") >= 0)
        {
            bool use13 = readOpts->GetBool("Positions in 1.3 Format");
            reader->SetPositionsIsIn13Format(use13);
        }
#endif
    }

    reader->UpdateInformation();
    vtkInformation *outInfo = reader->GetOutputInformation(0);
    const int nTimes
        = outInfo->Length(vtkStreamingDemandDrivenPipeline::TIME_STEPS());
    for (int i = 0; i < nTimes; ++i)
    {
        timeSteps.push_back(outInfo->Get(vtkStreamingDemandDrivenPipeline::TIME_STEPS(), i)); 
    }
    if (nTimes == 0)
        timeSteps.push_back(0);
}


// ****************************************************************************
//  Method: avtEMSTDFileFormat::GetNTimesteps
//
//  Purpose:
//      Tells the rest of the code how many timesteps there are in this file.
//
//  Programmer: biagas2 -- generated by xml2avt
//  Creation:   Tue May 21 11:07:32 PDT 2013
//
// ****************************************************************************

int
avtOpenFOAMFileFormat::GetNTimesteps(void)
{
    return (int)timeSteps.size();
}

// ****************************************************************************
//  Method: avtOpenFOAMFileFormat::GetTimes
//
//  Purpose:
//      Adds timesteps to times which provides the visible time stamp in Visit
//
// ****************************************************************************
void 
avtOpenFOAMFileFormat::GetTimes(std::vector<double> &times)
{
    times = timeSteps;
}

// ****************************************************************************
//  Method: avtOpenFOAMFileFormat::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: biagas2 -- generated by xml2avt
//  Creation:   Tue May 21 11:07:32 PDT 2013
//
// ****************************************************************************

void
avtOpenFOAMFileFormat::FreeUpResources(void)
{
}
// **************************************************************************** //  Method: avtOpenFOAMFileFormat::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: biagas2 -- generated by xml2avt
//  Creation:   Tue May 21 11:07:32 PDT 2013
//
//  Modifications:
//    Kathleen Biagas, Tue Aug 27 11:51:17 PDT 2013
//    Change how we read time information, to be more VTK compliant. Patch
//    provided by Takuya Oshima.
//
//    Kathleen Biagas, Mon Aug 15 14:09:55 PDT 2016
//    VTK-8, API for updating TimeStep changed.
// 
// ****************************************************************************

void
avtOpenFOAMFileFormat::PopulateDatabaseMetaData(avtDatabaseMetaData *md, 
    int timeState)
{
    reader->GetOutputInformation(0)->Set(
        vtkStreamingDemandDrivenPipeline::UPDATE_TIME_STEP(),
        timeSteps[timeState]);
    if (readZones)
    {
        // turn on ReadZones so we can get the names of the pointZones,
        // faceZones and cellZones if present.
        reader->SetReadZones(1);
    }
    reader->UpdateTimeStep(timeSteps[timeState]);

    stringVector lagrangianPatches;
    stringVector meshNames; // for non-lagrangian meshes
    StringStringVectorMap meshPatchMap;
    StringStringVectorMap regionPatchMap;

    for(int i = 0; i < reader->GetNumberOfPatchArrays(); ++i)
    {
        string fullPatchName(reader->GetPatchArrayName(i));
        size_t pos = fullPatchName.find("/");
        if (pos == string::npos)
        {
            // simplest case, no regions
            if (fullPatchName == "internalMesh")
            {
                avtMeshMetaData *mesh = new avtMeshMetaData;
                mesh->meshType = AVT_UNSTRUCTURED_MESH;
                mesh->hasSpatialExtents = false;
                mesh->topologicalDimension = 3;
                mesh->spatialDimension = 3;
                mesh->name = fullPatchName;
                mesh->blockTitle = "boundary";
                md->Add(mesh);
                meshNames.push_back(fullPatchName);
            }
            else
            {
                meshPatchMap["Patches"].push_back(fullPatchName);
            }
        }
        else if (fullPatchName.find("lagrangian") != string::npos)
        {
            lagrangianPatches.push_back(fullPatchName);
        }
        else 
        {
            // regions
            string region = fullPatchName.substr(0, pos);
            string mesh   = fullPatchName.substr(pos+1);
            if (mesh == "internalMesh")
            {
                avtMeshMetaData *mesh = new avtMeshMetaData;
                mesh->meshType = AVT_UNSTRUCTURED_MESH;
                mesh->hasSpatialExtents = false;
                mesh->topologicalDimension = 3;
                mesh->spatialDimension = 3;
                mesh->name = fullPatchName;
                mesh->blockTitle = "boundary";
                md->Add(mesh);
                meshNames.push_back(fullPatchName);
            }
            else
            {
                regionPatchMap[region].push_back(mesh);
            }
        }
    }
         
    StringStringVectorMap::const_iterator it;
    for (it = meshPatchMap.begin(); it != meshPatchMap.end(); ++it)
    {
        string root(it->first);

        avtMeshMetaData *mmd = new avtMeshMetaData;
        mmd->meshType = AVT_UNSTRUCTURED_MESH;
        mmd->hasSpatialExtents = false;
        mmd->topologicalDimension = 2;
        mmd->spatialDimension = 3;
        mmd->name = it->first;
        mmd->numBlocks = (int) it->second.size();
        mmd->blockTitle = "boundary";
        mmd->blockNames = it->second;
        md->Add(mmd);
        meshNames.push_back(it->first);
    }

    if (!regionPatchMap.empty())
    {
        avtMeshMetaData *regions = new avtMeshMetaData;
        regions->meshType = AVT_AMR_MESH;
        regions->hasSpatialExtents = false;
        regions->topologicalDimension = 3;
        regions->spatialDimension = 3;
        regions->name = "Regions";
        regions->groupTitle = "Region";
        regions->blockTitle = "boundary";
        intVector gIds;
        stringVector gNames;
        stringVector bNames;
        int count = 0;
        for (it = regionPatchMap.begin(); it != regionPatchMap.end(); ++it)
        {
            string gn(it->first);
            gNames.push_back(gn);
            stringVector n = it->second;
            for (size_t i = 0; i < n.size(); ++i)
            {
                gIds.push_back(count);
                bNames.push_back(gn + string(",") + n[i]);
            }
            ++count;
        }
        regions->groupIds   = gIds;
        regions->numGroups  = (int)gNames.size();
        regions->groupNames = gNames;
        regions->blockNames = bNames;
        regions->numBlocks  = (int)bNames.size();
        md->Add(regions);
        meshNames.push_back("Regions");
    }

    // point meshes, 'Clouds'.
    for (size_t i = 0;i < lagrangianPatches.size(); ++i)
    {
        avtMeshMetaData *pmesh = new avtMeshMetaData;
        pmesh->meshType = AVT_POINT_MESH;
        pmesh->hasSpatialExtents = false;
        pmesh->topologicalDimension = 1;
        pmesh->spatialDimension = 3;
        pmesh->name = lagrangianPatches[i];
        md->Add(pmesh);
    }

    if (readZones)
    {
        if (reader->GetNumberOfCellZones() > 0)
        {
            avtMeshMetaData *mmd = new avtMeshMetaData;
            mmd->meshType = AVT_UNSTRUCTURED_MESH;
            mmd->hasSpatialExtents = false;
            mmd->topologicalDimension = 3;
            mmd->spatialDimension = 3;
            mmd->name = "cellZones";
            mmd->blockTitle = "boundary";
            mmd->numBlocks = 0;
            for (int i = 0; i < reader->GetNumberOfCellZones();  ++i)
            {
              mmd->numBlocks++;
              mmd->blockNames.push_back(reader->GetCellZoneName(i));
            }
            md->Add(mmd);
            // don't add it to meshnames, because it has no vars
        }
        if (reader->GetNumberOfFaceZones() > 0)
        {
            avtMeshMetaData *mmd = new avtMeshMetaData;
            mmd->meshType = AVT_UNSTRUCTURED_MESH;
            mmd->hasSpatialExtents = false;
            mmd->topologicalDimension = 2;
            mmd->spatialDimension = 3;
            mmd->name = "faceZones";
            mmd->blockTitle = "boundary";
            mmd->numBlocks = 0;
            for (int i = 0; i < reader->GetNumberOfFaceZones();  ++i)
            {
              mmd->numBlocks++;
              mmd->blockNames.push_back(reader->GetFaceZoneName(i));
            }
            md->Add(mmd);
            // don't add it to meshnames, because it has no vars
        }
        if (reader->GetNumberOfPointZones() > 0)
        {
            avtMeshMetaData *mmd = new avtMeshMetaData;
            mmd->meshType = AVT_UNSTRUCTURED_MESH;
            mmd->hasSpatialExtents = false;
            mmd->topologicalDimension = 1;
            mmd->spatialDimension = 3;
            mmd->name = "pointZones";
            mmd->blockTitle = "boundary";
            mmd->numBlocks = 0;
            for (int i = 0; i < reader->GetNumberOfPointZones();  ++i)
            {
              mmd->numBlocks++;
              mmd->blockNames.push_back(reader->GetPointZoneName(i));
            }
            md->Add(mmd);
            // don't add it to meshnames, because it has no vars
        }
    }


    // Cell Arrays and PointArrays are associated with all PatchArrays
    // (except lagrangian), even if they don't truly exist on all patches.
    // I've been told this is expected behavior, but it would be nice if
    // the vtk reader could sort that out so we only present variables for
    // patches that truly have them.
  
    for (int i = 0; i < reader->GetNumberOfCellArrays(); ++i)
    {
         const char *name(reader->GetCellArrayName(i));
         vtkStdString cn = reader->GetCellArrayClassName(name);
         avtVarType varType = OpenFOAMClassNameToVarType(cn);
         avtCentering centering = convertCellToPoint?AVT_NODECENT:AVT_ZONECENT;
         for (size_t j = 0; j < meshNames.size(); ++j)
             AddVarToMetaData(varType, md, name, meshNames[j], centering);
    }

    for (int i = 0; i < reader->GetNumberOfPointArrays(); ++i)
    {
         const char *name = reader->GetPointArrayName(i);
         vtkStdString cn = reader->GetPointArrayClassName(name);
         avtVarType varType = OpenFOAMClassNameToVarType(cn);
         for (size_t j = 0; j < meshNames.size(); ++j)
             AddVarToMetaData(varType, md, name, meshNames[j], AVT_NODECENT);
    }

    for (int i = 0; i < reader->GetNumberOfLagrangianArrays(); ++i)
    {
         const char *name = reader->GetLagrangianArrayName(i);
         vtkStdString cn = reader->GetLagrangianArrayClassName(name);
         avtVarType varType = OpenFOAMClassNameToVarType(cn);
         for (size_t z = 0; z < lagrangianPatches.size(); ++z)
             AddVarToMetaData(varType, md, name, lagrangianPatches[z], 
                              AVT_NODECENT);
    }
    if (readZones)
    {
        // turn off ReadZones until we actually want to see them.
        reader->SetReadZones(0);
    }
}

// ****************************************************************************
//  Method: avtOpenFOAMFileFormat::GetBlock
//
//  Purpose:
//      Traverses the vtkMultiBlockDataSet structure for the block that
//      matches the current patch name.  Most of the time, there should
//      only be 1 vtkDataSet in mbds, due to the way we set the status
//      for patch arrays, but in the case of 'ReadZones' we will get the
//      full mbds, so we need to find only the current patch.
//
//  Notes:
//
//  Arguments:
//     mbds       The vtkMultiBlockDataSet to traverse.
//     matchMesh  Whether we should match mesh name before recursing with
//                a vtkMultiBlockDataSet.
//
//  Programmer: Kathleen Biagas
//  Creation:   Thu May 30 15:18:41 MST 2013
//
//  Modifications:
//
// ****************************************************************************

vtkDataSet *
avtOpenFOAMFileFormat::GetBlock(vtkMultiBlockDataSet *mbds, bool matchMesh)
{
    if (mbds == NULL)
    {
        return NULL;
    }
    vtkDataSet *rv = NULL;
    int nBlocks = mbds->GetNumberOfBlocks();
    for (int i = 0; i < nBlocks && rv == NULL; ++i)
    {
        vtkDataObject *block = mbds->GetBlock(i);
        if (block != NULL)
        {
            string blockName(mbds->GetMetaData(i)->Get(
                            vtkCompositeDataSet::NAME())); 
            if (block->GetDataObjectType() == VTK_MULTIBLOCK_DATA_SET)
            {
                if (matchMesh)
                {
                    // We don't want to recurse on 'faceZones' when we
                    // are trying to plot 'cellZones', and vice-versa.
                    if (blockName == currentMesh || 
                        blockName == currentPatch || 
                       (readZones && blockName == "Zones"))
                        rv = GetBlock(vtkMultiBlockDataSet::SafeDownCast(block),
                                      matchMesh);
                }
                else
                    rv = GetBlock(vtkMultiBlockDataSet::SafeDownCast(block),
                                  matchMesh);
            }
            else
            {
                if (blockName == currentPatch)
                    rv = vtkDataSet::SafeDownCast(block);
            }
        }
    } 
    return rv;
}


// ****************************************************************************
//  Method: avtOpenFOAMFileFormat::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.
//      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: biagas2 -- generated by xml2avt
//  Creation:   Tue May 21 11:07:32 PDT 2013
//
// ****************************************************************************

vtkDataSet *
avtOpenFOAMFileFormat::GetMesh(int timestate, int domain, const char *meshname)
{
    string mname(meshname);
    SelectPatchArray(domain, mname);

    if (readZones && (mname == "cellZones" ||
                      mname == "faceZones" ||
                      mname == "pointZones"))
    {
        reader->SetReadZones(1);
    }

    reader->Update();

    if (reader->GetOutput() == NULL)
    {
        debug1 << "avtOpenFOAMFileFormat::GetMesh, Error reading Patch "
               << reader->GetPatchArrayName(domain) << "." 
               << "  Reader's Output is NULL." << endl;
        return NULL;
    }

    bool matchMesh = (mname == "cellZones" || 
                      mname == "faceZones" || 
                      mname == "pointZones");

    vtkDataSet *block = GetBlock(reader->GetOutput(), matchMesh);

    if (block == NULL)
    {
        debug1 << "avtOpenFOAMFileFormat::GetMesh, Error reading Patch "
               << reader->GetPatchArrayName(domain) << "." 
               << "  GetBlock returned NULL." << endl;
        return NULL;
    }

    vtkDataSet *rv = block->NewInstance();
    if(rv != NULL)
        rv->ShallowCopy(block);
    reader->SetReadZones(0);
    return rv;
}


// ****************************************************************************
//  Method: avtOpenFOAMFileFormat::ReadVar
//
//  Purpose:
//      Gets a variable associated with this file.
//
//  Arguments:
//      timestate  The index of the timestate.  If GetNTimesteps returned
//                 'N' time steps, this is guaranteed to be between 0 and N-1.
//      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: Kathleen Biagas
//  Creation:   May 22, 2013
//
//  Modifications:
//
// ****************************************************************************

vtkDataArray *
avtOpenFOAMFileFormat::ReadVar(int timestate, int domain, const char *var)
{
    // Var may have been constructed as 'meshname/varname', so deconstruct
    // to get at the var name the reader is expecting.
    string varname(var);
    size_t pos = varname.rfind('/');
    if (pos != string::npos)
    {
        // we have a meshname/var compound var name, extract the mesh and var:
        string mesh(varname.substr(0, pos));
        varname = varname.substr(pos+1);
        // now we need to make sure this mesh is the currently selected Patch
        SelectPatchArray(domain, mesh);
    }

    bool cellData = true;

    // we always disable all, because checking the 'status' doesn't seem
    // to help
    reader->DisableAllCellArrays();
    reader->DisableAllPointArrays();

    if (reader->GetCellArrayExists(varname.c_str()))
    {
        reader->SetCellArrayStatus(varname.c_str(), 1);
        if (convertCellToPoint)
            cellData = false;
    }
    else if (reader->GetPointArrayExists(varname.c_str()))
    {
        reader->SetPointArrayStatus(varname.c_str(), 1);
        cellData = false;
    }
    else if (reader->GetLagrangianArrayExists(varname.c_str()))
    {
        reader->SetLagrangianArrayStatus(varname.c_str(), 1);
    }
    else
    {
        EXCEPTION1(InvalidVariableException, varname);
    }

    reader->Update();

    bool matchMesh = (currentMesh == "cellZones" || 
                      currentMesh == "faceZones" ||
                      currentMesh == "pointZones");
    vtkDataSet *block = GetBlock(reader->GetOutput(), matchMesh);
  
    if (block == NULL)
    {
        debug1 << "avtOpenFoamFileFormat::ReadVar:  could not create "
               <<  "vtkDataSet from reader's output" << endl;
        return NULL;
    }

    vtkDataArray *rv = NULL;
    if (cellData)
    {
        rv = block->GetCellData()->GetArray(varname.c_str());
    }
    else 
    {
        rv = block->GetPointData()->GetArray(varname.c_str());
    }
    if (rv == NULL)
    {
        debug1 << "avtOpenFOAMFileFormat::ReadVar: " << var
               << " should exist but reader could not retrieve it." << endl;
        EXCEPTION1(InvalidVariableException, varname);
    }
    rv->Register(NULL);
    return rv;
}


// ****************************************************************************
//  Method: avtOpenFOAMFileFormat::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.
//      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: biagas2 -- generated by xml2avt
//  Creation:   Tue May 21 11:07:32 PDT 2013
//
// ****************************************************************************

vtkDataArray *
avtOpenFOAMFileFormat::GetVar(int timestate, int domain, const char *varname)
{
    return ReadVar(timestate, domain, varname);
}


// ****************************************************************************
//  Method: avtOpenFOAMFileFormat::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.
//      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: biagas2 -- generated by xml2avt
//  Creation:   Tue May 21 11:07:32 PDT 2013
//
// ****************************************************************************

vtkDataArray *
avtOpenFOAMFileFormat::GetVectorVar(int timestate, int domain,
    const char *varname)
{
    return ReadVar(timestate, domain, varname);
}


// ****************************************************************************
//  Method: avtOpenFOAMFileFormat::ActivateTimestep
//
//  Purpose:
//
//  Arguments:
//    timestate The index of the timestate.  
//
//  Programmer: Kathleen Biagas 
//  Creation:   May 21, 2013
//
//  Modifications:
//    Kathleen Biagas, Tue Aug 27 11:51:17 PDT 2013
//    Change how we read time information, to be more VTK compliant. Patch
//    provided by Takuya Oshima.
//
//    Kathleen Biagas, Mon Aug 15 14:09:55 PDT 2016
//    VTK-8, API for updating TimeStep changed.
//
// ****************************************************************************

void
avtOpenFOAMFileFormat::ActivateTimestep(int timestate)
{
    if (timestate < 0 || timestate > (int)timeSteps.size())
    {
        EXCEPTION2(BadIndexException, timestate, (int) timeSteps.size());
    }
    if (timestate != currentTimeStep)
    {
        reader->GetOutputInformation(0)->Set(
            vtkStreamingDemandDrivenPipeline::UPDATE_TIME_STEP(),
            timeSteps[timestate]);
        currentTimeStep = timestate;
    }
}

// ****************************************************************************
//  Method: avtOpenFOAMFileFormat::OpenFOAMClassNameToVarType
//
//  Purpose: Converts the OpenFOAM class name to an avtVarType,
//
//  Arguments:
//    cn        The OpenFOAM class name.
//
//
//  Programmer: Kathleen Biagas
//  Creation:   May 21, 2013
//
// ****************************************************************************

avtVarType 
avtOpenFOAMFileFormat::OpenFOAMClassNameToVarType(const vtkStdString &cn)
{
    if (cn.find("scalar") != string::npos)
        return AVT_SCALAR_VAR;
    if (cn.find("Scalar") != string::npos)
        return AVT_SCALAR_VAR;
    if (cn.find("vector") != string::npos)
        return AVT_VECTOR_VAR;
    if (cn.find("Vector") != string::npos)
        return AVT_VECTOR_VAR;
    if (cn.find("symmTensor") != string::npos)
        return AVT_SYMMETRIC_TENSOR_VAR;
    if (cn.find("SymmTensor") != string::npos)
        return AVT_SYMMETRIC_TENSOR_VAR;
    if (cn.find("tensor") != string::npos)
        return AVT_TENSOR_VAR;
    if (cn.find("Tensor") != string::npos)
        return AVT_TENSOR_VAR;
    if (cn.find("label") != string::npos)
        return AVT_SCALAR_VAR;
    if (cn.find("Label") != string::npos)
        return AVT_SCALAR_VAR;
    debug3 << "Encountered unhandled OpenFOAM class name: " << cn << endl;
    return AVT_UNKNOWN_TYPE;
}

// ****************************************************************************
//  Method: avtOpenFOAMFileFormat::AddVarToMetaData
//
//  Purpose: Convenience method to add a new variable's meta data.
//
//  Arguments:
//    varType   The variable type.
//    md        MetaData where the varible should be stored
//    varName   The name of the variable.
//    meshName  The name of the mesh on which the variable lives.
//    centering How the variable is centered. 
//
//  Programmer: Kathleen Biagas
//  Creation:   May 21, 2013
//
// ****************************************************************************

void
avtOpenFOAMFileFormat::AddVarToMetaData(avtVarType varType,
    avtDatabaseMetaData *md, string varName, string meshName,
    avtCentering centering)
{
    string newVarName(meshName + string("/") + varName);
    switch(varType)
    {
        case AVT_SCALAR_VAR:
             AddScalarVarToMetaData(md, newVarName, meshName, centering);
             break;
        case AVT_VECTOR_VAR:
             AddVectorVarToMetaData(md, newVarName, meshName, centering);
             break;
        case AVT_TENSOR_VAR:
             AddTensorVarToMetaData(md, newVarName, meshName, centering);
             break;
        case AVT_SYMMETRIC_TENSOR_VAR:
             AddSymmetricTensorVarToMetaData(md, newVarName, meshName, centering);
             break;
        default:
             debug3 << "OpenFoam encountered unsupported var type: " 
                    << avtVarTypeToString(varType) << endl;
             break;
    }
}


// ****************************************************************************
//  Method: avtOpenFOAMFileFormat::SelectPatchArray
//
//  Purpose: Chooses the OpenFOAM patch array that should be enabled.
//      
//  Notes:   We disable all patch arrays except the patch we want, which we
//           discover from the meshName and blockname that corresponds
//           to the patch id ( if present).  Disabling/Enabling patch arrays
//           in this manner allows the reader to only fetch the patch we
//           are currently interested in.
//
//  Arguments:
//    domain    The patch id.
//    meshName  The name of the mesh.
//
//  Programmer: Kathleen Biagas
//  Creation:   May 21, 2013
//
// ****************************************************************************

void
avtOpenFOAMFileFormat::SelectPatchArray(int domain, const string &meshName)
{
    // Keep track of currentMesh and currentPatch to ensure extraction of
    // the correct block from the vtkMultiBlockDataSet later.
    reader->DisableAllPatchArrays();
    reader->DisableAllLagrangianArrays();
    currentMesh = meshName;
    if ((meshName == "internalMesh")) //|| (meshName.find("lagrangian") != string::npos))
    {
        reader->SetPatchArrayStatus(meshName.c_str(), 1);
        currentPatch = meshName;
    }
    else 
    {
        if (metadata != NULL)
        {
            const avtMeshMetaData *mmd = metadata->GetMesh(meshName.c_str());
            string mname(meshName);
            size_t pos = mname.find("/");
            if (pos != string::npos)
            {
                string region(mname.substr(0, pos));
                string patch(mname.substr(pos+1));
                if (region == "Patches")
                {
                    mname = mmd->blockNames[domain];
                    currentPatch =  mname;
                }
                else if (patch == "Patches")
                {
                    mname = region + string("/") + mmd->blockNames[domain];
                    currentPatch =  mname;
                }
                else
                {
                    currentPatch =  patch;
                }
            }
            else  if (mname == "Patches" || mname == "cellZones" ||
                      mname == "faceZones" || mname == "pointZones" )
            {
                mname = mmd->blockNames[domain];
                currentPatch =  mname;
            }
            else  if (mname == "Regions")
            {
                string region(mmd->groupNames[mmd->groupIds[domain]]);
                // now parse out the groupName from the blockName:
                string patch = mmd->blockNames[domain].substr(region.size()+1);
                currentPatch =  patch;
                mname = region + string("/") + patch;
            }
            reader->SetPatchArrayStatus(mname.c_str(), 1);
        }
        else
            EXCEPTION1(InvalidVariableException, meshName.c_str());
    }
}
