/*=========================================================================
 *
 *  Copyright Insight Software Consortium
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *=========================================================================*/
#ifndef __itkObjectStore_hxx
#define __itkObjectStore_hxx

#include "itkObjectStore.h"

namespace itk
{
template< typename TObjectType >
ObjectStore< TObjectType >
::ObjectStore()
{
  m_Size  = 0;
  m_LinearGrowthSize = 1024;
  m_GrowthStrategy = EXPONENTIAL_GROWTH;
}

template< typename TObjectType >
ObjectStore< TObjectType >
::~ObjectStore()
{
  this->Clear();
}

template< typename TObjectType >
void
ObjectStore< TObjectType >
::Reserve(SizeValueType n)
{
  // No need to grow? Do nothing.
  if ( n <= m_Size ) { return; }

  // Need to grow.  Allocate a new block of memory and copy that block's
  // pointers into the m_FreeList.  Updates m_Size appropriately.
  MemoryBlock new_block(n - m_Size);
  m_Store.push_back(new_block);

  m_FreeList.reserve(n);
  for ( ObjectType *ptr = new_block.Begin;
        ptr < new_block.Begin + new_block.Size; ptr++ )
    {
    m_FreeList.push_back(ptr);
    }
  m_Size += ( n - m_Size );
}

template< typename TObjectType >
typename ObjectStore< TObjectType >::ObjectType *
ObjectStore< TObjectType >
::Borrow()
{
  ObjectType *p;

  if ( m_FreeList.empty() ) // must allocate more memory
    {
    this->Reserve( m_Size + this->GetGrowthSize() );
    }
  p = m_FreeList.back();
  m_FreeList.pop_back();
  return p;

  //To do: This method will need to decrement counters in the memory blocks so
  //that blocks know when they can be deleted.
}

template< typename TObjectType >
void
ObjectStore< TObjectType >
::Return(ObjectType *p)
{
  // For speed, does no checking.
  m_FreeList.push_back(p);

  //  if ( m_FreeList.size() != m_Size )
  //    {  m_FreeList.push_back(p); }
  // else object has been used incorrectly

  // To do:
  // Eventually this method will have to increment counters in the memory
  // blocks so that blocks know when they can be deleted.  This will also allow
  // for checking to see if p actually belongs to this store.
  //
  // Problems could easily result if a pointer is Returned more than
  // once. Only time-wise efficient way to deal with this would be a flag in
  // the allocated memory block for each object, sort of how malloc deals with
  // it?
}

template< typename TObjectType >
SizeValueType
ObjectStore< TObjectType >
::GetGrowthSize()
{
  if( (m_GrowthStrategy == EXPONENTIAL_GROWTH) && ( m_Size != 0 ) )
    {
    return m_Size;
    }
  return m_LinearGrowthSize;
}

template< typename TObjectType >
void
ObjectStore< TObjectType >
::Squeeze()
{
  // Not implemented yet.
}

template< typename TObjectType >
void
ObjectStore< TObjectType >
::Clear()
{
  // Clear the free list.
  m_FreeList.clear();

  // Empty the MemoryBlock list and deallocate all memory blocks.
  while ( !m_Store.empty() )
    {
    m_Store.back().Delete();
    m_Store.pop_back();
    }
  m_Size = 0;
}

template< typename TObjectType >
void
ObjectStore< TObjectType >
::PrintSelf(std::ostream & os, Indent indent) const
{
  Superclass::PrintSelf(os, indent);

  os << indent << "m_GrowthStrategy: " << m_GrowthStrategy << std::endl;
  os << indent << "m_Size: " << m_Size << std::endl;
  os << indent << "m_LinearGrowthSize: " << static_cast< SizeValueType >( m_LinearGrowthSize ) << std::endl;
  os << indent << "Free list size: " << static_cast< SizeValueType >( m_FreeList.size() ) << std::endl;
  os << indent << "Free list capacity: " << static_cast< SizeValueType >( m_FreeList.capacity() ) << std::endl;
  os << indent << "Number of blocks in store: " << static_cast< SizeValueType >( m_Store.size() ) << std::endl;
}
} // end namespace itk

#endif
