/*
  CoreLinux++ 
  Copyright (C) 2000 CoreLinux Consortium
  
   The CoreLinux++ Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.

   The CoreLinux++ Library Library is distributed in the hope that it will 
   be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public
   License along with the GNU C Library; see the file COPYING.LIB.  If not,
   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.  
*/   


#if   !defined(__COMMON_HPP)
#include <Common.hpp>
#endif

#if   !defined(__ABSTRACTALLOCATOR_HPP)
#include <AbstractAllocator.hpp>
#endif

#if   !defined(__CORELINUXASSOCIATIVEITERATOR_HPP)
#include <CoreLinuxAssociativeIterator.hpp>
#endif

#if   !defined(__MAZEBUILDERFACTORY_HPP)
#include <MazeBuilderFactory.hpp>
#endif

#if   !defined(__DOOR_HPP)
#include <Door.hpp>
#endif

#if   !defined(__ROOM_HPP)
#include <Room.hpp>
#endif

#if   !defined(__WALL_HPP)
#include <Wall.hpp>
#endif

using namespace corelinux;

//
// Use default Allocators. Calls ::new and ::delete
//

CORELINUX_DEFAULT_ALLOCATOR( DoorAllocator, Door );
CORELINUX_DEFAULT_ALLOCATOR( RoomAllocator, Room );
CORELINUX_DEFAULT_ALLOCATOR( WallAllocator, Wall );

//
// Typedefs for readability
//

typedef  Iterator
            <
               AllocatorPtr
            > AllocatorsIterator;

typedef  AssociativeIterator
            <
               NameIdentifier,
               AllocatorPtr
            > CollectionIterator;

typedef  CoreLinuxAssociativeIterator
            <
               NamedAllocatorsConstIterator,
               NameIdentifier,
               AllocatorPtr
            > ImplementationIterator;

//
// Ease of use template methods to cast allocator
//

template <class AllocatorType>
   AllocatorType  castDownAllocator( AllocatorPtr aPtr )
{
   return dynamic_cast<AllocatorType>(aPtr);
}

template <class AllocatorType>
   AllocatorType  castDownIteratorElement( AllocatorsIterator *aPtr )
{
   return castDownAllocator<AllocatorType>(aPtr->getElement());
}


//
// Static data members
//

NameIdentifier MazeBuilderFactory::theDoorIdentifier("Door");
NameIdentifier MazeBuilderFactory::theRoomIdentifier("Room");
NameIdentifier MazeBuilderFactory::theWallIdentifier("Wall");


//
// Constructor - Setup the allocators
//

MazeBuilderFactory::MazeBuilderFactory( void )
   :
   AbstractFactory<NameIdentifier>()
{
   addAllocator(theDoorIdentifier,new DoorAllocator);
   addAllocator(theRoomIdentifier,new RoomAllocator);
   addAllocator(theWallIdentifier,new WallAllocator);
}

//
// Copy constructor
//

MazeBuilderFactory::MazeBuilderFactory( MazeBuilderFactoryCref aRef )
   :
   AbstractFactory<NameIdentifier>( aRef )
{
   CollectionIterator *aItr( aRef.createAssociativeIterator() );

   //
   // Iterate over references collection and duplicate what
   // we are able to given our domain. If the allocator
   // key does not match one of ours, we throw an assertion.
   //

   while( aItr->isValid() )
   {
      NameIdentifier    aName( aItr->getKey() );

      if( aName == theDoorIdentifier )
      {
         addAllocator( aName, aItr->getElement() );
      }
      else if( aName == theRoomIdentifier )
      {
         addAllocator( aName, aItr->getElement() );
      }
      else if( aName == theWallIdentifier )
      {
         addAllocator( aName, aItr->getElement() );
      }
      else
      {
         NEVER_GET_HERE;
      }
      aItr->setNext();
   }
   aRef.destroyAssociativeIterator( aItr );
}

//
// Destructor
//

MazeBuilderFactory::~MazeBuilderFactory( void )
{
   try
   {
      flushAllocators();
   }
   catch( ... )
   {
      ;  // do NOT rethrow during destructor
   }
}

//
// Assignment 
//

MazeBuilderFactoryRef MazeBuilderFactory::operator=( MazeBuilderFactoryCref aRef ) 
                  throw(Exception)
{
   throw Exception("Factory assignment not allowed!",LOCATION);
   return (*this);
}

//
// Equality operator
//

bool  MazeBuilderFactory::operator==( MazeBuilderFactoryCref aRef ) const
{
   CollectionIterator   *aItr( aRef.createAssociativeIterator() );
   bool           isSame( true );

   while( aItr->isValid() && isSame == true )
   {
      if( getAllocator( aItr->getKey() ) == NULLPTR )
      {
         isSame = false;
      }
      else
      {
         aItr->setNext();
      }
   }

   aRef.destroyAssociativeIterator( aItr );

   return isSame;
}

// Return number of allocators registered

Count MazeBuilderFactory::getAllocatorCount( void ) const
{
   return Count( theAllocators.size() );
}

// Return some instrumentation for this Factory

Count MazeBuilderFactory::getTotalAllocates( void ) const
{
   return getCreateCount();
}

// Return some instrumentation for this Factory

Count MazeBuilderFactory::getTotalDeallocates( void ) const
{
   return getDestroyCount();
}

// Return the Room Allocator Key

NameIdentifierCref   MazeBuilderFactory::getRoomIdentifier( void )
{
   return theRoomIdentifier;
}

// Return the Door Allocator Key

NameIdentifierCref   MazeBuilderFactory::getDoorIdentifier( void )
{
   return theDoorIdentifier;
}

// Return the Wall Allocator Key

NameIdentifierCref   MazeBuilderFactory::getWallIdentifier( void )
{
   return theWallIdentifier;
}

// Create a Maze Part

MapSitePtr MazeBuilderFactory::createPart( NameIdentifierRef aName ) const
                        throw(AllocatorNotFoundException) 
{
   AbstractAllocator<MapSite> *aAllocator
      ( 
         ( AbstractAllocator<MapSite> * )getAllocator( aName )
      );

   return aAllocator->createType();
}

// Destroy a Maze Part

void MazeBuilderFactory::destroyPart
   ( 
      NameIdentifierRef aName, 
      MapSitePtr aPtr 
   ) const throw(AllocatorNotFoundException) 
{
   AbstractAllocator<MapSite> *aAllocator
      ( 
         ( AbstractAllocator<MapSite> * )getAllocator( aName )
      );

   aAllocator->destroyType( aPtr );
}

//
// Sets a allocator for a particular type, removing
// the old one and returning it to the caller.
//

AllocatorPtr   MazeBuilderFactory::setAllocator
   ( 
      NameIdentifierRef aName,
      AllocatorPtr aNewAllocator
   )
{
   AllocatorPtr   aOldAllocator( removeAllocator(aName) );
   addAllocator( aName, aNewAllocator );
   return aOldAllocator;
}

//
// Get the total number of allocator allocates
//

Count MazeBuilderFactory::getCreateCount( void ) const
{
   Count                aCount( 0 );
   AllocatorsIterator   *aItr( this->createIterator() );

   while( aItr->isValid() )
   {
      aCount += aItr->getElement()->getAllocateCount();
      aItr->setNext();
   }

   this->destroyIterator( aItr );

   return aCount;
}

//
// Get the total number of allocator deallocates
//

Count MazeBuilderFactory::getDestroyCount( void ) const
{
   Count                aCount( 0 );
   AllocatorsIterator   *aItr( this->createIterator() );

   while( aItr->isValid() )
   {
      aCount += aItr->getElement()->getDeallocateCount();
      aItr->setNext();
   }

   this->destroyIterator( aItr );

   return aCount;
}

//
// Retrieve a allocator given an identifier
//

AllocatorPtr   MazeBuilderFactory::getAllocator( NameIdentifier aName ) const 
                              throw(AllocatorNotFoundException) 
{
   AllocatorPtr                  aPtr(NULLPTR);
   NamedAllocatorsConstIterator  fItr(theAllocators.find(aName));

   if( fItr != theAllocators.end() )
   {
      aPtr = (*fItr).second;
   }
   else
   {
      throw AllocatorNotFoundException( LOCATION );
   }

   return aPtr;
}

//
// Add a new allocator to the Factory
//

void  MazeBuilderFactory::addAllocator( NameIdentifier aName, AllocatorPtr aPtr )
                        throw(AllocatorAlreadyExistsException)
{
   REQUIRE( aPtr != NULLPTR );
   NamedAllocatorsConstIterator  fItr(theAllocators.find(aName));
   if( fItr == theAllocators.end() )
   {
      theAllocators.insert(NamedAllocators::value_type(aName,aPtr));
   }
   else
   {
      throw AllocatorAlreadyExistsException(LOCATION);
   }
}

//
// Remove a Allocator from the Factory and return it to the
// caller
//

AllocatorPtr   MazeBuilderFactory::removeAllocator( NameIdentifier aName )
                        throw(AllocatorNotFoundException)
{
   AllocatorPtr               aPtr( NULLPTR );
   NamedAllocatorsIterator    fItr( theAllocators.find(aName) );

   if( fItr != theAllocators.end() )
   {
      aPtr = (*fItr).second;
      theAllocators.erase( fItr );
   }
   else
   {
      throw AllocatorNotFoundException( LOCATION );
   }

   return aPtr;
}

//
// Create a iterator for the allocators
//

AllocatorsIterator *MazeBuilderFactory::createIterator( void ) const
{
   return new ImplementationIterator
                  ( 
                     theAllocators.begin(), 
                     theAllocators.end() 
                  );
}

//
// Destroy the AllocatorIterator
//

void  MazeBuilderFactory::destroyIterator( AllocatorsIterator *aPtr ) const
{
   REQUIRE( aPtr != NULLPTR );
   delete aPtr;
}

//
// Create a iterator over the association, which includes the key
// identifier
//

CollectionIterator *MazeBuilderFactory::createAssociativeIterator( void ) const
{
   return new ImplementationIterator
                  ( 
                     theAllocators.begin(), 
                     theAllocators.end() 
                  );
}

//
// Destroy the CollectionIterator
//

void  MazeBuilderFactory::destroyAssociativeIterator
   ( 
      CollectionIterator *aPtr 
   ) const
{
   REQUIRE( aPtr != NULLPTR );
   delete aPtr;
}
//
// Helper routine
//

void  MazeBuilderFactory::flushAllocators( void )
{
   NamedAllocatorsIterator begin( theAllocators.begin() );
   NamedAllocatorsIterator end( theAllocators.end() );

   while( begin != end )
   {
      delete (*begin).second;
      ++begin;
   }
   theAllocators.clear();
}

/*
   Common rcs information do not modify
   $Author: frankc $
   $Revision: 1.1 $
   $Date: 2000/04/21 02:38:47 $
   $Locker:  $
*/


