/*
  CoreLinux++ 
  Copyright (C) 1999,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(__EQUIPMENTCOMPOSITE_HPP)
#include <EquipmentComposite.hpp>
#endif

#if   !defined(__MAP_HPP)
#include <Map.hpp>
#endif

#if   !defined(__SET_HPP)
#include <Set.hpp>
#endif

#if   !defined(__LIST_HPP)
#include <List.hpp>
#endif

#if   !defined(__CORELINUXITERATOR_HPP)
#include <CoreLinuxIterator.hpp>
#endif

using namespace corelinux;
using namespace std;

//
// We firewall each of the composite collections as the
// involvement of the Iterator, and by defintion, hiding the
// collection implementation. 
//

// A set which insures that there is only member of the same instance 
// allowed in our child collection.

CORELINUX_SET(  EquipmentPtr,  less<EquipmentPtr> ,  EquipmentList );

// A list of iterators that we pass out. We could use Set or
// Vector as well.

CORELINUX_LIST(  CORELINUX(Iterator)<EquipmentPtr>* ,  IteratorList );

// A map of our instance (key) to our equipment (children)

CORELINUX_MAP
   ( 
    EquipmentCompositePtr, 
    EquipmentListPtr, 
    less<EquipmentCompositePtr>,
    EquipmentMap
    );

// A map of our instance (key) to our iterators

CORELINUX_MAP
   ( 
      EquipmentCompositePtr, 
      IteratorListPtr, 
      less<EquipmentCompositePtr>,
      IteratorMap
   );

//
// Finally, we declare the maps globally to all
// composite instances.
//

static   EquipmentMap   gEquipment;
static   IteratorMap    gIterator;

//
// Default constructor, throws exception
//

EquipmentComposite::EquipmentComposite( void )
   throw(CompositeException)
   :
   Equipment()
{
   ;  // do nothing
}

//
// Constructor with a name. In the process we
// register ourselves in the maps of lists
//

EquipmentComposite::EquipmentComposite( NameCref aName )
   :
   Equipment( aName )
{
   gEquipment[EquipmentCompositePtr(this)] =  new EquipmentList;
   gIterator[EquipmentCompositePtr(this)] = new IteratorList ;
}

//
// Copy constructor. Same registration effort with the addition
// of copying in the references children.
//
EquipmentComposite::EquipmentComposite( EquipmentCompositeCref aRef )
   :
   Equipment( aRef )
{
   // Lookup the guests registered children

   EquipmentMapConstIterator guest
      ( 
         gEquipment.find( EquipmentCompositePtr(&aRef) ) 
      );

   // If we find it then construct our list with
   // a semi-deep copy of theirs.
   // Otherwise we just have an empty list
   // to start with

   if( guest != gEquipment.end() )
   {
      EquipmentListPtr  aPtr( new EquipmentList );
      (*aPtr) = *(*guest).second;
      gEquipment[EquipmentCompositePtr(this)] =  aPtr;
   }
   else
   {
      gEquipment[EquipmentCompositePtr(this)] =  new EquipmentList;
   }
   gIterator[EquipmentCompositePtr(this)] = new IteratorList ;
}

//
// Destructor. Our main effort is removing the 
// maps of our list collections.
//

EquipmentComposite::~EquipmentComposite( void )
{

   //
   // Clear out the IteratorList.
   // Because we are the factory for the iterators
   // we must delete them individually.
   //

   IteratorMapIterator  aIItr( gIterator.find(this) );

   if( aIItr != gIterator.end() )
   {
      IteratorListIterator begin( (*aIItr).second->begin() );
      IteratorListIterator end( (*aIItr).second->end() );
      while( begin != end )
      {
         delete (*begin);
         ++begin;
      }
      delete (*aIItr).second;
      gIterator.erase(aIItr);
   }
   else
   {
      ;  // do nothing
   }

   //
   // Empty the children.
   //

   EquipmentMapIterator aEItr( gEquipment.find(this) );

   if( aEItr != gEquipment.end() )
   {
      delete (*aEItr).second;
      gEquipment.erase(aEItr);
   }
   else
   {
      ;  // do nothing
   }
}

//
// Equality operator. Depending on your requirements you may
// want to perform a memberwise comparison.
//

bool  EquipmentComposite::operator==( EquipmentCompositeCref aRef ) const
{
   return (Equipment::operator==(aRef));
}

//
// Assignment operator. This throws an exception, 
// 

EquipmentCompositeRef EquipmentComposite::operator=
   ( 
      EquipmentCompositeCref aRef 
   ) throw(CompositeException)
{
   Equipment::operator=(aRef);
   return (*this);
}

//
// Get power should total the childrens Watt requirement
//

Watt EquipmentComposite::getPower( void )
{
   Watt  aWattTotal(0);

   //
   // We use our own iterator capability
   // to collect the total wattage
   //

   Iterator<EquipmentPtr> *aIterator( this->createIterator() );

   CHECK( aIterator != NULLPTR );

   while( aIterator->isValid() )
   {
      aWattTotal += aIterator->getElement()->getPower();
      aIterator->setNext();
   }

   this->destroyIterator( aIterator );

   return aWattTotal;
}

//
// Add a child component to our children list. We make
// sure that the instance is not already a child. We
// could also use the iterator return
//

void  EquipmentComposite::addComponent( EquipmentPtr aPtr ) throw(CORELINUX(InvalidCompositeException))
{
   REQUIRE( aPtr != NULLPTR );

   //
   // Look for the registered list and add the
   // pointer. The result of a map insert is
   // that the returned pair second member is
   // true if successful, false if the value
   // is not unique.
   //

   EquipmentMapIterator aEItr( gEquipment.find(this) );

   CHECK( aEItr != gEquipment.end() );

   pair<EquipmentListIterator,bool> 
      aResult = (*aEItr).second->insert( aPtr ) ;

   CHECK( aResult.second == true );
}

//
// After insuring that the child exists in the list,
// we remove it. We use assertions to indicate a real
// problem. 
// We use a series of assertions during development to
// flush out any problems.
//

void  EquipmentComposite::removeComponent( EquipmentPtr aPtr ) throw(CORELINUX(InvalidCompositeException))
{
   REQUIRE( aPtr != NULLPTR );

   //
   // We find our registered children
   //

   EquipmentMapIterator    aEItr( gEquipment.find(this) );

   CHECK( aEItr != gEquipment.end() );

   //
   // We find the target to be removed.
   //

   EquipmentListIterator   aCItr( (*aEItr).second->find(aPtr) );

   CHECK( aCItr != (*aEItr).second->end() );

   (*aEItr).second->erase(aCItr);

}

//
// We use a CoreLinuxIterator as it works with the STL
// collections of C++. We also register the new iterator
// so we can verify ownership when it is returned. 
// We use a series of assertions during development to
// flush out any problems.
//

Iterator<EquipmentPtr> *EquipmentComposite::createIterator( void ) throw(CORELINUX(InvalidCompositeException))
{
   EquipmentMapIterator    aEItr( gEquipment.find(this) );

   CHECK( aEItr != gEquipment.end() );

   //
   // Create the iterator with our children set
   //

   Iterator<EquipmentPtr> *aPtr =
      new CoreLinuxIterator<EquipmentListIterator,EquipmentPtr>
         (
            (*aEItr).second->begin() ,
            (*aEItr).second->end() 
         );

   CHECK( aPtr != NULLPTR );

   //
   // We register the iterator as being "loaned" out
   // by this composite.
   //

   IteratorMapIterator     aIItr( gIterator.find(this) );

   CHECK( aIItr != gIterator.end() );

   (*aIItr).second->push_back(aPtr);

   return aPtr;
}

//
// We are asked to destroy the iterator we have created.
// Of course, we check that the iterator belongs to us
// by validating it against our list of "loaned" iterators.
//

void  EquipmentComposite::destroyIterator( Iterator<EquipmentPtr> *aPtr ) throw(CORELINUX(InvalidCompositeException))
{
   REQUIRE( aPtr != NULLPTR );

   IteratorMapIterator     aIItr( gIterator.find(this) );

   CHECK( aIItr != gIterator.end() );

   IteratorListIterator    begin( (*aIItr).second->begin() );
   IteratorListIterator    end( (*aIItr).second->end() );

   while( begin != end )
   {
      if( (*begin) == aPtr )
      {
         (*aIItr).second->erase(begin);
         delete aPtr;
         begin = end;
      }
      else
      {
         ++begin;
      }
   }
}

/*
   Common rcs information do not modify
   $Author: prudhomm $
   $Revision: 1.2 $
   $Date: 2000/08/31 22:49:01 $
   $Locker:  $
*/



