text: /*=========================================================================
  
    Program:   Visualization Toolkit
    Module:    vtkPMultiResolutionGenericIOReader.cxx
  
    Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
    All rights reserved.
    See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
  
       This software is distributed WITHOUT ANY WARRANTY; without even
       the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
       PURPOSE.  See the above copyright notice for more information.
  
  =========================================================================*/
  #include "vtkPMultiResolutionGenericIOReader.h"
  
  #include "vtkCallbackCommand.h"
  #include "vtkCompositeDataPipeline.h"
  #include "vtkDataArraySelection.h"
  #include "vtkFileSeriesReader.h"
  #include "vtkInformation.h"
  #include "vtkInformationVector.h"
  #include "vtkMultiBlockDataSet.h"
  #include "vtkNew.h"
  #include "vtkObjectFactory.h"
  #include "vtkPGenericIOMultiBlockReader.h"
  #include "vtkSmartPointer.h"
  #include "vtkStringArray.h"
  #include "vtkUnstructuredGrid.h"
  
  #include "vtk_jsoncpp.h"
  
  #include <algorithm>
  #include <cassert>
  #include <map>
  #include <set>
  #include <string>
  #include <sstream>
  
  struct resolution_t
  {
    std::string FileName;
    vtkSmartPointer< vtkFileSeriesReader > Reader;
  };
  
  class vtkPMultiResolutionGenericIOReader::vtkInternal
  {
  public:
    std::vector< resolution_t > Resolutions;
    int NumberOfBlocksPerLevel;
    Json::Reader JsonReader;
  
    void AddLevel(int level)
      {
      resolution_t newLevel;
      newLevel.Reader = vtkSmartPointer< vtkFileSeriesReader >::New();
      vtkNew< vtkPGenericIOMultiBlockReader > internalReader;
      newLevel.Reader->SetReader(internalReader.GetPointer());
      newLevel.Reader->SetFileNameMethod("SetFileName");
      this->Resolutions.insert(this->Resolutions.begin() + level, newLevel);
      }
  #define JSON_READ_ERROR() std::cerr << "Error reading in JSON" << std::endl; \
        return false
  
    vtkPGenericIOMultiBlockReader* GetReaderForLevel(int level)
      {
      return vtkPGenericIOMultiBlockReader::SafeDownCast(
        this->Resolutions[level].Reader->GetReader());
      }
  
    void Clear()
      {
      this->Resolutions.clear();
      this->NumberOfBlocksPerLevel = -1;
      }
  
    bool ParseJson(const std::string& parentDir, std::istream& jsonIn)
      {
      Json::Value root;
      if (!JsonReader.parse(jsonIn,root))
        {
        JSON_READ_ERROR();
        }
      if (!root.isMember("levels"))
        {
        JSON_READ_ERROR();
        }
      const Json::Value& levelsArray = root.get("levels",Json::Value::null);
      if (!levelsArray.isArray())
        {
        JSON_READ_ERROR();
        }
      for (Json::Value::ArrayIndex i = 0; i < levelsArray.size(); ++i)
        {
        const Json::Value& level = levelsArray[i];
        if (!level.isMember("timesteps"))
          {
          JSON_READ_ERROR();
          }
        const Json::Value& timestepsArray = level.get("timesteps",Json::Value::null);
        if (!timestepsArray.isArray())
          {
          JSON_READ_ERROR();
          }
        this->AddLevel(i);
        std::map< double, std::string > timeFiles;
        for (Json::Value::ArrayIndex j = 0; j < timestepsArray.size(); ++j)
          {
          const Json::Value& timestep = timestepsArray[j];
          if (!(timestep.isMember("time") && timestep.isMember("file")))
            {
            JSON_READ_ERROR();
            }
          const Json::Value& time = timestep.get("time",Json::Value::null);
          const Json::Value& file = timestep.get("file",Json::Value::null);
          if (!time.isNumeric())
            {
            JSON_READ_ERROR();
            }
          double t = time.asDouble();
          std::string f = file.asString();
          // allow paths relative to the config file
          if (f[0] != '/')
            {
            f = parentDir + f;
            }
          timeFiles.insert(std::pair< double, std::string >(t,f));
          }
        for (std::map< double, std::string >::iterator itr = timeFiles.begin();
          itr != timeFiles.end(); ++itr)
          {
          // vtkFileSeriesReader ignores time on inputs unless they already have
          // a time range, but iteration over the map ensures they are in order...
          // Eventually we may need to take into account the time in the JSON,
          // which is why it is there.
          this->Resolutions[i].Reader->AddFileName(itr->second.c_str());
          }
        }
      return true;
      }
  };
  
  vtkStandardNewMacro(vtkPMultiResolutionGenericIOReader);
  //----------------------------------------------------------------------------
  vtkPMultiResolutionGenericIOReader::vtkPMultiResolutionGenericIOReader()
  {
    this->Internal = new vtkInternal();
    this->FileName = NULL;
    this->Internal->NumberOfBlocksPerLevel = -1;
    this->SetNumberOfInputPorts(0);
    this->SetNumberOfOutputPorts(1);
  
    this->XAxisVariableName = NULL;
    this->YAxisVariableName = NULL;
    this->ZAxisVariableName = NULL;
  
    this->SetXAxisVariableName("x");
    this->SetYAxisVariableName("y");
    this->SetZAxisVariableName("z");
  
    this->PointDataArraySelection = vtkDataArraySelection::New();
    this->SelectionObserver = vtkCallbackCommand::New();
    this->SelectionObserver->SetCallback(
      &vtkPMultiResolutionGenericIOReader::SelectionModifiedCallback);
    this->SelectionObserver->SetClientData(this);
    this->PointDataArraySelection->AddObserver(
      vtkCommand::ModifiedEvent,this->SelectionObserver);
  }
  
  //----------------------------------------------------------------------------
  vtkPMultiResolutionGenericIOReader::~vtkPMultiResolutionGenericIOReader()
  {
    this->SetFileName(NULL);
    delete this->Internal;
    this->PointDataArraySelection->RemoveObserver(this->SelectionObserver);
    this->SelectionObserver->Delete();
    this->PointDataArraySelection->Delete();
  }
  
  //----------------------------------------------------------------------------
  void vtkPMultiResolutionGenericIOReader::PrintSelf(ostream& os, vtkIndent indent)
  {
    this->Superclass::PrintSelf(os, indent);
  }
  
  //----------------------------------------------------------------------------
  bool vtkPMultiResolutionGenericIOReader::CanReadFile(const char* fName)
  {
    std::string filename(fName);
    std::string::size_type it = filename.find_last_of(".");
    std::string extension(filename, it+1, filename.length());
    return extension == "gios";
  }
  
  //----------------------------------------------------------------------------
  static inline bool SetStringProperty(char* & destStr, const char* newStr)
  {
    if (destStr == NULL && newStr == NULL) return false;
    if (destStr && newStr && strcmp(destStr,newStr) == 0) return false;
    delete [] destStr;
    if (newStr)
      {
      size_t n = strlen(newStr) +1;
      char* cp1 = new char[n];
      const char* cp2 = newStr;
      destStr = cp1;
      do { *cp1++ = *cp2++; } while (--n);
      }
    else
      {
      destStr = NULL;
      }
    return true;
  }
  
  //----------------------------------------------------------------------------
  void vtkPMultiResolutionGenericIOReader::SetFileName(const char* fname)
  {
    this->Internal->Clear();
    if (!SetStringProperty(this->FileName,fname)) return;
    if (fname)
      {
      std::ifstream fis(this->FileName);
      std::string filename(this->FileName);
      std::string::size_type idx = filename.find_last_of("/");
      std::string parentDir(filename,0,idx+1);
      if (!this->Internal->ParseJson(parentDir,fis))
        {
        // clear any partially read data if reading failed for some reason
        this->Internal->Resolutions.clear();
        }
      else
        {
        // sync selected data arrays
        this->PointDataArraySelection->CopySelections(
          this->Internal->GetReaderForLevel(0)->GetPointDataArraySelection());
        // sync x,y,z axes
        for (int i = 0; i < this->GetNumberOfLevels(); ++i)
          {
          vtkPGenericIOMultiBlockReader* r = this->Internal->GetReaderForLevel(i);
          r->SetXAxisVariableName(this->XAxisVariableName);
          r->SetYAxisVariableName(this->YAxisVariableName);
          r->SetZAxisVariableName(this->ZAxisVariableName);
          }
        }
      }
    this->Modified();
  }
  
  //----------------------------------------------------------------------------
  void vtkPMultiResolutionGenericIOReader::SetXAxisVariableName(const char* arg)
  {
    if (SetStringProperty(this->XAxisVariableName,arg))
      {
      this->Modified();
      for (unsigned i = 0; i < this->Internal->Resolutions.size(); ++i)
        {
        this->Internal->GetReaderForLevel(i)->SetXAxisVariableName(arg);
        }
      }
  }
  
  //----------------------------------------------------------------------------
  void vtkPMultiResolutionGenericIOReader::SetYAxisVariableName(const char* arg)
  {
    if (SetStringProperty(this->YAxisVariableName,arg))
      {
      this->Modified();
      for (unsigned i = 0; i < this->Internal->Resolutions.size(); ++i)
        {
        this->Internal->GetReaderForLevel(i)->SetYAxisVariableName(arg);
        }
      }
  }
  
  //----------------------------------------------------------------------------
  void vtkPMultiResolutionGenericIOReader::SetZAxisVariableName(const char* arg)
  {
    if (SetStringProperty(this->ZAxisVariableName,arg))
      {
      this->Modified();
      for (unsigned i = 0; i < this->Internal->Resolutions.size(); ++i)
        {
        this->Internal->GetReaderForLevel(i)->SetZAxisVariableName(arg);
        }
      }
  }
  
  //----------------------------------------------------------------------------
  vtkStringArray* vtkPMultiResolutionGenericIOReader::GetArrayList()
  {
    if (this->GetNumberOfLevels() > 0)
      {
      return this->Internal->GetReaderForLevel(0)->GetArrayList();
      }
    else
      {
      return NULL;
      }
  }
  
  //----------------------------------------------------------------------------
  bool vtkPMultiResolutionGenericIOReader::InsertLevel(const char* fileName, int level)
  {
    if (fileName == NULL)
      {
      return false;
      }
    assert(this->Internal != NULL);
    if (level < 0 || static_cast<unsigned>(level) > this->Internal->Resolutions.size())
      {
      vtkErrorMacro(<< "Level is out of range.");
      return false;
      }
    //this->Internal->AddLevel(level,fileName);
  
    if (this->GetNumberOfLevels() != 0)
      {
      this->Internal->GetReaderForLevel(level)->GetPointDataArraySelection()
        ->CopySelections(this->PointDataArraySelection);
      }
    else
      {
      this->PointDataArraySelection->CopySelections(
        this->Internal->GetReaderForLevel(level)->GetPointDataArraySelection());
      }
    this->Modified();
    return true;
  }
  
  //----------------------------------------------------------------------------
  int vtkPMultiResolutionGenericIOReader::GetNumberOfLevels() const
  {
    assert(this->Internal != NULL);
    return this->Internal->Resolutions.size();
  }
  
  //----------------------------------------------------------------------------
  const char* vtkPMultiResolutionGenericIOReader::GetFileNameForLevel(int level) const
  {
    assert(this->Internal != NULL);
    if (level < 0 || static_cast<unsigned>(level) >= this->Internal->Resolutions.size())
      {
      return (const char*) NULL;
      }
    return this->Internal->Resolutions[level].FileName.c_str();
  }
  
  //----------------------------------------------------------------------------
  void vtkPMultiResolutionGenericIOReader::RemoveAllLevels()
  {
    this->Internal->Resolutions.clear();
    this->Modified();
  }
  
  //------------------------------------------------------------------------------
  void vtkPMultiResolutionGenericIOReader::SelectionModifiedCallback(
    vtkObject* vtkNotUsed(caller), unsigned long vtkNotUsed(eid), void* clientdata, void* vtkNotUsed(calldata))
  {
    assert(clientdata != NULL);
    vtkPMultiResolutionGenericIOReader* reader =
      static_cast<vtkPMultiResolutionGenericIOReader*>(clientdata);
    for (int i = 0; i < reader->GetNumberOfLevels(); ++i)
      {
      reader->Internal->GetReaderForLevel(i)->GetPointDataArraySelection()
        ->CopySelections(reader->PointDataArraySelection);
      }
    reader->Modified();
  }
  
  //------------------------------------------------------------------------------
  int vtkPMultiResolutionGenericIOReader::GetNumberOfPointArrays()
  {
    return this->PointDataArraySelection->GetNumberOfArrays();
  }
  
  //------------------------------------------------------------------------------
  const char* vtkPMultiResolutionGenericIOReader::GetPointArrayName(int i)
  {
    assert("pre: array index is out-of-bounds!" &&
      (i >= 0) && (i < this->GetNumberOfPointArrays()));
    return this->PointDataArraySelection->GetArrayName(i);
  }
  
  //------------------------------------------------------------------------------
  int vtkPMultiResolutionGenericIOReader::GetPointArrayStatus(const char* name)
  {
    assert(this->PointDataArraySelection->ArrayExists(name));
    return this->PointDataArraySelection->ArrayIsEnabled(name);
  }
  
  //------------------------------------------------------------------------------
  void vtkPMultiResolutionGenericIOReader::SetPointArrayStatus(
    const char* name, int status)
  {
    if (!this->PointDataArraySelection->ArrayExists(name))
      {
      assert(this->Internal->Resolutions.size() > 1);
      this->PointDataArraySelection->CopySelections(
        this->Internal->GetReaderForLevel(0)->GetPointDataArraySelection());
      vtkPMultiResolutionGenericIOReader::SelectionModifiedCallback(NULL,0l,(void*)this,NULL);
      assert(this->PointDataArraySelection->ArrayExists(name));
      }
    if (status)
      {
      this->PointDataArraySelection->EnableArray(name);
      }
    else
      {
      this->PointDataArraySelection->DisableArray(name);
      }
    for (int i = 0; i < this->GetNumberOfLevels(); ++i)
      {
      if (this->Internal->GetReaderForLevel(i)->GetArrayList()->LookupValue(name) >=0)
        {
        this->Internal->GetReaderForLevel(i)->SetPointArrayStatus(name,status);
        }
      }
  }
  
  void printBoundsOfBlockInfo(vtkMultiBlockDataSet* ds,int offset)
  {
    for (unsigned i = 0; i < ds->GetNumberOfBlocks(); ++i)
      {
      vtkInformation* info = ds->GetMetaData(i);
      std::cerr << "Bounds for block " << (i + offset) << ": ";
      if (info->Has(vtkCompositeDataPipeline::BOUNDS()))
        {
        double bb[6];
        info->Get(vtkCompositeDataPipeline::BOUNDS(),bb);
        std::cerr << "[ ";
        for (int j = 0; j < 6; ++j)
          {
          std::cerr << bb[j] << ", ";
          }
        std::cerr << "]" << std::endl;
        }
      }
  }
  
  //----------------------------------------------------------------------------
  int vtkPMultiResolutionGenericIOReader::RequestInformation(vtkInformation *request,
          vtkInformationVector** inputVector, vtkInformationVector* outputVector)
  {
    // call the superclass's method
    this->Superclass::RequestInformation(request,inputVector,outputVector);
  
    // get the output info object
    vtkInformation* outInfo = outputVector->GetInformationObject(0);
    assert(outInfo != NULL);
  
    // this filter does handle streaming pieces of the dataset
    outInfo->Set(CAN_HANDLE_PIECE_REQUEST(),1);
  
    // create a dummy dataset that will have the right structure
    vtkSmartPointer< vtkMultiBlockDataSet > infoSet =
      vtkSmartPointer< vtkMultiBlockDataSet >::New();
    infoSet->SetNumberOfBlocks(this->GetNumberOfLevels());
    this->Internal->NumberOfBlocksPerLevel = -1;
  
  
    // request the information from each internal reader and put them
    // into the information object for this reader as blocks
    for (unsigned i = 0; i < this->Internal->Resolutions.size(); ++i)
      {
      vtkMultiBlockDataSet* dataSet;
      this->Internal->Resolutions[i].Reader->ProcessRequest(
        request,inputVector,outputVector);
      if (i == 0)
        {
        this->PointDataArraySelection->CopySelections(
          this->Internal->GetReaderForLevel(0)->GetPointDataArraySelection());
        }
      dataSet = vtkMultiBlockDataSet::SafeDownCast(
        outInfo->Get(
          vtkCompositeDataPipeline::COMPOSITE_DATA_META_DATA()));
      // also make sure that they have the same number of blocks
      // (important for this kind of data)
      if (this->Internal->NumberOfBlocksPerLevel == -1)
        {
        this->Internal->NumberOfBlocksPerLevel =
          dataSet->GetNumberOfBlocks();
        }
      else if ((unsigned)(this->Internal->NumberOfBlocksPerLevel) !=
          dataSet->GetNumberOfBlocks())
        {
        vtkErrorMacro(<< "Wrong number of blocks in level " << i);
        return 0;
        }
  
  //    printBoundsOfBlockInfo(dataSet,i * this->Internal->NumberOfBlocksPerLevel);
      infoSet->SetBlock(i,dataSet);
      outInfo->Remove(
        vtkCompositeDataPipeline::COMPOSITE_DATA_META_DATA());
      }
    // We assume all datasets have blocks with the same bounds.  The rest of the pipeline
    // does not know this, so this loop first finds which resolution has bounds and copies
    // those bounds to all the other resolutions for each block
    for (int i = 0; i < this->Internal->NumberOfBlocksPerLevel; ++i) // for each block
      {
      double bounds[6] = {0,0,0,0,0,0};
      // find the resolution with nonzero bounds (zero is default from GenericIO reader
      for (unsigned j = 0; j < this->Internal->Resolutions.size(); ++j)
        {
        if (bounds[0] == 0 && bounds[1] == 0)
          {
          static_cast<vtkMultiBlockDataSet*>(
                infoSet->GetBlock(j))
              ->GetMetaData(i)->Get(vtkStreamingDemandDrivenPipeline::BOUNDS(),bounds);
          }
        }
      // copy bounds to all resolutions
      for (unsigned j = 0; j < this->Internal->Resolutions.size(); ++j)
        {
        static_cast<vtkMultiBlockDataSet*>(
              infoSet->GetBlock(j))
            ->GetMetaData(i)->Set(vtkStreamingDemandDrivenPipeline::BOUNDS(),bounds,6);
        }
      }
  
    std::cerr.flush();
    // return the computed object as the composite data metadata
    outInfo->Set(vtkCompositeDataPipeline::COMPOSITE_DATA_META_DATA(),
      infoSet.GetPointer());
    return 1;
  }
  
  //----------------------------------------------------------------------------
  // This function is a binary search that returns the index of where the item
  // should be inserted if it is not already present.  More precisely, it
  // returns the minimum index i such that vec[i] <= value && value < vec[i+1].
  // If the item is greater than every item in the array, it returns the size of
  // the array and if the item is less than every item in the array it returns 0.
  // NOTE: as per binary search, vec should be a sorted array of ints
  int binSearch(int* vec,int size, int value)
  {
    // handle boundary conditions
    if (vec[0] > value)
      {
      return 0;
      }
    if (vec[size-1] < value)
      {
      return size;
      }
    // loop invariants:
    // vec[min] is always <= value
    // vec[max] is alwasy > value
    int min = 0, max = size;
    while (min < max -1)
      {
      int mid = (min+max)/2;
      if (vec[mid] <= value)
        {
        min = mid;
        }
      else
        {
        max = mid;
        }
      }
    return min;
  }
  
  int vtkPMultiResolutionGenericIOReader::RequestUpdateExtent(vtkInformation* request,
                                                              vtkInformationVector** inputVector,
                                                              vtkInformationVector* outputVector)
  {
    // The vtkFileSeriesReader needs this to be called to update its timestep
    for (unsigned i = 0; i < this->Internal->Resolutions.size(); ++i)
      {
      this->Internal->Resolutions[i].Reader->ProcessRequest(request,inputVector,outputVector);
      this->Internal->GetReaderForLevel(i)->GetPointDataArraySelection()
        ->CopySelections(this->PointDataArraySelection);
      }
    return 1;
  }
  
  //----------------------------------------------------------------------------
  int vtkPMultiResolutionGenericIOReader::RequestData(vtkInformation *request,
          vtkInformationVector** vtkNotUsed(inputVector),
          vtkInformationVector* outputVector)
  {
    // Get the output dataset
    vtkInformation* outInfo = outputVector->GetInformationObject(0);
    assert(outInfo != NULL);
    vtkMultiBlockDataSet* output = vtkMultiBlockDataSet::GetData(outputVector,0);
    assert(output != NULL);
    // set the number of blocks
    output->SetNumberOfBlocks(this->GetNumberOfLevels());
  
    int size;
    std::vector<int> idVector;
    // find out which parts of the dataset we need to load
    if (outInfo->Has(vtkCompositeDataPipeline::LOAD_REQUESTED_BLOCKS()))
      {
      int* ids;
      size = outInfo->Length(vtkCompositeDataPipeline::UPDATE_COMPOSITE_INDICES());
      ids = outInfo->Get(vtkCompositeDataPipeline::UPDATE_COMPOSITE_INDICES());
      idVector.resize(size);
      std::copy(ids,ids+size,idVector.begin());
      }
    // default to loading all of the lowest level of detail
    else
      {
      for (int j = 0; j < this->Internal->NumberOfBlocksPerLevel; ++j)
        {
        idVector.push_back(j);
        }
      size = idVector.size();
      }
  
    // sort the requested blocks
    std::sort(idVector.begin(),idVector.end());
    // compute the block ids relative to the file (the internal reader needs these)
    std::vector<int> localIdVector;
    for (unsigned i = 0; i < idVector.size(); ++i)
      {
      localIdVector.push_back(idVector[i] % this->Internal->NumberOfBlocksPerLevel);
      }
    // these mark the beginning and end of the local ids for the internal reader
    // for the current itertion of the loop
    int lBound = 0, uBound;
    for (int i = 0; i < this->GetNumberOfLevels(); ++i)
      {
      // compute new bounds for current reader's blocks
      uBound = binSearch(&idVector[0],idVector.size(),this->Internal->NumberOfBlocksPerLevel*(i+1));
      int levelSize = uBound - lBound;
  
      vtkNew< vtkMultiBlockDataSet > dataset;
      if (levelSize > 0)
        {
        // create a new request
        vtkNew< vtkInformation > internalRequestData;
        internalRequestData->Set(vtkDemandDrivenPipeline::REQUEST_DATA());
        internalRequestData->Copy(request);
  
        // create a new output info
        vtkNew< vtkInformation > internalOutInfo;
        internalOutInfo->CopyEntry(outInfo,vtkStreamingDemandDrivenPipeline::TIME_STEPS());
        internalOutInfo->CopyEntry(outInfo,vtkStreamingDemandDrivenPipeline::TIME_RANGE());
        internalOutInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_TIME_STEP(),
                             outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_TIME_STEP()));
        internalOutInfo->Set(vtkDataObject::DATA_OBJECT(),dataset.GetPointer());
        internalOutInfo->Set(vtkCompositeDataPipeline::UPDATE_COMPOSITE_INDICES(),
                             &localIdVector[lBound],levelSize);
        internalOutInfo->Set(vtkCompositeDataPipeline::LOAD_REQUESTED_BLOCKS(),1);
  
        // put the output info in a vector
        vtkNew< vtkInformationVector > internalOutVector;
        internalOutVector->Append(internalOutInfo.GetPointer());
  
        // ask the internal reader for its data
        this->Internal->Resolutions[i].Reader->ProcessRequest(
              internalRequestData.GetPointer(),(vtkInformationVector**)NULL,
              internalOutVector.GetPointer());
        // set the block in our output to the output of the internal reader
        }
      else
        {
        dataset->SetNumberOfBlocks(this->Internal->NumberOfBlocksPerLevel);
        }
      output->SetBlock(i,dataset.GetPointer());
      // the next reader's blocks will start where this one's blocks ended
      lBound = uBound;
      if (i == 0)
        {
        output->GetInformation()->Set(vtkDataObject::DATA_TIME_STEP(),
                                      dataset->GetInformation()->Get(vtkDataObject::DATA_TIME_STEP()));
        }
      }
    return 1;
  }
  