// For comment from VTK Developers (developer a.maclean@cas.edu.au).
#include "ParticleReader.h"

#include <sstream>
#include <algorithm>

#include <vtkSmartPointer.h>
#include <vtkStreamingDemandDrivenPipeline.h>
#include <vtkInformation.h>
#include <vtkInformationVector.h>
#include <vtkObjectFactory.h>

#include <vtkPointData.h>
#include <vtkPoints.h>
#include <vtkPolyData.h>
#include <vtkCellArray.h>

#include <vtkDoubleArray.h>

vtkCxxRevisionMacro(ParticleReader, "$Revision: 1.0 $");
vtkStandardNewMacro(ParticleReader);

//----------------------------------------------------------------------------
ParticleReader::ParticleReader() :
  FileName(NULL)
  , File(NULL)
  , HasValue(1)
{
  this->SetNumberOfInputPorts(0);
}

//----------------------------------------------------------------------------
ParticleReader::~ParticleReader()
{ 
  if (this->File)
    {
    this->File->close();
    delete this->File;
    this->File = NULL;
    }
  
  if (this->FileName)
    {
    delete [] this->FileName;
    this->FileName = NULL;
    }
}

//----------------------------------------------------------------------------
void ParticleReader::OpenFile()
{
  if (!this->FileName)
    {
    vtkErrorMacro(<<"FileName must be specified.");
    return;
    }

  // If the file was open close it.
  if (this->File)
    {
    this->File->close();
    delete this->File;
    this->File = NULL;
    }
  
  // Open the new file.
  vtkDebugMacro(<< "Initialize: opening file " << this->FileName);
#ifdef _WIN32
  this->File = new ifstream(this->FileName, ios::in | ios::binary);
#else
  this->File = new ifstream(this->FileName, ios::in);
#endif
  if (! this->File || this->File->fail())
    {
    vtkErrorMacro(<< "Initialize: Could not open file " << 
    this->FileName);
    return;
    }
}

//----------------------------------------------------------------------------
// This method returns the largest data that can be generated.
int ParticleReader::RequestInformation(
  vtkInformation *vtkNotUsed(request),
  vtkInformationVector **vtkNotUsed(inputVector),
  vtkInformationVector *outputVector)
{
  // get the info object
  vtkInformation *outInfo = outputVector->GetInformationObject(0);

  outInfo->Set(vtkStreamingDemandDrivenPipeline::MAXIMUM_NUMBER_OF_PIECES(),
               -1);

  return 1;
}

//----------------------------------------------------------------------------
// This method returns the largest data that can be generated.
int ParticleReader::RequestData(
  vtkInformation *vtkNotUsed(request),
  vtkInformationVector **vtkNotUsed(inputVector),
  vtkInformationVector *outputVector)
{
  // get the info object
  vtkInformation *outInfo = outputVector->GetInformationObject(0);

  // get the ouptut
  vtkPolyData *output = vtkPolyData::SafeDownCast(
    outInfo->Get(vtkDataObject::DATA_OBJECT()));

  if (!this->FileName)
    {
    vtkErrorMacro(<<"FileName must be specified.");
    return 0;
    }
  
  this->OpenFile();
    
  // Get the size of the header from the size of the image
  this->File->seekg(0,ios::end);
  if (this->File->fail())
    {
    vtkErrorMacro("Could not seek to end of file.");
    return 0;
    }

  size_t fileLength = (unsigned long)this->File->tellg();
  size_t bytesRead = 0;

  vtkstd::istringstream is;
  vtkstd::string s;
  
  vtkSmartPointer<vtkPoints> pointSource = vtkSmartPointer<vtkPoints>::New();
     pointSource->SetDataTypeToDouble();
  vtkSmartPointer<vtkDoubleArray> pointIntensity = vtkSmartPointer<vtkDoubleArray>::New();
  vtkSmartPointer<vtkPolyData> pointPolyData = vtkSmartPointer<vtkPolyData>::New();
  vtkSmartPointer<vtkCellArray> pointVertices = vtkSmartPointer<vtkCellArray>::New();

  pointSource->Reset();
  pointIntensity->Reset();
  pointVertices->Reset();
  pointIntensity->SetName("Scalar");

  bool lookForEndString = false;
  this->File->seekg(0,ios::beg);
  
  size_t alliquot = 0;
  size_t count = 0;
  while ( vtkstd::getline(*this->File,s,'\n') )
  {
    if ( s.size() != 0 )
      {
      bytesRead += s.size();
      if ( bytesRead > alliquot )
        {
        this->UpdateProgress( bytesRead/(double)fileLength );
        count++;
        alliquot = fileLength / 10 * count;
        }
      // Skip over comment lines.
      std::string::iterator itr;
      std::string tgt("/*");
      itr = vtkstd::search(s.begin(),s.end(),tgt.begin(),tgt.end());
      if ( itr != s.end() )
        {
        lookForEndString = true;
        continue;
        }
      if ( lookForEndString )
        {
        tgt = "*/";
        itr = vtkstd::search(s.begin(),s.end(),tgt.begin(),tgt.end());
        if ( itr != s.end() )
          lookForEndString = false;
        continue;
        }

      tgt = "//";
      itr = vtkstd::search(s.begin(),s.end(),tgt.begin(),tgt.end());
      if ( itr != s.end() )
        continue;
      tgt = "%";
      itr = vtkstd::search(s.begin(),s.end(),tgt.begin(),tgt.end());
      if ( itr != s.end() )
        continue;
      tgt = "#";
      itr = vtkstd::search(s.begin(),s.end(),tgt.begin(),tgt.end());
      if ( itr != s.end() )
        continue;
      // If comma delimited, replace with tab
      std::replace(s.begin(),s.end(),',','\t');
      // We have data.
      is.str(s);
      double x = 0;
      double y = 0;
      double z = 0;
      double w = 0;
      is >> x >> y >> z >> w;
      pointSource->InsertNextPoint(x, y, z);
      if ( this->HasValue)
        pointIntensity->InsertNextValue(w);
      is.clear();
    }
  }
  for(int j = 0; j < pointSource->GetNumberOfPoints(); ++j )
    {
    pointVertices->InsertNextCell( 1 );
    pointVertices->InsertCellPoint( j );
    }
  pointPolyData->Reset();
  pointPolyData->SetPoints(pointSource);
  pointPolyData->SetVerts(pointVertices);
  if ( this->HasValue )
    pointPolyData->GetPointData()->SetScalars(pointIntensity);
  pointPolyData->Modified();
  output->DeepCopy(pointPolyData);
  output->Modified();
  
  return 1;
}


//----------------------------------------------------------------------------
void ParticleReader::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os,indent);

  os << indent << "FileName: " <<
    (this->FileName ? this->FileName : "(none)") << "\n";

}
