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

  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 "vtkVVFileInstance.h"

#include "vtkObjectFactory.h"
#include "vtkStringArray.h"
#include "vtkGlobFileNames.h"
#include "vtkCollection.h"
#include "vtkAlgorithm.h"

#include "vtkKWOpenWizard.h"
#include "vtkKWWindowBase.h"
#include "vtkKWApplication.h"
#include "vtkKWMessageDialog.h"
#include "vtkKWDataTransfer.h"
#include "vtkKWRemoteIOManager.h"
#include "vtkKWCacheManager.h"
#include "vtkKWOpenFileProperties.h"

#include "vtkVVFileAuthenticator.h"
#include "vtkVVDataItemPool.h"
#include "vtkVVDataItemVolume.h"
#include "vtkVVApplication.h"

#include <vtksys/stl/vector>
#include <vtksys/stl/string>
#include <vtksys/ios/sstream>
#include <vtksys/SystemTools.hxx>

//---------------------------------------------------------------------------
vtkStandardNewMacro(vtkVVFileInstance);
vtkCxxRevisionMacro(vtkVVFileInstance, "$Revision: 1.52 $");

#ifdef KWCommonPro_USE_XML_RW
#include "XML/vtkXMLVVFileInstanceReader.h"
#include "XML/vtkXMLVVFileInstanceWriter.h"
#endif
vtkKWGetXMLReaderWriterObjectsImplementationMacro(vtkVVFileInstance, vtkXMLVVFileInstanceReader, vtkXMLVVFileInstanceWriter);

//----------------------------------------------------------------------------
class vtkVVFileInstanceInternals
{
public:

  class FileLocation
  {
  public:
    vtksys_stl::string OriginalPath;
    vtksys_stl::string RelocatedPath; // eventually relocated
    vtksys_stl::string SourceURI;
    vtksys_stl::string DestinationURI;
    vtksys_stl::vector< vtksys_stl::string > PreviewSourceURIs;
    vtksys_stl::vector< vtksys_stl::string > PreviewDestinationURIs;
    vtksys_stl::vector< vtksys_stl::string > PreviewFilenames;
  };

  typedef vtksys_stl::vector<vtkVVFileInstanceInternals::FileLocation> FileNamePoolType;
  typedef vtksys_stl::vector<vtkVVFileInstanceInternals::FileLocation>::iterator FileNamePoolIterator;
  FileNamePoolType FileNamePool;
};

//----------------------------------------------------------------------------
vtkVVFileInstance::vtkVVFileInstance()
{
  this->Internals                     = new vtkVVFileInstanceInternals;
  this->DataItemPool                      = vtkVVDataItemPool::New();
  this->Name                          = NULL;
  this->RelocationDirectory           = NULL;
  this->DataTransferCollection = NULL;
  this->OpenFileProperties            = NULL;
}

//----------------------------------------------------------------------------
vtkVVFileInstance::~vtkVVFileInstance()
{
  if (this->Internals)
    {
    delete this->Internals;
    }

  if (this->DataItemPool)
    {
    this->DataItemPool->Delete();
    this->DataItemPool = NULL;
    }

  this->SetName(NULL);
  this->SetRelocationDirectory(NULL);
  if (this->DataTransferCollection)
    {
    this->DataTransferCollection->Delete();
    }

  this->SetOpenFileProperties(NULL);
}

//----------------------------------------------------------------------------
void vtkVVFileInstance::UnLoad()
{
  if (this->DataItemPool)
    {
    this->DataItemPool->RemoveAllDataItems();
    }
}

//----------------------------------------------------------------------------
int vtkVVFileInstance::GetNumberOfFileNames()
{
  return (int)this->Internals->FileNamePool.size();
}

//----------------------------------------------------------------------------
void vtkVVFileInstance::AddFileName(const char *filename)
{
  vtkVVFileInstanceInternals::FileLocation loc;
  loc.OriginalPath = filename;

  vtksys_stl::string relocated_filename;
  if (this->RelocationDirectory &&
      !vtksys::SystemTools::FileExists(filename) &&
      vtksys::SystemTools::LocateFileInDir(
        filename, this->RelocationDirectory, relocated_filename, 1))
    {
    filename = relocated_filename.c_str();
    }

  loc.RelocatedPath = filename;

  this->Internals->FileNamePool.push_back(loc);
}

//----------------------------------------------------------------------------
const char* vtkVVFileInstance::GetNthFileName(int i)
{
  if (i < 0 || i >= this->GetNumberOfFileNames())
    {
    return NULL;
    }

  return (this->Internals->FileNamePool[i].RelocatedPath).c_str();
}

//----------------------------------------------------------------------------
void vtkVVFileInstance::DeleteAllFileNames()
{
  this->Internals->FileNamePool.clear();
}

//----------------------------------------------------------------------------
void vtkVVFileInstance::SetFileName(const char *filename)
{
  this->DeleteAllFileNames();
  this->AddFileName(filename);
}

//----------------------------------------------------------------------------
const char* vtkVVFileInstance::GetFileName()
{
  return this->GetNthFileName(0);
}

//----------------------------------------------------------------------------
int vtkVVFileInstance::HasFileName(const char *filename)
{
  if (filename && *filename)
    {
    vtkVVFileInstanceInternals::FileNamePoolIterator it = 
      this->Internals->FileNamePool.begin();
    vtkVVFileInstanceInternals::FileNamePoolIterator end = 
      this->Internals->FileNamePool.end();
    for (; it != end; ++it)
      {
      if (!strcmp(filename, (*it).OriginalPath.c_str()) ||
          !strcmp(filename, (*it).RelocatedPath.c_str()))
        {
        return 1;
        }
      }
    }

  return 0;
}

//----------------------------------------------------------------------------
int vtkVVFileInstance::HasSameFileNames(vtkVVFileInstance *instance)
{
  if (!instance || 
      instance->GetNumberOfFileNames() != this->GetNumberOfFileNames())
    {
    return 0;
    }

  // TODO: optimize this code

  vtkVVFileInstanceInternals::FileNamePoolIterator it = 
    this->Internals->FileNamePool.begin();
  vtkVVFileInstanceInternals::FileNamePoolIterator end = 
    this->Internals->FileNamePool.end();
  for (; it != end; ++it)
    {
    if (!instance->HasFileName((*it).RelocatedPath.c_str()))
      {
      return 0;
      }
    }

  return 1;
}

//----------------------------------------------------------------------------
void vtkVVFileInstance::SetFileNameURI(const char *filename, 
                                       const char *sourceURI,
                                       const char *destinationURI)
{
  if (filename && *filename)
    {
    vtkVVFileInstanceInternals::FileNamePoolIterator it = 
      this->Internals->FileNamePool.begin();
    vtkVVFileInstanceInternals::FileNamePoolIterator end = 
      this->Internals->FileNamePool.end();
    for (; it != end; ++it)
      {
      if (!strcmp(filename, (*it).OriginalPath.c_str()) ||
          !strcmp(filename, (*it).RelocatedPath.c_str()))
        {
        (*it).SourceURI = (sourceURI ? sourceURI : "");
        (*it).DestinationURI = (destinationURI ? destinationURI : "");
        return;
        }
      }
    }
}

//----------------------------------------------------------------------------
void vtkVVFileInstance
::AddFileNamePreviewURI(const char *filename, 
                        const char *previewFilename,
                        const char *previewSourceURI,
                        const char *previewDestinationURI)
{
  if (filename && *filename)
    {
    vtkVVFileInstanceInternals::FileNamePoolIterator it = 
      this->Internals->FileNamePool.begin();
    vtkVVFileInstanceInternals::FileNamePoolIterator end = 
      this->Internals->FileNamePool.end();
    for (; it != end; ++it)
      {
      if (!strcmp(filename, (*it).OriginalPath.c_str()) ||
          !strcmp(filename, (*it).RelocatedPath.c_str()))
        {
        if (previewSourceURI && previewDestinationURI && previewFilename)
          {
          (*it).PreviewSourceURIs.push_back(previewSourceURI);
          (*it).PreviewDestinationURIs.push_back(previewDestinationURI);
          (*it).PreviewFilenames.push_back(previewFilename);
          }
        return;
        }
      }
    }
}

//----------------------------------------------------------------------------
const char* vtkVVFileInstance::GetNthFileNamePreviewSourceURI(int i)
{
  if (this->GetNumberOfFileNames())
    {
    if (i < (int)this->Internals->FileNamePool[0].PreviewSourceURIs.size())
      {
      if (this->Internals->FileNamePool[0].PreviewSourceURIs[i].length())
        {
        return this->Internals->FileNamePool[0].PreviewSourceURIs[i].c_str();
        }
      }
    }
  return NULL;
}

//----------------------------------------------------------------------------
const char* vtkVVFileInstance::GetNthFileNamePreviewDestinationURI(int i)
{
  if (this->GetNumberOfFileNames())
    {
    if (i < (int)this->Internals->FileNamePool[0].PreviewDestinationURIs.size())
      {
      if (this->Internals->FileNamePool[0].PreviewDestinationURIs[i].length())
        {
        return this->Internals->FileNamePool[0].PreviewDestinationURIs[i].c_str();
        }
      }
    }
  return NULL;
}

//----------------------------------------------------------------------------
const char* vtkVVFileInstance::GetNthFileNamePreviewFilename(int i)
{
  if (this->GetNumberOfFileNames())
    {
    if (i < (int)this->Internals->FileNamePool[0].PreviewFilenames.size())
      {
      if (this->Internals->FileNamePool[0].PreviewFilenames[i].length())
        {
        return this->Internals->FileNamePool[0].PreviewFilenames[i].c_str();
        }
      }
    }
  return NULL;
}

//----------------------------------------------------------------------------
const char* vtkVVFileInstance::GetNthFileNameSourceURI(int i)
{
  if (i < 0 || i > this->GetNumberOfFileNames())
    {
    return NULL;
    }

  return this->Internals->FileNamePool[i].SourceURI.length() ? 
         this->Internals->FileNamePool[i].SourceURI.c_str() : NULL;
}

//----------------------------------------------------------------------------
const char* vtkVVFileInstance::GetNthFileNameDestinationURI(int i)
{
  if (i < 0 || i > this->GetNumberOfFileNames())
    {
    return NULL;
    }

  return this->Internals->FileNamePool[i].DestinationURI.length() ? 
         this->Internals->FileNamePool[i].DestinationURI.c_str() : NULL;
}

//----------------------------------------------------------------------------
int vtkVVFileInstance::LoadFromURIs()
{
  vtkVVApplication *vvapp = 
    vtkVVApplication::SafeDownCast(this->GetApplication());
  if (!vvapp)
    {
    vtkErrorMacro("Failed loading data from URIs, application is not set!");
    return 0;
    }

  if (!vvapp->SupportRemoteIO())
    {
    return 0;
    }

  vtkKWRemoteIOManager *remote_io_mgr = vvapp->GetRemoteIOManager();
  vtkKWCacheManager *cache_mgr = remote_io_mgr->GetCacheManager();

  // Get the filename, URI's and PreviewURI's

  vtkstd::string fname = this->GetFileName() ? this->GetFileName() : "";

  vtkstd::string sourceUri = 
    this->GetNthFileNameSourceURI(0) ? 
    this->GetNthFileNameSourceURI(0) : "";

  vtkstd::string destinationUri = 
    this->GetNthFileNameDestinationURI(0) ? 
    this->GetNthFileNameDestinationURI(0) : "";

  vtkstd::string previewFilename = 
    this->GetNthFileNamePreviewFilename(0) ?
    this->GetNthFileNamePreviewFilename(0) : "";

  vtkstd::string previewSourceUri = 
    this->GetNthFileNamePreviewSourceURI(0) ?
    this->GetNthFileNamePreviewSourceURI(0) : "";

  vtkstd::string previewDestinationUri = 
    this->GetNthFileNamePreviewDestinationURI(0) ?
    this->GetNthFileNamePreviewDestinationURI(0) : "";

  vtkstd::string previewDestinationUriAbsolute 
    = cache_mgr->GetRemoteCacheDirectory();
  previewDestinationUriAbsolute += "/";
  previewDestinationUriAbsolute += previewDestinationUri;

  vtkstd::string destinationUriAbsolute 
    = cache_mgr->GetRemoteCacheDirectory();
  destinationUriAbsolute += "/";
  destinationUriAbsolute += destinationUri;

  // The absolute path of the file.

  vtkstd::string cachedFilename = cache_mgr->GetRemoteCacheDirectory();
  cachedFilename += "/";
  cachedFilename += fname;

  // The absolute path of the preview file. 

  vtkstd::string previewCachedFilename = cache_mgr->GetRemoteCacheDirectory();
  previewCachedFilename += "/";
  previewCachedFilename += previewFilename;

  // First check in the cache to see if the file exists. If it does, load it

  int load_data_success = 0;
  int load_preview_success = 0;

  // If the cache manager isn't in the force-redownload mode.
  // (used for debugging)

  if (!cache_mgr->GetEnableForceRedownload())
    {
    if (vtksys::SystemTools::FileExists(cachedFilename.c_str()))
      {
      // The file was found in the cache. Let's load that instead.
      load_data_success = this->Load(cachedFilename.c_str()) ? 2 : 0;
      }

    if (!load_data_success && previewSourceUri.length())
      {
      // Look for the preview file and if present load it.
      if (vtksys::SystemTools::FileExists(previewCachedFilename.c_str()))
        {
        // The file was found in the cache. Let's load that instead.
        load_preview_success = 
          this->LoadPreview(previewCachedFilename.c_str()) ? 1 : 0;
        }

      if (load_preview_success == false)
        {
        // So the preview wasn't found/loaded from the cache. Let's look for
        // it in the relocated path as well..
        load_preview_success = this->LoadPreview( previewFilename.c_str() );

        // Apparently our file convention is also busted. We put the files one
        // level down for instance in Article_1124 while the previewFilename
        // does not contain that. So look one level down in the filename as well.
        // ie, if previewFilename is 
        //   "Article_1124/1125/.preview/Gray000.bmp.lowres.View1.0.mha",
        // we will look for 
        //   "RelocatedPath/1125/.preview/Gray000.bmp.lowres.View1.0.mha" as well.

        vtksys_stl::string::size_type firstSlashPos 
                  = previewFilename.find_first_of("/");

        if (firstSlashPos != vtksys_stl::string::npos)
          {
          vtksys_stl::string previewRelocatedFilename = "";
          if (this->RelocationDirectory)
            {
            previewRelocatedFilename += this->RelocationDirectory;
            previewRelocatedFilename += "/";
            previewRelocatedFilename += previewFilename.substr( firstSlashPos + 1 );
            load_preview_success = this->LoadPreview( previewRelocatedFilename.c_str() );
            }
          }
        }
      }
    }

  if (this->DataTransferCollection)
    {
    this->DataTransferCollection->Delete();
    this->DataTransferCollection = NULL;
    }
      
  if (!load_data_success)
    {
    // We could not find/load the preview file or the file itself from the 
    // cache. Let's download them.

    this->DataTransferCollection = vtkCollection::New();

    // First check if we have a previewURI and if so download it.

    if (!load_preview_success && previewSourceUri.length())
      {
      vtkKWDataTransfer *dt = remote_io_mgr->CreateNewDataTransfer();
      if (dt)
        {
        remote_io_mgr->AddDataTransfer(dt);
        dt->Delete();
        dt->SetSourceURI(previewSourceUri.c_str());
        dt->SetDestinationURI(previewDestinationUriAbsolute.c_str());
        dt->SetIdentifier(previewCachedFilename.c_str());
        dt->SetTransferType(vtkKWDataTransfer::RemoteDownload);

        this->DataTransferCollection->AddItem(dt);

        // Previews are always downloaded on the blocking main-thread. Since
        // they are small, we can safely assume that they won't block for 
        // long.
        dt->SetAsynchronous(0);
        remote_io_mgr->QueueRead(dt);

        // By this line, the file should be in the cache. Let's load it.
        load_preview_success = this->LoadPreview(dt->GetIdentifier());
        }
      }

    // Let's check if we have a URI. If so, download it.

    if (sourceUri.length())
      {
      // If another file instance is downloading the same file (since a
      // session can reference the same file multiple times), then use
      // the same transfer. This is not necessary for the preview code above
      // because it's loaded synchronously (i.e. by the time we reach
      // another instance that requires the same preview file, it will be
      // already loaded by the current instance and therefore available 
      // without the need for creating a new data transfer)

      vtkKWDataTransfer *dt = 
        remote_io_mgr->GetDataTransferByIdentifier(cachedFilename.c_str());
      if (dt)
        {
        this->DataTransferCollection->AddItem(dt);
        }
      else
        {
        dt = remote_io_mgr->CreateNewDataTransfer();
        if (dt)
          {
          remote_io_mgr->AddDataTransfer(dt);
          dt->Delete();
          dt->SetSourceURI(sourceUri.c_str());
          dt->SetDestinationURI(destinationUriAbsolute.c_str());
          dt->SetIdentifier(cachedFilename.c_str());
          dt->SetTransferType(vtkKWDataTransfer::RemoteDownload);

          this->DataTransferCollection->AddItem(dt);
          
          // If a preview URI has already been loaded, ie we have something to
          // show the user, for him to play with, we will download this on a 
          // seperate non-blocking thread. If no preview URI is present (or we
          // failed to download/load it), we will download this URI on the main
          // thread and make the user wait for it.
          dt->SetAsynchronous(load_preview_success == 0 ? 0 : 1);
          remote_io_mgr->QueueRead(dt);

          // By this line, the file should be in the cache, if it was 
          // downloaded on the main thread. If not, we will get it some time 
          // later.
          if (!dt->GetAsynchronous())
            {
            // Let's load it up.
            load_data_success = this->Load(dt->GetIdentifier());
            }
          }
        }
      }
    }

  return (load_data_success || load_preview_success) ? 1 : 0;
}

//----------------------------------------------------------------------------
int vtkVVFileInstance::HasDataTransfer(vtkKWDataTransfer *dt)
{
  vtkCollection *data_transfers = this->GetDataTransferCollection();
  if (data_transfers)
    {
    const int nb_transfers = data_transfers->GetNumberOfItems();
    for (int j = 0; j < nb_transfers; j++)
      {
      if (data_transfers->GetItemAsObject(j) == dt)
        {
        return 1;
        }
      }
    }
  return 0;
}

//----------------------------------------------------------------------------
void vtkVVFileInstance::CancelAllDataTransfers()
{
  vtkCollection *data_transfers = this->GetDataTransferCollection();
  if (data_transfers)
    {
    const int nb_transfers = data_transfers->GetNumberOfItems();
    for (int j = 0; j < nb_transfers; j++)
      {
      vtkKWDataTransfer *dt = vtkKWDataTransfer::SafeDownCast(
        data_transfers->GetItemAsObject(j));
      if (dt)
        {
        // Cancel the damn transfer
        }
      }
    data_transfers->RemoveAllItems();
    }
}

//----------------------------------------------------------------------------
int vtkVVFileInstance::Load()
{
  return this->Load(this->GetFileName());
}

//----------------------------------------------------------------------------
int vtkVVFileInstance::Load(const char *filename)
{
  // Note: validation is done in vtkVVFileInstance::LoadFromOpenWizard

  vtkKWOpenWizard *openwizard = vtkKWOpenWizard::New();
  openwizard->SetApplication(this->GetApplication());
  openwizard->SetFileName(filename);

  // If we have some open properties already, they were either unserialized
  // from a session, or set when we opened a file already. We *need* to
  // keep them, and prevent the open wizard from overwriting them with
  // any .vvi files that could be in the way.

  if (this->GetOpenFileProperties())
    {
    openwizard->SetIgnoreVVIOnRead(1);
    openwizard->SetIgnoreVVIOnWrite(1);
    openwizard->SetOpenWithCurrentOpenFileProperties(1);
    openwizard->GetOpenFileProperties()->DeepCopy(
      this->GetOpenFileProperties());
    }

  int success = 
    openwizard->Invoke(filename, vtkKWOpenWizard::INVOKE_QUIET) &&
    this->LoadFromOpenWizard(openwizard);
  openwizard->Delete();

  return success;
}

//----------------------------------------------------------------------------
int vtkVVFileInstance::LoadPreview(const char *filename)
{
  // Note: validation is done in vtkVVFileInstance::LoadFromOpenWizard

  vtkKWOpenWizard *openwizard = vtkKWOpenWizard::New();
  openwizard->SetApplication(this->GetApplication());
  openwizard->SetFileName(filename);

  // If we have some open properties already, they were either unserialized
  // from a session, or set when we opened a file already. HOWEVER, they
  // can't be used for a preview, which is usually a totally different
  // size and file format than the filename associated to this file instance.
  // The preview file format has to be self-contained: make sure we ignore
  // the .vvi files. 

  if (this->GetOpenFileProperties())
    {
    openwizard->SetIgnoreVVIOnRead(1);
    openwizard->SetIgnoreVVIOnWrite(1);
    }

  int success = 
    openwizard->Invoke(filename, vtkKWOpenWizard::INVOKE_QUIET) &&
    this->LoadFromOpenWizard(openwizard);
  openwizard->Delete();

  return success;
}

//----------------------------------------------------------------------------
int vtkVVFileInstance::LoadFromOpenWizard(vtkKWOpenWizard *openwizard)
{
  // IMPORTANT: note that openwizard is *not* Invoke()'ed, it is assumed
  // it was invoked previously (say, by Load()). Its last reader is used 
  // directly at this point.

  if (!openwizard)
    {
    vtkErrorMacro("Failed loading data, empty wizard!");
    return 0;
    }

  // File instance has no name? Use the filename. Considering that file
  // instances are usually stored in a vtkVVFileInstancePool and that a
  // file can be loaded twice with different open properties (leading to
  // two different file instances), one should really have set the file
  // instance name to something unique in the pool already. But just 
  // in case...

  if (!this->GetName())
    {
    this->SetName(openwizard->GetFileName());
    }

  // The file instance has no filenames, then use the open wizard's filename.

  if (!this->GetFileName())
    {
    this->SetFileName(openwizard->GetFileName());
    }

  // Sometimes file instance are created without an app. Fill that void.

  if (!this->GetApplication())
    {
    this->SetApplication(openwizard->GetApplication());
    }

  // Authenticate the file prior to loading it.

  vtkVVApplication * vvapp = 
    vtkVVApplication::SafeDownCast(this->GetApplication());
  if (vvapp)
    {
    vvapp->GetAuthenticator()->SetFileInstance(this);
    if (!vvapp->GetAuthenticator()->AuthenticateFile(
          openwizard->GetFileName()))
      {
      return 0;
      }
    }
  
  // Load and create data items by iterating over all output ports
  // and creating vtkVVDataItemVolume
  
  vtkAlgorithm *algorithm = vtkAlgorithm::SafeDownCast(
    openwizard->GetLastReader());
  int loaded = 0;
  int nb_output_ports = algorithm->GetNumberOfOutputPorts();
  for (int i = 0; i < nb_output_ports ; ++i)
    {
    vtkVVDataItemVolume *volume_data = vtkVVDataItemVolume::New();
    volume_data->SetApplication(this->GetApplication());
    if (!volume_data->GetName() && this->GetName())
      {
      volume_data->SetName(this->GetName());
      }
    volume_data->SetFileInstance(this);
    if (!volume_data->LoadFromOpenWizard(openwizard, i))
      {
      vtkErrorMacro(
        << " Failed loading data, can not add data to data pool! "
        << this->GetFileName() << ", port: " << i);
      }
    else
      {
      if (!volume_data->GetName())
        {
        volume_data->SetName(volume_data->GetDescriptiveName());
        }
      // Add the data item to the pool. This means that each file instance
      // keeps track of references of all the data items that were created
      // loading that file.
      // Note though that a reference to *all* data items created by *all*
      // files as well as created programmatically or by plug-ins is kept in
      // the vtkVVWindowBase::DataItemPool (for convenience)
      
      this->GetDataItemPool()->AddDataItem(volume_data);

      loaded++;
      }
    volume_data->Delete();
    }

  // Copy the whole open properties, so that it can be serialized later
  // on to a session (the same file could have been opened with different
  // parameters). Unless we have one already, which happens when we load
  // from a session.

  if (loaded && !this->GetOpenFileProperties())
    {
    vtkKWOpenFileProperties *open_prop = vtkKWOpenFileProperties::New();
    open_prop->DeepCopy(openwizard->GetOpenFileProperties());
    this->SetOpenFileProperties(open_prop);
    open_prop->Delete(); // it was ref counted
    }
  
  return loaded;
}

//----------------------------------------------------------------------------
int vtkVVFileInstance::HasRenderWidgetInWindow(vtkVVWindowBase *win)
{
  for (int i = 0; i < this->DataItemPool->GetNumberOfDataItems(); i++)
    {
    if (this->DataItemPool->GetNthDataItem(i)->HasRenderWidgetInWindow(win))
      {
      return 1;
      }
    }
  return 0;
}

//----------------------------------------------------------------------------
void vtkVVFileInstance::AddDefaultRenderWidgets(vtkVVWindowBase *win)
{
  if (!this->GetDataItemPool()->GetNumberOfDataItems())
    {
    this->Load();
    }

  for (int i = 0; i < this->DataItemPool->GetNumberOfDataItems(); i++)
    {
    if (!this->DataItemPool->GetNthDataItem(i)->HasRenderWidgetInWindow(win))
      {
      this->DataItemPool->GetNthDataItem(i)->AddDefaultRenderWidgets(win);
      }
    }
}

//----------------------------------------------------------------------------
void vtkVVFileInstance::RemoveDefaultRenderWidgets(vtkVVWindowBase *win)
{
  for (int i = 0; i < this->DataItemPool->GetNumberOfDataItems(); i++)
    {
    this->DataItemPool->GetNthDataItem(i)->RemoveDefaultRenderWidgets(win);
    }
}

//----------------------------------------------------------------------------
void vtkVVFileInstance::SetRelocationDirectory(const char *arg)
{
  if (!this->RelocationDirectory && !arg)
    {
    return;
    }
  if (this->RelocationDirectory && arg && 
      !strcmp(this->RelocationDirectory, arg))
    {
    return;
    }
  if (this->RelocationDirectory)
    {
    delete [] this->RelocationDirectory;
    }
  if (arg)
    {
    this->RelocationDirectory = new char [strlen(arg) + 1];
    strcpy(this->RelocationDirectory, arg);
    this->RelocateDeadFiles(this->RelocationDirectory);
    }
  else
    {
    this->RelocationDirectory = NULL;
    }

  this->Modified();
}

//----------------------------------------------------------------------------
int vtkVVFileInstance::RelocateDeadFiles(const char *relocation_dir)
{
  int nb_files = this->GetNumberOfFileNames();
  if (!nb_files || 
      !relocation_dir || 
      !vtksys::SystemTools::FileExists(relocation_dir))
    {
    return 0;
    }

  int nb_files_relocated = 0;
  vtksys_stl::string filename_found;

  // Let's *also* try in the parent, just to make sure we can recover
  // from Rick manually "editing" the damn' session files.

  vtksys_stl::string relocation_dir_parent(
    vtksys::SystemTools::GetFilenamePath(relocation_dir));

  vtkVVFileInstanceInternals::FileNamePoolIterator it = 
    this->Internals->FileNamePool.begin();
  vtkVVFileInstanceInternals::FileNamePoolIterator end = 
    this->Internals->FileNamePool.end();
  for (; it != end; ++it)
    {
    if (!vtksys::SystemTools::FileExists((*it).RelocatedPath.c_str()) &&
        (vtksys::SystemTools::LocateFileInDir(
          (*it).RelocatedPath.c_str(), relocation_dir, filename_found, 1) ||
         vtksys::SystemTools::LocateFileInDir(
           (*it).RelocatedPath.c_str(), 
           relocation_dir_parent.c_str(), filename_found, 1)))
      {
      (*it).OriginalPath = (*it).RelocatedPath;
      (*it).RelocatedPath = 
        vtksys::SystemTools::CollapseFullPath(filename_found.c_str());
      nb_files_relocated++;
      }
    }

  return nb_files_relocated;
}

//----------------------------------------------------------------------------
void vtkVVFileInstance::SetOpenFileProperties(vtkKWOpenFileProperties *arg)
{
  if (this->OpenFileProperties == arg)
    {
    return;
    }

  if (this->OpenFileProperties)
    {
    this->OpenFileProperties->UnRegister(this);
    }
    
  this->OpenFileProperties = arg;

  if (this->OpenFileProperties)
    {
    this->OpenFileProperties->Register(this);
    }

  this->Modified();
}

//----------------------------------------------------------------------------
int vtkVVFileInstance::HasSameOpenFileProperties(vtkVVFileInstance *instance)
{
  return (
    instance && 
    ((this->GetOpenFileProperties() == instance->GetOpenFileProperties()) ||
     (this->GetOpenFileProperties() &&
      this->GetOpenFileProperties()->IsEqual(
        instance->GetOpenFileProperties()))));
}

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

  vtkIndent next_indent = indent.GetNextIndent();

  os << indent << "Name: " 
     << (this->Name ? this->Name : "(NULL)") << endl;

  os << indent << "RelocationDirectory: " 
     << (this->RelocationDirectory ? this->RelocationDirectory : "(NULL)") 
     << endl;

  int nb_filenames = this->GetNumberOfFileNames();
  os << indent << "FileNames (" << this->GetNumberOfFileNames() 
     << ", displaying first and last):\n";
  
  if (nb_filenames)
    {
    os << next_indent << this->GetNthFileName(0) << "\n";
    if (nb_filenames > 1)
      {
      os << next_indent << "...\n";
      os << next_indent << this->GetNthFileName(nb_filenames - 1) << "\n";
      }
    }

  os << indent << "DataItemPool: " << this->DataItemPool << endl;
  if (this->DataItemPool)
    {
    this->DataItemPool->PrintSelf(os, next_indent);
    }
  else
    {
    os << "(none)\n";
    }
}
