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

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

#include "vtkCallbackCommand.h"
#include "vtkObjectFactory.h"
#include "vtkVVFileInstance.h"

#include <vtksys/stl/vector>
#include <vtksys/stl/string>
#include <vtksys/ios/sstream>

#include <time.h>
#include <stdio.h>
#include <string.h>

//---------------------------------------------------------------------------
vtkStandardNewMacro(vtkVVFileInstancePool);
vtkCxxRevisionMacro(vtkVVFileInstancePool, "$Revision: 1.13 $");

#ifdef KWCommonPro_USE_XML_RW
#include "XML/vtkXMLVVFileInstancePoolReader.h"
#include "XML/vtkXMLVVFileInstancePoolWriter.h"
#endif
vtkKWGetXMLReaderWriterObjectsImplementationMacro(vtkVVFileInstancePool, vtkXMLVVFileInstancePoolReader, vtkXMLVVFileInstancePoolWriter);

//----------------------------------------------------------------------------
class vtkVVFileInstancePoolInternals
{
public:

  // file instances

  typedef vtksys_stl::vector<vtkVVFileInstance*> FileInstancePoolType;
  typedef vtksys_stl::vector<vtkVVFileInstance*>::iterator FileInstancePoolIterator;
  FileInstancePoolType FileInstancePool;
};

//----------------------------------------------------------------------------
vtkVVFileInstancePool::vtkVVFileInstancePool()
{  
  this->Internals = new vtkVVFileInstancePoolInternals;
}

//----------------------------------------------------------------------------
vtkVVFileInstancePool::~vtkVVFileInstancePool()
{
  // Delete our pool

  if (this->Internals)
    {
    this->RemoveAllFileInstances(); // Note that this call this->Initialize()
    delete this->Internals;
    }
}

//----------------------------------------------------------------------------
int vtkVVFileInstancePool::GetNumberOfFileInstances()
{
  if (this->Internals)
    {
    return (int)this->Internals->FileInstancePool.size();
    }

  return 0;
}

//----------------------------------------------------------------------------
vtkVVFileInstance *vtkVVFileInstancePool::GetNthFileInstance(int i)
{
  // Get the ith file instance

  if (i < 0 || i >= this->GetNumberOfFileInstances() || !this->Internals)
    {
    vtkErrorMacro("Index out of range");
    return NULL;
    }
  
  return this->Internals->FileInstancePool[i];
}

//----------------------------------------------------------------------------
vtkVVFileInstance *vtkVVFileInstancePool::GetFileInstanceWithName(const char *name)
{
  // Get data item by name

  if (name)
    {
    vtkVVFileInstancePoolInternals::FileInstancePoolIterator it = 
      this->Internals->FileInstancePool.begin();
    vtkVVFileInstancePoolInternals::FileInstancePoolIterator end = 
      this->Internals->FileInstancePool.end();
    for (; it != end; ++it)
      {
      if ((*it)->GetName() && !strcmp((*it)->GetName(), name))
        {
        return (*it);
        }
      }
    }
  
  return NULL;
}

//----------------------------------------------------------------------------
int vtkVVFileInstancePool::HasFileInstance(vtkVVFileInstance *instance)
{
  if (instance)
    {
    vtkVVFileInstancePoolInternals::FileInstancePoolIterator it = 
      this->Internals->FileInstancePool.begin();
    vtkVVFileInstancePoolInternals::FileInstancePoolIterator end = 
      this->Internals->FileInstancePool.end();
    for (; it != end; ++it)
      {
      if ((*it) == instance)
        {
        return 1;
        }
      }
    }
  return 0;
}

//----------------------------------------------------------------------------
int vtkVVFileInstancePool::HasFileInstanceWithName(const char *name)
{
  return this->GetFileInstanceWithName(name) ? 1 : 0;
}

//----------------------------------------------------------------------------
int vtkVVFileInstancePool::GetIndexOfFileInstance(vtkVVFileInstance *instance)
{
  int r = 0;
  if (instance)
    {
    vtkVVFileInstancePoolInternals::FileInstancePoolIterator it = 
      this->Internals->FileInstancePool.begin();
    vtkVVFileInstancePoolInternals::FileInstancePoolIterator end = 
      this->Internals->FileInstancePool.end();
    for (; it != end; ++it)
      {
      if ((*it) == instance)
        {
        return r;
        }
      ++r;
      }
    }
  return -1;
}

//----------------------------------------------------------------------------
void vtkVVFileInstancePool::RemoveAllFileInstances()
{
  if (this->Internals)
    {
    // Inefficient but there is too many things to do in RemoveFileInstance,
    // let's not duplicate and go out of sync

    while (this->Internals->FileInstancePool.size())
      {
      this->RemoveFileInstance(
        (*this->Internals->FileInstancePool.begin()));
      }
    }
}

//----------------------------------------------------------------------------
void vtkVVFileInstancePool::RemoveFileInstance(vtkVVFileInstance *instance)
{
  if (!instance)
    {
    return;
    }

  // Remove file instance

  vtkObject *obj;

  vtkVVFileInstancePoolInternals::FileInstancePoolIterator it = 
    this->Internals->FileInstancePool.begin();
  vtkVVFileInstancePoolInternals::FileInstancePoolIterator end = 
    this->Internals->FileInstancePool.end();
  for (; it != end; ++it)
    {
    if ((*it) == instance)
      {
      obj = (vtkObject*)(*it);

      (*it)->UnLoad();
      (*it)->UnRegister(this);

      this->Internals->FileInstancePool.erase(it);
      break;
      }
    }
}

//----------------------------------------------------------------------------
int vtkVVFileInstancePool::AddFileInstance(vtkVVFileInstance *instance)
{
  if (!instance)
    {
    vtkErrorMacro("can not add NULL instance to pool!");
    return 0;
    }

  // Already in the pool ?

  if (this->HasFileInstance(instance))
    {
    vtkErrorMacro("The file instance is already in the pool!");
    return 0;
    }
  
  this->Internals->FileInstancePool.push_back(instance);
  instance->Register(this);

  return 1;
}

//----------------------------------------------------------------------------
int vtkVVFileInstancePool::GetNumberOfFileInstancesWithSameFileNames(
  vtkVVFileInstance *instance)
{
  int nb = 0;
  if (instance)
    {
    vtkVVFileInstancePoolInternals::FileInstancePoolIterator it = 
      this->Internals->FileInstancePool.begin();
    vtkVVFileInstancePoolInternals::FileInstancePoolIterator end = 
      this->Internals->FileInstancePool.end();
    for (; it != end; ++it)
      {
      if ((*it)->HasSameFileNames(instance))
        {
        ++nb;
        }
      }
    }
  
  return nb;
}

//----------------------------------------------------------------------------
vtkVVFileInstance *vtkVVFileInstancePool::GetNthFileInstanceWithSameFileNames(
  int i, vtkVVFileInstance *instance)
{
  if (instance && i >= 0)
    {
    vtkVVFileInstancePoolInternals::FileInstancePoolIterator it = 
      this->Internals->FileInstancePool.begin();
    vtkVVFileInstancePoolInternals::FileInstancePoolIterator end = 
      this->Internals->FileInstancePool.end();
    for (; it != end; ++it)
      {
      if ((*it)->HasSameFileNames(instance) && !i--)
        {
        return (*it);
        }
      }
    }
  
  return NULL;
}

//----------------------------------------------------------------------------
int vtkVVFileInstancePool::HasFileInstanceWithSameFileNames(
  vtkVVFileInstance *instance)
{
  return this->GetNthFileInstanceWithSameFileNames(0, instance) ? 1 : 0;
}

//----------------------------------------------------------------------------
int vtkVVFileInstancePool::GetIndexOfNthFileInstanceWithSameFileNames(
  int i, vtkVVFileInstance *instance)
{
  return this->GetIndexOfFileInstance(
    this->GetNthFileInstanceWithSameFileNames(i, instance));
}

//----------------------------------------------------------------------------
void vtkVVFileInstancePool::RemoveNthFileInstanceWithSameFileNames(
  int i, vtkVVFileInstance *instance)
{
  this->RemoveFileInstance(
    this->GetNthFileInstanceWithSameFileNames(i, instance));
}

//----------------------------------------------------------------------------
const char* vtkVVFileInstancePool::SuggestUniqueNameForFileInstance(
  vtkVVFileInstance *instance)
{
  static vtksys_stl::string unique_name;

  if (!instance)
    {
    return NULL;
    }

  // No file, use the class name and time

  if (!instance->GetNumberOfFileNames())
    {
    unique_name = instance->GetClassName();
    vtksys_ios::ostringstream unique_suffix;
    unique_suffix << " (" << clock() << ")";
    unique_name += unique_suffix.str();
    return unique_name.c_str();
    }

  // If that instance is the only one with the same filename(s), just use the
  // first filename.

  int nb_same = this->GetNumberOfFileInstancesWithSameFileNames(instance);
  if (!nb_same)
    {
    unique_name = instance->GetNthFileName(0);
    return unique_name.c_str();
    }

  // Find a candidate (not very optimized, right?)
  // Can't pick the last one directly, since file instances come and go
  // i.e. we could have: foo, foo (2), foo (3), then file is gone,
  // and after reloading yet another foo, nb_same would be too but foo (3)
  // is taken.

  int candidate_index = nb_same;

  // See if we can use the last one to find a better candidate (provided
  // that it was constructed with SuggestUniqueNameForFileInstance):
  // we would rather have the indices to be sequential.

  vtkVVFileInstance *last_inserted_instance(
    this->GetNthFileInstanceWithSameFileNames(nb_same - 1, instance));
  if (last_inserted_instance && last_inserted_instance->GetName())
    {
    const char *last_open_paren = 
      strrchr(last_inserted_instance->GetName(), '(');
    if (last_open_paren)
      {
      int better_candidate_index;
      if (sscanf(last_open_paren + 1, "%d", &better_candidate_index) == 1)
        {
        candidate_index = better_candidate_index;
        }
      }
    }

  do
    {
    ++candidate_index;
    unique_name = instance->GetNthFileName(0);
    vtksys_ios::ostringstream unique_suffix;
    unique_suffix << " (" << candidate_index << ")";
    unique_name += unique_suffix.str();
    } while (this->HasFileInstanceWithName(unique_name.c_str()));

  return unique_name.c_str();
}

//----------------------------------------------------------------------------
const char* vtkVVFileInstancePool::SuggestUniqueNameForFileInstanceWithFileName(
  const char *filename)
{
  static vtksys_stl::string unique_name;

  vtkVVFileInstance *dummy_file = vtkVVFileInstance::New();
  dummy_file->AddFileName(filename);
  const char *ptr = this->SuggestUniqueNameForFileInstance(dummy_file);
  dummy_file->Delete();
  if (!ptr)
    {
    return NULL;
    }
  unique_name = ptr;

  return unique_name.c_str();
}

//----------------------------------------------------------------------------
int vtkVVFileInstancePool::GetNumberOfSimilarFileInstances(
  vtkVVFileInstance *instance)
{
  int nb = 0;
  if (instance)
    {
    vtkVVFileInstancePoolInternals::FileInstancePoolIterator it = 
      this->Internals->FileInstancePool.begin();
    vtkVVFileInstancePoolInternals::FileInstancePoolIterator end = 
      this->Internals->FileInstancePool.end();
    for (; it != end; ++it)
      {
      if ((*it)->HasSameFileNames(instance) &&
          (*it)->HasSameOpenFileProperties(instance))
        {
        ++nb;
        }
      }
    }
  
  return nb;
}

//----------------------------------------------------------------------------
vtkVVFileInstance *vtkVVFileInstancePool::GetNthSimilarFileInstance(
  int i, vtkVVFileInstance *instance)
{
  if (instance && i >= 0)
    {
    vtkVVFileInstancePoolInternals::FileInstancePoolIterator it = 
      this->Internals->FileInstancePool.begin();
    vtkVVFileInstancePoolInternals::FileInstancePoolIterator end = 
      this->Internals->FileInstancePool.end();
    for (; it != end; ++it)
      {
      if ((*it)->HasSameFileNames(instance) &&
          (*it)->HasSameOpenFileProperties(instance) &&
          !i--)
        {
        return (*it);
        }
      }
    }
  
  return NULL;
}

//----------------------------------------------------------------------------
int vtkVVFileInstancePool::HasSimilarFileInstance(
  vtkVVFileInstance *instance)
{
  return this->GetNthSimilarFileInstance(0, instance) ? 1 : 0;
}

//----------------------------------------------------------------------------
int vtkVVFileInstancePool::GetIndexOfNthSimilarFileInstance(
  int i, vtkVVFileInstance *instance)
{
  return this->GetIndexOfFileInstance(
    this->GetNthSimilarFileInstance(i, instance));
}

//----------------------------------------------------------------------------
void vtkVVFileInstancePool::RemoveNthSimilarFileInstance(
  int i, vtkVVFileInstance *instance)
{
  this->RemoveFileInstance(
    this->GetNthSimilarFileInstance(i, instance));
}

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

  os << indent << "FileInstances (" << this->GetNumberOfFileInstances() << "):\n";
  indent = indent.GetNextIndent();
  for (int i = 0; i < this->GetNumberOfFileInstances(); i++)
    {
    vtkVVFileInstance *instance = this->GetNthFileInstance(i);
    os << indent << "FileInstance #" << i << ": " << instance << endl;
    instance->PrintSelf(os, indent);
    }
}
