/*=========================================================================

  Copyright (c) Kitware, Inc.
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/VolViewCopyright.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 "vtkKWOpenFileHelper.h"

#include "vtkKWInternationalization.h"
#include "vtkKWMessageDialog.h"
#include "vtkKWOpenFileProperties.h"
#include "vtkKWOpenWizard.h"
#include "vtkKWProgressCommand.h"
#include "vtkKWWindow.h"

#include "vtkDataObject.h"
#include "vtkDataSetAttributes.h"
#include "vtkDICOMCollectorOptions.h"
#include "vtkInformation.h"
#include "vtkObjectFactory.h"
#include "vtkStreamingDemandDrivenPipeline.h"
#include "vtkStringArray.h"
#include "vtkStructuredPoints.h"
#include "vtkUnstructuredGrid.h"

// The readers we support

#include "vtkAnalyzeReader.h"
#include "vtkBMPReader.h"
#include "vtkDICOMCollector.h"
#include "vtkDICOMReader.h"
#include "vtkGESignaReader.h"
#include "vtkGESignaReader3D.h"
#include "vtkJPEGReader.h"
#include "vtkLSMReader.h"
#include "vtkPICReader.h"
#include "vtkPNGReader.h"
#include "vtkPNMReader.h"
#include "vtkSLCReader.h"
#include "vtkSTKReader.h"
#include "vtkStructuredPointsReader.h"
#include "vtkUnstructuredGridReader.h"
#include "vtkTIFFReader.h"
#include "vtkMetaImageReader.h"
#include "vtkXMLImageDataReader.h"

#include "vtksys/SystemTools.hxx"

#ifdef VTK_USE_ANSI_STDLIB
#define VTK_IOS_NOCREATE 
#else
#define VTK_IOS_NOCREATE | ios::nocreate
#endif

#define VTK_VV_OW_DATA_ORIGIN_UNKNOWN -0.125
#define VTK_VV_OW_DATA_SPACING_UNKNOWN -0.125

//----------------------------------------------------------------------------
vtkStandardNewMacro(vtkKWOpenFileHelper);
vtkCxxRevisionMacro(vtkKWOpenFileHelper, "$Revision: 1.14 $");

//----------------------------------------------------------------------------
vtkCxxSetObjectMacro(vtkKWOpenFileHelper, LastReader, vtkAlgorithm);
vtkCxxSetObjectMacro(vtkKWOpenFileHelper, DICOMOptions, vtkDICOMCollectorOptions);

//----------------------------------------------------------------------------
class vtkKWOpenFileHelperInternals
{
public:

  struct ValidFileExtensionNode
  {
    vtksys_stl::string Description;
    vtksys_stl::string Extension;
  };

  typedef vtksys_stl::vector<ValidFileExtensionNode> ValidFileExtensionPoolType;
  typedef vtksys_stl::vector<ValidFileExtensionNode>::iterator ValidFileExtensionPoolIterator;
  ValidFileExtensionPoolType ValidFileExtensionPool;
  vtksys_stl::string TclString;
};

//----------------------------------------------------------------------------
vtkKWOpenFileHelper::vtkKWOpenFileHelper()
{
  this->Internals = new vtkKWOpenFileHelperInternals;

  this->OpenFileProperties       = vtkKWOpenFileProperties::New();
  this->DICOMOptions             = vtkDICOMCollectorOptions::New();
  this->LastReader               = NULL;
  this->OpenWizard               = NULL;
  this->SupportDICOMFormatOnly   = 0;
  this->AllowVTKUnstructuredGrid = 0;
}

//----------------------------------------------------------------------------
vtkKWOpenFileHelper::~vtkKWOpenFileHelper()
{
  this->SetOpenWizard(NULL);

  this->SetLastReader(NULL);

  if (this->OpenFileProperties)
    {
    this->OpenFileProperties->Delete();
    this->OpenFileProperties = 0;
    }

  this->SetDICOMOptions(0);

  if (this->Internals)
    {
    delete this->Internals;
    this->Internals = 0;
    }
}

//----------------------------------------------------------------------------
void vtkKWOpenFileHelper::SetOpenWizard(vtkKWOpenWizard* wizard)
{
  // Intentionally not ref-counted. The OpenWizard, if non-NULL, owns us and
  // will be around as long as we are...
  //
  this->OpenWizard = wizard;
}

//----------------------------------------------------------------------------
const char* vtkKWOpenFileHelper::GetFileTypesTclString()
{
  this->Internals->TclString = "";

  vtkKWOpenFileHelperInternals::ValidFileExtensionPoolIterator it =
    this->Internals->ValidFileExtensionPool.begin();
  vtkKWOpenFileHelperInternals::ValidFileExtensionPoolIterator end =
    this->Internals->ValidFileExtensionPool.end();
  for (; it != end; ++it)
    {
    this->Internals->TclString += "{{";
    this->Internals->TclString += it->Description;
    this->Internals->TclString += "} {";
    this->Internals->TclString += it->Extension;
    this->Internals->TclString += "}} ";
    }

  return this->Internals->TclString.c_str();
}

//----------------------------------------------------------------------------
int vtkKWOpenFileHelper::CheckReader(vtkImageReader2 *reader, 
                                     const char *path,
                                     int &bestReaderValue)
{
  // If DICOM reader, use a progress, as it might take some time to
  // collect data and check scalar type the first time

  vtkKWProgressCommand *cb = NULL;
  vtkDICOMReader *dicom_reader = vtkDICOMReader::SafeDownCast(reader);
  bool monitorProgress = false;
  if (dicom_reader && this->GetOpenWizard())
    {
    monitorProgress = true;
    }
  if (monitorProgress)
    {
    cb = vtkKWProgressCommand::New();
    cb->SetWindow(vtkKWWindow::SafeDownCast(
      this->GetOpenWizard()->GetMasterWindow()));
    cb->SetStartMessage(ks_("Progress|Checking DICOM file"));
    cb->SetRetrieveProgressMethodToCallData();
    dicom_reader->AddObserver(vtkCommand::StartEvent, cb);
    dicom_reader->AddObserver(vtkCommand::ProgressEvent, cb);
    dicom_reader->AddObserver(vtkCommand::EndEvent, cb);
    }

  int valid = reader->CanReadFile(path);
  if (valid > bestReaderValue)
    {
    if (this->LastReader)
      {
      this->LastReader->Delete();
      }
    this->LastReader = reader;
    this->LastReader->Register(this);

    if (!reader->GetFileName())
      {
      reader->SetFileName(path);
      }

    // Important, we do not want the default, so that the wizard can
    // detect that case and guess a better default

    reader->SetFilePattern(NULL); 

    // Initialize the values to known settings so that we will know if the
    // reader changed them.

    reader->SetDataOrigin(VTK_VV_OW_DATA_ORIGIN_UNKNOWN,
                          VTK_VV_OW_DATA_ORIGIN_UNKNOWN,
                          VTK_VV_OW_DATA_ORIGIN_UNKNOWN);
    reader->SetDataSpacing(VTK_VV_OW_DATA_SPACING_UNKNOWN,
                           VTK_VV_OW_DATA_SPACING_UNKNOWN,
                           VTK_VV_OW_DATA_SPACING_UNKNOWN);

    vtkExecutive *exec = reader->GetExecutive();
    vtkStreamingDemandDrivenPipeline* sddp =
      vtkStreamingDemandDrivenPipeline::SafeDownCast(exec);

    if(sddp && !sddp->UpdateInformation())
      {
      // UpdateInformation failed, i.e. something went wrong in some code
      // like RequestInformation(), where the checks that are made against
      // the file are stronger than what is returned by CanReadFile.
      // In that case, return 1, which means, according to vtkImageReader2:
      // "I think I can read the file but I cannot prove it"
      valid = 1;
      }
    else
      {
      vtkInformation *pinfo = exec->GetOutputInformation(0);

      int scalarType;
      int numComp;

      vtkInformation *scalarInfo =
        vtkDataObject::GetActiveFieldInformation(
          pinfo,
          vtkDataObject::FIELD_ASSOCIATION_POINTS,
          vtkDataSetAttributes::SCALARS);
      if (scalarInfo)
        {
        scalarType = scalarInfo->Get(vtkDataObject::FIELD_ARRAY_TYPE());
        numComp = scalarInfo->Get(vtkDataObject::FIELD_NUMBER_OF_COMPONENTS());
        }
      else
        {
        scalarType = VTK_DOUBLE;
        numComp = 1;
        }

      int *wextent =
        pinfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT());
      double *origin = pinfo->Get(vtkImageData::ORIGIN() );
      double *spacing = pinfo->Get(vtkImageData::SPACING() );
      if (!wextent || 
          !origin || 
          !spacing ||
          (wextent[1] - wextent[0] <= 0 &&
           wextent[3] - wextent[2] <= 0 &&
           wextent[5] - wextent[4] <= 0))
        {
        // The extent could not be retrieved.
        // In that case, return 1, which means, according to vtkImageReader2:
        // "I think I can read the file but I cannot prove it"
        // Also trigger it if UpdateInformation succeeded but
        // ExecuteInformation failed (in legacy filters like both 
        // vtkGESignaReader), i.e. when the default 
        // DataExtent (0,0) (0,0) (0,0) has not been updated and was pushed
        // that way to the WholeExtent... This test can not be done directly
        // on DataExtent because some filters (like vtkDICOMReader) will
        // set the WholeExtent directly, wthout updating DataExtent.
        // Note that this will also trigger a false-positive when 
        // reading a single pixel file (1x1x1 pixel), which we are going to
        // assume is extremely rare.
        valid = 1;
        this->GetOpenFileProperties()->SetOrigin(
          VTK_VV_OW_DATA_ORIGIN_UNKNOWN, 
          VTK_VV_OW_DATA_ORIGIN_UNKNOWN, 
          VTK_VV_OW_DATA_ORIGIN_UNKNOWN);
        this->GetOpenFileProperties()->SetSpacing(
          VTK_VV_OW_DATA_SPACING_UNKNOWN, 
          VTK_VV_OW_DATA_SPACING_UNKNOWN, 
          VTK_VV_OW_DATA_SPACING_UNKNOWN);
        this->GetOpenFileProperties()->SetWholeExtent(
          0, -1, 0, -1, 0, -1);
        }
      else
        {
        // copy the info to be safe (there is always a chance that
        // UpdateInformation actually read in the data and we don't want to
        // hold onto that)
        this->GetOpenFileProperties()->SetOrigin(origin);
        this->GetOpenFileProperties()->SetSpacing(spacing);
        this->GetOpenFileProperties()->SetWholeExtent(wextent);
        this->GetOpenFileProperties()->SetScalarType(scalarType);
        this->GetOpenFileProperties()->SetNumberOfScalarComponents(numComp);
        this->GetOpenFileProperties()->SetDataByteOrder(
          reader->GetDataByteOrder());
        this->GetOpenFileProperties()->SetFileDimensionality(
          reader->GetFileDimensionality());
        this->GetOpenFileProperties()->SetFilePattern(
          reader->GetFilePattern());
        this->GetOpenFileProperties()->SetScope(
          dicom_reader 
          ? vtkKWOpenFileProperties::ScopeMedical 
          : vtkKWOpenFileProperties::ScopeScientific);
        }
      }
    }

  if (monitorProgress)
    {
    dicom_reader->RemoveObserver(cb);
    cb->Delete();
    }
  reader->Delete();

  if (valid > bestReaderValue)
    {
    bestReaderValue = valid;
    }

  return valid;
}

//----------------------------------------------------------------------------
int vtkKWOpenFileHelper::IsFileValid(const char *fname)
{
  vtkStringArray *filenames = vtkStringArray::New();
  filenames->InsertNextValue(fname);
  int res = this->IsFileValid(filenames);
  filenames->Delete();
  return res;
}

//----------------------------------------------------------------------------
int vtkKWOpenFileHelper::IsFileValid(vtkStringArray *filenames)
{
  // No (or empty) filename

  if (!filenames || !filenames->GetNumberOfValues())
    {
    return vtkKWOpenFileHelper::FILE_IS_INVALID;
    }  

  // File doesn't exist

  const char *fname = filenames->GetValue(0);
  if (!vtksys::SystemTools::FileExists(fname))
    {
    return vtkKWOpenFileHelper::FILE_IS_INVALID;
    }

  const char *ext = fname + strlen(fname) - 4;

  vtkImageReader2* reader = 0;
  int bestReaderValue = 0;
  if (this->LastReader)
    {
    this->LastReader->Delete();
    }
  this->LastReader = 0;
  
  int reader_res = 0;

  // we manually check the file type we know about instead of using the
  // factory this gives us more flexibility about the order in which we check
  // the files etc. Start by doing the fast checks (e.g. file types with good
  // easy to read and verify headers)

  vtkAnalyzeReader * analyzeReader = vtkAnalyzeReader::New();
  reader = analyzeReader;
  if (this->CheckReader(reader, fname, bestReaderValue) == 3)
    {
    analyzeReader->SpacingSpecifiedFlagOn();
    analyzeReader->OriginSpecifiedFlagOn();
    return vtkKWOpenFileHelper::FILE_IS_VALID;
    }

  vtkBMPReader *bmpReader = vtkBMPReader::New();
  bmpReader->Allow8BitBMPOn();
  reader = bmpReader;
  if (this->CheckReader(reader, fname, bestReaderValue) == 3)
    {
    return vtkKWOpenFileHelper::FILE_IS_VALID;
    }

  vtkDICOMReader *dreader = vtkDICOMReader::New();
  dreader->GetDICOMCollector()->SetOptions( this->DICOMOptions );
  // If there is only one single file, try to check for a whole stack, 
  // otherwise assume we were given an explicit list of files to load.
  if (filenames->GetNumberOfValues() <= 1)
    {
    int old_explore = this->DICOMOptions->GetExploreDirectory();
    this->DICOMOptions->ExploreDirectoryOn();
    if (old_explore != this->DICOMOptions->GetExploreDirectory())
      {
      dreader->GetDICOMCollector()->ClearCollectedSlices();
      }
    }
  reader = dreader;
  reader->SetFileNames(filenames);
  reader_res = this->CheckReader(reader, fname, bestReaderValue);
  if (reader_res == 3)
    {
    return vtkKWOpenFileHelper::FILE_IS_VALID;
    }
  if (reader_res == 1)
    {
    return vtkKWOpenFileHelper::FILE_IS_VALID_NOT_SUPPORTED;
    }

  if (this->SupportDICOMFormatOnly)
    {
    return vtkKWOpenFileHelper::FILE_IS_INVALID;
    }

  reader = vtkGESignaReader3D::New();
  if (this->CheckReader(reader, fname, bestReaderValue) == 3)
    {
    return vtkKWOpenFileHelper::FILE_IS_VALID;
    }

  reader = vtkGESignaReader::New();
  if (this->CheckReader(reader, fname, bestReaderValue) == 3)
    {
    return vtkKWOpenFileHelper::FILE_IS_VALID;
    }

  reader = vtkJPEGReader::New();
  if (this->CheckReader(reader, fname, bestReaderValue) == 3)
    {
    return vtkKWOpenFileHelper::FILE_IS_VALID;
    }

  vtkLSMReader * lsmReader = vtkLSMReader::New();
  reader = lsmReader;
  if (this->CheckReader(reader, fname, bestReaderValue) == 3)
    {
    lsmReader->SpacingSpecifiedFlagOn();
    lsmReader->OriginSpecifiedFlagOn();
    return vtkKWOpenFileHelper::FILE_IS_VALID;
    }

  vtkSTKReader * stkReader = vtkSTKReader::New();
  reader = stkReader;
  if (this->CheckReader(reader, fname, bestReaderValue) == 3)
    {
    stkReader->SpacingSpecifiedFlagOn();
    stkReader->OriginSpecifiedFlagOn();
    return vtkKWOpenFileHelper::FILE_IS_VALID;
    }

  reader = vtkPICReader::New();
  if (this->CheckReader(reader, fname, bestReaderValue) == 3)
    {
    return vtkKWOpenFileHelper::FILE_IS_VALID;
    }

  reader = vtkPNGReader::New();
  if (this->CheckReader(reader, fname, bestReaderValue) == 3)
    {
    return vtkKWOpenFileHelper::FILE_IS_VALID;
    }

  reader = vtkPNMReader::New();
  if (this->CheckReader(reader, fname, bestReaderValue) == 3)
    {
    return vtkKWOpenFileHelper::FILE_IS_VALID;
    }

  vtkSLCReader * slcReader = vtkSLCReader::New();
  reader = slcReader;
  if (this->CheckReader(reader, fname, bestReaderValue) == 3)
    {
    slcReader->SpacingSpecifiedFlagOn();
    slcReader->OriginSpecifiedFlagOn();
    return vtkKWOpenFileHelper::FILE_IS_VALID;
    }

  vtkTIFFReader *treader = vtkTIFFReader::New();
  reader = treader;
  if (this->CheckReader(reader, fname, bestReaderValue) == 3)
    {
    treader->SpacingSpecifiedFlagOn();
    treader->OriginSpecifiedFlagOn();
    return vtkKWOpenFileHelper::FILE_IS_VALID;
    }

  reader = vtkMetaImageReader::New();
  if (this->CheckReader(reader, fname, bestReaderValue) == 3)
    {
    return vtkKWOpenFileHelper::FILE_IS_VALID;
    }

  // Check for new VTK format files

  vtkXMLImageDataReader *xidr = vtkXMLImageDataReader::New();
  int valid = xidr->CanReadFile(fname);
  if (valid > bestReaderValue)
    {
    if (this->LastReader)
      {
      this->LastReader->Delete();
      }
    this->LastReader = xidr;
    this->LastReader->Register(this);
    bestReaderValue = valid;
    xidr->SetFileName(fname);
    xidr->UpdateInformation();
    // copy the info to be safe (there is always a chance that
    // UpdateInformation actually read in the data and we don;t want to hold
    // onto that)
    this->GetOpenFileProperties()->SetOrigin(
      xidr->GetOutput()->GetOrigin());
    this->GetOpenFileProperties()->SetSpacing(
      xidr->GetOutput()->GetSpacing());
    this->GetOpenFileProperties()->SetWholeExtent(
      xidr->GetOutput()->GetWholeExtent());
    this->GetOpenFileProperties()->SetScalarType(
      xidr->GetOutput()->GetScalarType());
    this->GetOpenFileProperties()->SetNumberOfScalarComponents(
      xidr->GetOutput()->GetNumberOfScalarComponents());
    }
  xidr->Delete();
  if (valid == 3)
    {
    return vtkKWOpenFileHelper::FILE_IS_VALID;
    }

  vtkKWOpenFileHelperInternals::ValidFileExtensionPoolIterator it = 
    this->Internals->ValidFileExtensionPool.begin();
  vtkKWOpenFileHelperInternals::ValidFileExtensionPoolIterator end = 
    this->Internals->ValidFileExtensionPool.end();
  for (; it != end; ++it)
    {
    vtksys_stl::vector<vtksys_stl::string> extensions;
    vtksys::SystemTools::Split(it->Extension.c_str(), extensions, ' ');

    vtksys_stl::vector<vtksys_stl::string>::iterator e_it = extensions.begin();
    vtksys_stl::vector<vtksys_stl::string>::iterator e_end = extensions.end();
    for (; e_it != e_end; e_it++)
      {
      vtksys_stl::string upper = vtksys::SystemTools::UpperCase(*e_it);
      vtksys_stl::string lower = vtksys::SystemTools::LowerCase(*e_it);
      if (!strcmp(ext, upper.c_str()) || !strcmp(ext, lower.c_str()))
        {
        return vtkKWOpenFileHelper::FILE_IS_VALID_NOT_DATA;
        }
      }
    }

  // Check for vtk files           

  if (!strcmp(ext, ".vtk") || !strcmp(ext, ".VTK"))
    {    
    // We need to make sure this is a structured points file
    vtkDataReader *dr = vtkDataReader::New();
    dr->SetFileName(fname);
    if (!dr->IsFileStructuredPoints() && 
      !(dr->IsFileUnstructuredGrid() && this->AllowVTKUnstructuredGrid))
      {
      if (this->GetOpenWizard() && this->GetOpenWizard()->GetApplication())
        {
        vtkKWMessageDialog::PopupMessage(
          this->GetOpenWizard()->GetApplication(), 0,
          ks_("Open Wizard|Dialog|Open File Error!"),
          k_("The VTK file specified is either invalid, "
             "or not a structured points data file. "
             "Of the different data types that vtk files can contain, "
             "only structured points is suitable for volume rendering."), 
          vtkKWMessageDialog::ErrorIcon );
        }
      else
        {
        vtkErrorMacro(
          "The VTK file specified is either invalid, "
          "or not a structured points data file. "
          "Of the different data types that vtk files can contain, "
          "only structured points is suitable for volume rendering."
          );
        }
      dr->Delete();
      return  vtkKWOpenFileHelper::FILE_IS_INVALID;
      }
    // we need to get the image info but don't want to read in the whole
    // file. Do that here
    if (dr->IsFileStructuredPoints())
      {
      dr->Delete();
      vtkStructuredPointsReader *spr = vtkStructuredPointsReader::New();
      spr->SetFileName(fname);
      spr->Update();
      this->GetOpenFileProperties()->SetOrigin(
        spr->GetOutput()->GetOrigin());
      this->GetOpenFileProperties()->SetSpacing(
        spr->GetOutput()->GetSpacing());
      this->GetOpenFileProperties()->SetWholeExtent(
        spr->GetOutput()->GetWholeExtent());
      this->GetOpenFileProperties()->SetScalarType(
        spr->GetOutput()->GetScalarType());
      this->GetOpenFileProperties()->SetNumberOfScalarComponents(
        spr->GetOutput()->GetNumberOfScalarComponents());
      if (this->LastReader)
        {
        this->LastReader->Delete();
        }
      this->LastReader = spr;
      return vtkKWOpenFileHelper::FILE_IS_VALID;
      }
    else
      {
      dr->Delete();
      vtkUnstructuredGridReader *uspr = vtkUnstructuredGridReader::New();
      uspr->SetFileName(fname);
      uspr->ReadAllScalarsOn();
      uspr->Update();
      this->GetOpenFileProperties()->SetOrigin(
        VTK_VV_OW_DATA_ORIGIN_UNKNOWN, 
        VTK_VV_OW_DATA_ORIGIN_UNKNOWN, 
        VTK_VV_OW_DATA_ORIGIN_UNKNOWN);
      this->GetOpenFileProperties()->SetSpacing(
        VTK_VV_OW_DATA_SPACING_UNKNOWN, 
        VTK_VV_OW_DATA_SPACING_UNKNOWN, 
        VTK_VV_OW_DATA_SPACING_UNKNOWN);
      this->GetOpenFileProperties()->SetWholeExtent(
        uspr->GetOutput()->GetWholeExtent());
      if (this->LastReader)
        {
        this->LastReader->Delete();
        }
      this->LastReader = uspr;
      return vtkKWOpenFileHelper::FILE_IS_VALID;
      }
    }

  // Can always be raw, but anything can be raw

  reader = vtkImageReader2::New();
  reader->SetFileName(fname);
  this->SetLastReader(reader);
  reader->Delete();
  this->GetOpenFileProperties()->SetOrigin(
    VTK_VV_OW_DATA_ORIGIN_UNKNOWN, 
    VTK_VV_OW_DATA_ORIGIN_UNKNOWN, 
    VTK_VV_OW_DATA_ORIGIN_UNKNOWN);
  this->GetOpenFileProperties()->SetSpacing(
    VTK_VV_OW_DATA_SPACING_UNKNOWN, 
    VTK_VV_OW_DATA_SPACING_UNKNOWN, 
    VTK_VV_OW_DATA_SPACING_UNKNOWN);
  this->GetOpenFileProperties()->SetWholeExtent(
    0, -1, 0, -1, 0, -1);

  return vtkKWOpenFileHelper::FILE_IS_PROBABLY_VALID;
}

//----------------------------------------------------------------------------
void vtkKWOpenFileHelper::AddValidFileExtension(
    const char *description, const char *extension)
{
  if (!description || !extension || !*extension)
    {
    return;
    }

  vtkKWOpenFileHelperInternals::ValidFileExtensionNode node;
  node.Description = description;
  node.Extension = extension;

  this->Internals->ValidFileExtensionPool.push_back(node);
}

//----------------------------------------------------------------------------
int vtkKWOpenFileHelper::ComputeRawFileColumns(
  const char *path, int guess, int numComp)
{
  int result = guess;
  
  ifstream *ftemp;
  // read the file specified and guess on the byte order
#ifdef _WIN32
  ftemp = new ifstream(path, ios::in | ios::binary VTK_IOS_NOCREATE);
#else
  ftemp = new ifstream(path, ios::in VTK_IOS_NOCREATE);
#endif
  if (ftemp && !ftemp->fail())
    {
    ftemp->seekg(0,ios::end);
    int fsize = ftemp->tellg();
    // we need at least guess*4*numComp bytes
    int targetSize = guess*4*numComp;
    if (fsize >= targetSize)
      {
      // read in data from the middle of the file
      int startPos = fsize/2 - targetSize/2;
      ftemp->seekg(startPos,ios::beg);
      unsigned char *mem = new unsigned char [targetSize];
      ftemp->read((char *)mem,targetSize);
      delete ftemp;

      // analyse the data for the best number of colums
      // search the range from guess/2 to guess*2
      int i;
      int numCols;
      int bestSize = guess;
      double bestError = 255*guess;
      double error;
      double guessError = 0;
      for (numCols = guess/2; numCols < guess*2; numCols++)
        {
        error = 0;
        for (i = 0; i < numCols*numComp; ++i)
          {
          error += fabs((double)(mem[i] - mem[numCols*numComp+i]));
          }
        // compute average error per pixel
        error = error / numCols;
        if (numCols == guess)
          {
          guessError = error;
          }
        if (error < bestError)
          {
          bestSize = numCols;
          bestError = error;
          }
        }

      //  analyze the results, if the best error is significantly better than
      //  the guess error, and the bestSize is significantly different from
      //  the guess htne use the bestSize instead
      if (bestError < 0.9*guessError && fabs((float)bestSize - guess) > guess*0.02)
        {
        result = bestSize;
        }

      delete [] mem;
      } 
    }

  return result;
}

//----------------------------------------------------------------------------
int vtkKWOpenFileHelper::ComputeRawFileRows(
  const char *path, int columns, int guess, int nb_components)
{
  int result = guess;
  
  ifstream *ftemp;
  // read the file specified and guess on the byte order
#ifdef _WIN32
  ftemp = new ifstream(path, ios::in | ios::binary VTK_IOS_NOCREATE);
#else
  ftemp = new ifstream(path, ios::in VTK_IOS_NOCREATE);
#endif
  if (ftemp && !ftemp->fail())
    {
    ftemp->seekg(0,ios::end);
    int fsize = ftemp->tellg();

    // we need at least (guess*2+1)*columns*nb_components bytes
    int targetSize = (guess*2+1)*columns*nb_components;
    if (fsize >= targetSize)
      {
      // read in data from the middle of the file
      int startPos = fsize/2 - targetSize/2;
      ftemp->seekg(startPos,ios::beg);
      unsigned char *mem = new unsigned char [targetSize];
      ftemp->read((char *)mem,targetSize);
      delete ftemp;

      // analyse the data for the best number of colums
      // search the range from guess/2 to guess*2
      int i;
      int numRows;
      int bestSize = guess;
      double bestError = 255*columns;
      double error;
      double guessError = 0;
      for (numRows = guess/2; numRows < guess*2; numRows++)
        {
        error = 0;
        for (i = 0; i < columns*nb_components; ++i)
          {
          error += fabs((double)(mem[i] - mem[numRows*columns*nb_components+i]));
          }
        if (numRows == guess)
          {
          guessError = error;
          }
        if (error < bestError)
          {
          bestSize = numRows;
          bestError = error;
          }
        }

      //  analyze the results, if the best error is significantly better than
      //  the guess error, and the bestSize is significantly different from
      //  the guess htne use the bestSize instead
      if (bestError < 0.9*guessError && fabs((float)bestSize - guess) > guess*0.02)
        {
        result = bestSize;
        }

      delete [] mem;
      } 
    }

  return result;
}

//----------------------------------------------------------------------------
void vtkKWOpenFileHelper::AnalyzeRawFile(const char *path)
{
  ifstream *ftemp;
  int componentSize = 1;
  int numComponents = 1;

  vtkImageReader2 *rdr = 
    vtkImageReader2::SafeDownCast(this->GetLastReader());
  if (!rdr)
    {
    return;
    }

  // read the file specified and guess on the byte order
#ifdef _WIN32
  ftemp = new ifstream(path, ios::in | ios::binary VTK_IOS_NOCREATE);
#else
  ftemp = new ifstream(path, ios::in VTK_IOS_NOCREATE);
#endif
  if (ftemp && !ftemp->fail())
    {
    ftemp->seekg(0,ios::end);
    int fsize = ftemp->tellg();
    const unsigned long sample_size = 8000;
    if (fsize >= sample_size)
      {
      // read in data from the middle of the file
      int startPos = fsize/2 - sample_size / 2;
      ftemp->seekg(startPos,ios::beg);
      unsigned char *mem = new unsigned char [sample_size];
      ftemp->read((char *)mem,sample_size);
      delete ftemp;
      // analyse the 9K for swapping, byte size and color
      int i;
      double even, odd, red, green, blue, alpha;
      even = 0;
      odd = 0;
      red = 0;
      green = 0;
      blue = 0;
      alpha = 0;
      for (i = 2; i < sample_size; i = i + 2)
        {
        even += fabs((double)(mem[i] - mem[i-2]));
        odd += fabs((double)(mem[i+1] - mem[i-1]));
        }
      for (i = 3; i < sample_size; i = i + 3)
        {
        red +=  fabs((double)(mem[i] - mem[i-3]));
        green +=  fabs((double)(mem[i+1] - mem[i-2]));
        blue +=  fabs((double)(mem[i+2] - mem[i-1]));
        }
      // note do not divide by three on the next line
      double rgbVar = red + green + blue;
      red = 0;
      green = 0;
      blue = 0;
      for (i = 4; i < sample_size; i = i + 4)
        {
        red +=  fabs((double)(mem[i] - mem[i-4]));
        green +=  fabs((double)(mem[i+1] - mem[i-3]));
        blue +=  fabs((double)(mem[i+2] - mem[i-2]));
        alpha +=  fabs((double)(mem[i+3] - mem[i-1]));
        }
      double rgbaVar = red + green + blue + alpha;
      double evenOddVar = even + odd;
      if (rgbVar*2 < evenOddVar)
        {
        if (rgbVar*2 < rgbaVar)
          {
          numComponents = 3;
          }
        else
          {
          numComponents = 4;
          }
        }
      else
        {
        if (rgbaVar*2 < evenOddVar)
          {
          numComponents = 4;
          }
        }
      
      // if even is significantly higher than odd then we must be little 
      // endian and 16bit data (10 was found a little too high, switch to 6)
      const int factor = 6;
      if (even > factor*odd)
        {
        rdr->SetDataByteOrderToLittleEndian();
        this->GetOpenFileProperties()->SetDataByteOrderToLittleEndian();
        componentSize = 2;
        }
      else
        {
        if (odd > factor*even)
          {
          rdr->SetDataByteOrderToBigEndian();
          this->GetOpenFileProperties()->SetDataByteOrderToBigEndian();
          componentSize = 2;
          }
        }
      delete [] mem;
      } 
    }
  
#ifdef _WIN32
  ftemp = new ifstream(path, ios::in | ios::binary VTK_IOS_NOCREATE);
#else
  ftemp = new ifstream(path, ios::in VTK_IOS_NOCREATE);
#endif

  if (ftemp && !ftemp->fail())
    {
    ftemp->seekg(0,ios::end);
    int fsize = ftemp->tellg();
    delete ftemp;
    // based on size guess at resolution
    int pixelSize = componentSize*numComponents; 
    double dtmp = fsize/pixelSize;
    dtmp = log(dtmp)/(log(static_cast<double>(2))*rdr->GetFileDimensionality());
    int xpow2 = static_cast<int>(dtmp + 0.01);
    dtmp = fabs(dtmp - xpow2);
    // if dtmp is larger than 0.2 or so then we have a bad estimate
    // Just FYI
    int xsize = 1;
    while (xpow2 > 0)
      {
      xsize = xsize*2;
      xpow2--;
      }

    // now we have an estimate on the number of rows and columns
    // try looking for interrow correlation to determine the number of columns
    int columns = xsize;
    int rows = xsize;
    int slices = xsize;
    // currently only implementd for 1 byte data types
    if (componentSize == 1)
      {
      columns = 
        vtkKWOpenFileHelper::ComputeRawFileColumns(
          path,xsize, numComponents);
      if (rdr->GetFileDimensionality() == 2)
        {
        rows = fsize/(columns*numComponents);
        }
      else
        {
        rows = (xsize*xsize)/columns;
        rows = vtkKWOpenFileHelper::ComputeRawFileRows(
          path, columns, rows, numComponents);
        slices = fsize/(columns*numComponents*rows);
        }
      }
    // otherwise at least guess slices based on X and Y and fsize
    else if (rdr->GetFileDimensionality() == 3)
      {
      slices = fsize/(columns*numComponents*rows*pixelSize);
      }
    
    if (rdr->GetFileDimensionality() == 2)
      {
      rdr->SetDataExtent(0,columns-1,0,rows-1,0,0);
      this->GetOpenFileProperties()->SetWholeExtent(0,columns-1,0,rows-1,0,0);
      }
    else
      {
      rdr->SetDataExtent(0,columns-1,0,rows-1,0,slices-1);
      this->GetOpenFileProperties()->SetWholeExtent(
        0,columns-1, 0,rows-1,0,slices-1);
      }
    if (componentSize == 2)
      {
      rdr->SetDataScalarType(VTK_UNSIGNED_SHORT);
      this->GetOpenFileProperties()->SetScalarType(VTK_UNSIGNED_SHORT);
      }
    else
      {
      rdr->SetDataScalarType(VTK_UNSIGNED_CHAR);
      this->GetOpenFileProperties()->SetScalarType(VTK_UNSIGNED_CHAR);
      }
    }
  rdr->SetNumberOfScalarComponents(numComponents);
  this->GetOpenFileProperties()->SetNumberOfScalarComponents(numComponents);
}

//----------------------------------------------------------------------------
int vtkKWOpenFileHelper::FindSeriesPattern(
  const char *seed_file,
  char *pattern,
  int *zmin, int *zmax)
{
  // Try to find a sequential pattern in a directory

  int length = static_cast<int>(strlen(seed_file));
  int i;
  int start_num_pos = -1;
  int end_num_pos = -1;

  for (i = length; i >=0 ; i--)
    {
    if (seed_file[i] >= '0' && seed_file[i] <= '9')
      {
      start_num_pos = end_num_pos = i;
      int j = i-1;
      while (j >=0 && seed_file[j] >= '0' && seed_file[j] <= '9')
        {
        start_num_pos = j;
        j--;
        }
      break;
      }
    }
  if(start_num_pos == -1)
    {
    return 0;
    }

  char *prefix = new char[start_num_pos + 1];
  prefix[start_num_pos] = 0;
  strncpy(prefix, seed_file, start_num_pos);
  int length_postfix = length - end_num_pos - 1;
  char *postfix = new char[length_postfix + 1];
  postfix[length_postfix] = 0;
  if(end_num_pos + 1 < length - 1)
    {
    strcpy(postfix, seed_file + end_num_pos + 1);
    }

  int number_string_length = end_num_pos - start_num_pos+1;
  char *number_string = new char[number_string_length + 1];
  number_string[number_string_length] = 0;
  strncpy(number_string, seed_file + start_num_pos, number_string_length);
  int number_value = 0;
  sscanf(number_string, "%d", &number_value);
  *zmax = *zmin = number_value;
  int done = 0;
  int maxlength = length * 2;
  char *test_file_name = new char[maxlength];
  char *test_file_name_pad = new char[maxlength];
  int has_pad = 0;

  // Try counting up from zmax

  while (!done)
    {
    sprintf(test_file_name, "%s%i%s", prefix, *zmax + 1, postfix);
    char formatstring[1024];
    sprintf(formatstring, "%%s%%0%dd%%s", number_string_length);
    sprintf(test_file_name_pad, formatstring, prefix, *zmax + 1, postfix);
    if (vtksys::SystemTools::FileExists(test_file_name))
      {
      (*zmax)++;
      }
    else if (vtksys::SystemTools::FileExists(test_file_name_pad))
      {
      (*zmax)++;
      has_pad = 1;
      }
    else
      {
      done = 1;
      }
    }
  done = 0;

  // Try counting down from zmin

  while (!done && *zmin > 0)
    {
    sprintf(test_file_name, "%s%i%s", prefix, *zmin - 1, postfix);
    char formatstring[1024];
    sprintf(formatstring, "%%s%%0%dd%%s", number_string_length);
    sprintf(test_file_name_pad, formatstring, prefix, *zmin - 1, postfix);
    if (vtksys::SystemTools::FileExists(test_file_name))
      {
      (*zmin)--;
      }
    else if (vtksys::SystemTools::FileExists(test_file_name_pad))
      {
      (*zmin)--;
      has_pad = 1;
      }
    else
      {
      done = 1;
      }
    }
  if (has_pad)
    {
    sprintf(pattern, "%s%%0%ii%s", prefix,number_string_length, postfix);
    }
  else
    {
    sprintf(pattern, "%s%%i%s", prefix, postfix);
    }

  // Delete the junk, should be using std::string....

  delete [] prefix;
  delete [] postfix;
  delete [] number_string;
  delete [] test_file_name;
  delete [] test_file_name_pad;

  return 1;
}

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

  os << indent << "OpenWizard: " << this->OpenWizard << endl;

  os << indent << "LastReader:";
  if (this->LastReader)
    {
    os << " " << this->LastReader->GetClassName()
      << " (" << this->LastReader << ")" << endl;
    this->LastReader->PrintSelf(os,indent.GetNextIndent());
    }
  else
    {
    os << " (none)" << endl;
    }

  os << indent << "OpenFileProperties:";
  if (this->GetOpenFileProperties())
    {
    os << " " << this->GetOpenFileProperties()->GetClassName()
      << " (" << this->GetOpenFileProperties() << ")" << endl;
    this->GetOpenFileProperties()->PrintSelf(os,indent.GetNextIndent());
    }
  else
    {
    os << " (none)" << endl;
    }

  os << indent << "DICOMOptions:";
  if (this->DICOMOptions)
    {
    os << " " << this->DICOMOptions->GetClassName()
      << " (" << this->DICOMOptions << ")" << endl;
    this->DICOMOptions->PrintSelf(os,indent.GetNextIndent());
    }
  else
    {
    os << " (none)" << endl;
    }

  os << indent << "SupportDICOMFormatOnly: "
    << (this->SupportDICOMFormatOnly? "On" : "Off") << endl;

  os << indent << "AllowVTKUnstructuredGrid: "
    << (this->AllowVTKUnstructuredGrid? "On" : "Off") << endl;
}
