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

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

#include "vtkCallbackCommand.h"
#include "vtkImageData.h"
#include "vtkObjectFactory.h"
#include "vtkVVDataItem.h"

#include <vtksys/stl/vector>

#ifdef KWCommonPro_USE_XML_RW
#include "XML/vtkXMLVVDataItemPoolReader.h"
#include "XML/vtkXMLVVDataItemPoolWriter.h"
#endif
vtkKWGetXMLReaderWriterObjectsImplementationMacro(vtkVVDataItemPool, vtkXMLVVDataItemPoolReader, vtkXMLVVDataItemPoolWriter);

//---------------------------------------------------------------------------
vtkStandardNewMacro(vtkVVDataItemPool);
vtkCxxRevisionMacro(vtkVVDataItemPool, "$Revision: 1.3 $");

//----------------------------------------------------------------------------
class vtkVVDataItemPoolInternals
{
public:

  // Data items

  typedef vtksys_stl::vector<vtkVVDataItem*> DataItemPoolType;
  typedef vtksys_stl::vector<vtkVVDataItem*>::iterator DataItemPoolIterator;
  DataItemPoolType DataItemPool;
};

//----------------------------------------------------------------------------
vtkVVDataItemPool::vtkVVDataItemPool()
{  
  this->Internals = new vtkVVDataItemPoolInternals;

  this->Initialize();
}

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

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

//----------------------------------------------------------------------------
void vtkVVDataItemPool::Initialize()
{  
}

//----------------------------------------------------------------------------
int vtkVVDataItemPool::GetNumberOfDataItems()
{
  if (this->Internals)
    {
    return (int)this->Internals->DataItemPool.size();
    }

  return 0;
}

//----------------------------------------------------------------------------
vtkVVDataItem *vtkVVDataItemPool::GetNthDataItem(int i)
{
  // Get the ith data item

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

//----------------------------------------------------------------------------
vtkVVDataItem *vtkVVDataItemPool::GetDataItemWithName(const char *name)
{
  // Get data item by name

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

//----------------------------------------------------------------------------
int vtkVVDataItemPool::GetIndexOfDataItemWithName(const char *name)
{
  // return index of data item from a name
  int r = 0;
  if (name)
    {
    vtkVVDataItemPoolInternals::DataItemPoolIterator it = 
      this->Internals->DataItemPool.begin();
    vtkVVDataItemPoolInternals::DataItemPoolIterator end = 
      this->Internals->DataItemPool.end();
    for (; it != end; ++it)
      {
      if ((*it)->GetName() && !strcmp((*it)->GetName(), name))
        {
        return r;
        }
      ++r;
      }
    }
  
  return -1;
}

//----------------------------------------------------------------------------
int vtkVVDataItemPool::HasDataItemWithName(const char *name)
{
  return this->GetDataItemWithName(name) ? 1 : 0;
}

//----------------------------------------------------------------------------
int vtkVVDataItemPool::HasDataItem(vtkVVDataItem *data)
{
  if (data)
    {
    vtkVVDataItemPoolInternals::DataItemPoolIterator it = 
      this->Internals->DataItemPool.begin();
    vtkVVDataItemPoolInternals::DataItemPoolIterator end = 
      this->Internals->DataItemPool.end();
    for (; it != end; ++it)
      {
      if ((*it) == data)
        {
        return 1;
        }
      }
    }
  return 0;
}

//----------------------------------------------------------------------------
int vtkVVDataItemPool::GetIndexOfDataItem(vtkVVDataItem *data)
{
  int r = 0;
  if (data)
    {
    vtkVVDataItemPoolInternals::DataItemPoolIterator it = 
      this->Internals->DataItemPool.begin();
    vtkVVDataItemPoolInternals::DataItemPoolIterator end = 
      this->Internals->DataItemPool.end();
    for (; it != end; ++it)
      {
      if ((*it) == data)
        {
        return r;
        }
      ++r;
      }
    }
  return -1;
}

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

    while (this->Internals->DataItemPool.size())
      {
      this->RemoveDataItem(
        (*this->Internals->DataItemPool.begin()));
      }

    // As a convenience, re-initialize the container
    // We assume here that this method is used to actually close 
    // a project where multiple data items were loaded. In that
    // case we do not want to keep some values around to be applied
    // to the new data items that will be loaded (typically, the
    // cropping parameters)

    this->Initialize();
    }
}

//----------------------------------------------------------------------------
void vtkVVDataItemPool::RemoveDataItemWithName(const char *name)
{
  this->RemoveDataItem(this->GetDataItemWithName(name));
}

//----------------------------------------------------------------------------
void vtkVVDataItemPool::RemoveDataItem(vtkVVDataItem *data)
{
  if (!data)
    {
    return;
    }

  // Remove data item

  vtkObject *obj;

  vtkVVDataItemPoolInternals::DataItemPoolIterator it = 
    this->Internals->DataItemPool.begin();
  vtkVVDataItemPoolInternals::DataItemPoolIterator end = 
    this->Internals->DataItemPool.end();
  for (; it != end; ++it)
    {
    if ((*it) == data)
      {
      obj = (vtkObject*)(*it);

      this->RemoveCallbackCommandObserver(obj, vtkCommand::StartEvent);
      this->RemoveCallbackCommandObserver(obj, vtkCommand::EndEvent);
      this->RemoveCallbackCommandObserver(obj, vtkCommand::ProgressEvent);

      (*it)->UnRegister(this);

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

//----------------------------------------------------------------------------
int vtkVVDataItemPool::AddDataItem(vtkVVDataItem *data)
{
  if (!data)
    {
    vtkErrorMacro("can not add NULL data to pool!");
    return 0;
    }

  if (!data->GetName())
    {
    vtkErrorMacro("can not add data with no name!");
    return 0;
    }

  // Already in the pool ?

  if (this->HasDataItem(data))
    {
    vtkErrorMacro("The data item is already in the pool!");
    return 0;
    }
  
  this->Internals->DataItemPool.push_back(data);
  data->Register(this);

  // Forward the progress events from the data item to the data

  this->AddCallbackCommandObserver(data, vtkCommand::StartEvent);
  this->AddCallbackCommandObserver(data, vtkCommand::EndEvent);
  this->AddCallbackCommandObserver(data, vtkCommand::ProgressEvent);

  return 1;
}

//----------------------------------------------------------------------------
vtkCallbackCommand* vtkVVDataItemPool::GetCallbackCommand()
{
  vtkCallbackCommand *command = this->Superclass::GetCallbackCommand();
  if (command)
    {
    command->AbortFlagOnExecuteOn();
    }
  return command;
}

//----------------------------------------------------------------------------
void vtkVVDataItemPool::ProcessCallbackCommandEvents(vtkObject *caller,
                                                 unsigned long event,
                                                 void *calldata)
{
  // Propagate the progress event from all the data objects

  if (event == vtkCommand::StartEvent ||
      event == vtkCommand::EndEvent ||
      event == vtkCommand::ProgressEvent)
    {
    this->InvokeEvent(event, calldata);
    }

  this->Superclass::ProcessCallbackCommandEvents(caller, event, calldata);
}

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

  os << indent << "DataItems (" << this->GetNumberOfDataItems() << "):\n";
  indent = indent.GetNextIndent();
  for (int i = 0; i < this->GetNumberOfDataItems(); i++)
    {
    vtkVVDataItem *data = this->GetNthDataItem(i);
    os << indent << "DataItem #" << i << ": " << data << endl;
    data->PrintSelf(os, indent);
    }
}
