/*
  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(__VECTOR_HPP)
#include <Vector.hpp>
#endif

#if   !defined(__MAZEFACTORY_HPP)
#include <MazeFactory.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;

//
// Constant Identifiers for our domain
//

const    NameIdentifier doorName("Door");
const    NameIdentifier roomName("Room");
const    NameIdentifier wallName("Wall");

//
// 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());
}

//
// Constructor - Setup the allocators
//

MazeFactory::MazeFactory( void )
   :
   AbstractFactory<NameIdentifier>()
{
   addAllocator(doorName,new DoorAllocator);
   addAllocator(roomName,new RoomAllocator);
   addAllocator(wallName,new WallAllocator);
}

//
// Copy constructor
//

MazeFactory::MazeFactory( MazeFactoryCref aRef )
   :
   AbstractFactory<NameIdentifier>( aRef )
{
   CollectionIterator *aItr( aRef.createAssociativeIterator() );

   //
   // Iterate over references collection and duplicate what
   // we are able to given our domain
   //

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

      if( aName == doorName )
      {
         addAllocator
            ( 
               aName, 
               castDownIteratorElement<DoorAllocatorPtr>(aItr) 
            );
      }
      else if( aName == roomName )
      {
         addAllocator
            ( 
               aName, 
               castDownIteratorElement<RoomAllocatorPtr>(aItr) 
            );
      }
      else if( aName == wallName )
      {
         addAllocator
            ( 
               aName, 
               castDownIteratorElement<WallAllocatorPtr>(aItr) 
            );
      }
      else
      {
         NEVER_GET_HERE;
      }
      aItr->setNext();
   }
   aRef.destroyAssociativeIterator( aItr );
}

//
// Destructor
//

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

//
// Assignment 
//

MazeFactoryRef MazeFactory::operator=( MazeFactoryCref aRef ) 
                  throw(Exception)
{
   throw Exception("Factory assignment not allowed!",LOCATION);
   return (*this);
}

//
// Equality operator
//

bool  MazeFactory::operator==( MazeFactoryCref 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 MazeFactory::getAllocatorCount( void ) const
{
   return Count( theAllocators.size() );
}

// Return some instrumentation for this Factory

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

// Return some instrumentation for this Factory

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

// Create a Room

RoomPtr MazeFactory::createRoom( RoomNumberCref aRef ) const
                        throw(AllocatorNotFoundException) 
{
   RoomAllocatorPtr  aRoomAllocator
      ( 
         castDownAllocator<RoomAllocatorPtr>( getAllocator( roomName ) )
      );

   RoomPtr  aRoom( aRoomAllocator->createType() );

   aRoom->setRoomNumber( aRef );

   return aRoom;
}

// Create a Wall

WallPtr MazeFactory::createWall( void ) const
                        throw(AllocatorNotFoundException) 
{
   WallAllocatorPtr  aWallAllocator
      ( 
         castDownAllocator<WallAllocatorPtr>( getAllocator( wallName ) )
      );

   return aWallAllocator->createType();
}

// Create a door joining two rooms

DoorPtr MazeFactory::createDoor( RoomPtr aFirst, RoomPtr aSecond ) const
                              throw(AllocatorNotFoundException) 
{
   DoorAllocatorPtr  aDoorAllocator
      ( 
         castDownAllocator<DoorAllocatorPtr>( getAllocator( doorName ) )
      );

   DoorPtr  aDoor( aDoorAllocator->createType() );

   aDoor->setFirstRoom( aFirst );
   aDoor->setSecondRoom( aSecond );

   return aDoor;
}

// Destroy a Room

void MazeFactory::destroyRoom( RoomPtr aPtr ) const
                              throw(AllocatorNotFoundException) 
{
   RoomAllocatorPtr  aRoomAllocator
      ( 
         castDownAllocator<RoomAllocatorPtr>( getAllocator( roomName ) )
      );

   aRoomAllocator->destroyType( aPtr );
}

// Destroy a Wall

void MazeFactory::destroyWall( WallPtr aPtr ) const
                              throw(AllocatorNotFoundException) 
{
   WallAllocatorPtr  aWallAllocator
      ( 
         castDownAllocator<WallAllocatorPtr>( getAllocator( wallName ) )
      );

   aWallAllocator->destroyType( aPtr );
}

// Destroy a Door

void MazeFactory::destroyDoor( DoorPtr aPtr ) const
                              throw(AllocatorNotFoundException) 
{
   DoorAllocatorPtr  aDoorAllocator
      ( 
         castDownAllocator<DoorAllocatorPtr>( getAllocator( doorName ) )
      );

   aDoorAllocator->destroyType( aPtr );
}

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

Count MazeFactory::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 MazeFactory::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   MazeFactory::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  MazeFactory::addAllocator( NameIdentifier aName, AllocatorPtr aPtr )
                        throw(AllocatorAlreadyExistsException)
{
   REQUIRE( aPtr != NULLPTR );
   NamedAllocatorsIterator  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   MazeFactory::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 *MazeFactory::createIterator( void ) const
{
   return new ImplementationIterator
                  ( 
                     theAllocators.begin(), 
                     theAllocators.end() 
                  );
}

//
// Destroy the AllocatorIterator
//

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

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

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

//
// Destroy the CollectionIterator
//

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

void  MazeFactory::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.2 $
   $Date: 2000/10/07 12:35:23 $
   $Locker:  $
*/


