// This may look like C code, but it is really -*- C++ -*-
// RTSS --- Railway Total System Simulator
// (c) TAKAGI Ryo (at Kogakuin University)
// Expandable.hh --- template classes SimpleExpandable & Expandable
// -----
// ChangeLog:
// 2007. 11. 19
//  Merged into the source of RTSS.
// 2004. 6. 5
//  Original date on the file --- OOMTS of Birmingham University
// -----

// -----
// Remark:
//  This file makes it easy to expand the program at a later date.
// Classes, structs and typedefs:
//  template <PP> class SimpleExpandable
//  template <PP, CA> class Expandable
// -----

#ifndef Expandable_HH
#define Expandable_HH

#include <cstdlib>
#include <string>
#include <vector>
#include <iostream>

using std :: vector ;

/******************************************************************
 * class SimpleExpandable
 ******************************************************************/

template < typename PP >
class SimpleExpandable
{

public :

  // Various classes needed
  class ObjectCreator
  {
  public :
    virtual PP * create () const = 0 ;
    void add_to_creators_list ( const char * ) ;
  } ;

  friend class ObjectCreator ;


  struct ObjectCreatorEntry
  {
    ObjectCreatorEntry
    ( const ObjectCreator * , const char * ) ;
    const ObjectCreator * creator ;
    std :: string object_type ;
  } ;

  class ObjectCreatorsList
    : public std :: vector < ObjectCreatorEntry >
    {
    public:
      ObjectCreatorsList ()
	: std :: vector < ObjectCreatorEntry > () {}
    } ;

private :

  static ObjectCreatorsList * & object_creators () ;

  // Add another object creator
  static void add_object_creator ( const ObjectCreatorEntry & ) ;

public :

  // Constructor
  SimpleExpandable () {} ;

  // Create object of type PP, return pointer
  static PP * create ( const char * ) ;
  static PP * create ( const std :: string & ) ;
} ;


/******************************************************************
 * class Expandable
 ******************************************************************/

template < typename PP , typename CA >
class Expandable
{

public :

  // Various classes needed
  class ObjectCreator
  {
  public :
    virtual PP * create () const = 0 ;
    virtual PP * create ( const CA & ) const = 0 ;
    void add_to_creators_list ( const char * ) ;
  } ;

  friend class ObjectCreator ;


  struct ObjectCreatorEntry
  {
    ObjectCreatorEntry
    ( const ObjectCreator * , const char * ) ;
    const ObjectCreator * creator ;
    std :: string object_type ;
  } ;

  class ObjectCreatorsList
    : public std :: vector < ObjectCreatorEntry >
    {
    public :
      ObjectCreatorsList ()
	: std :: vector < ObjectCreatorEntry > () {}
    } ;

private :

  static ObjectCreatorsList * & object_creators () ;

  // Add another object creator
  static void add_object_creator ( const ObjectCreatorEntry & ) ;

public :

  // Constructor
  Expandable () {} ;

  // Create object of type PP, return pointer
  static PP * create ( const char * ) ;
  static PP * create ( const std :: string & ) ;
  static PP * create ( const char * , const CA & ) ;
  static PP * create ( const std :: string & , const CA & ) ;
} ;


/******************************************************************
 *  Function definitions
 ******************************************************************/

template < typename PP >
typename SimpleExpandable < PP > :: ObjectCreatorsList * &
SimpleExpandable < PP > :: object_creators ()
{
  static typename SimpleExpandable < PP > :: ObjectCreatorsList *
    x = new typename SimpleExpandable < PP > :: ObjectCreatorsList ;
  return x ;
}



template < typename PP , typename CA >
typename Expandable < PP , CA > :: ObjectCreatorsList * &
Expandable < PP , CA > :: object_creators ()
{
  static typename Expandable < PP , CA > :: ObjectCreatorsList *
    x = new typename Expandable < PP , CA > :: ObjectCreatorsList ;
  return x ;
}



template < typename PP >
SimpleExpandable < PP > :: ObjectCreatorEntry :: ObjectCreatorEntry
( const typename SimpleExpandable < PP > :: ObjectCreator * creator_in ,
  const char * object_type_in )
{
  creator = creator_in ;
  object_type = object_type_in ;
}



template < typename PP , typename CA >
Expandable < PP , CA > :: ObjectCreatorEntry :: ObjectCreatorEntry
( const typename Expandable < PP , CA > :: ObjectCreator * creator_in ,
  const char * object_type_in )
{
  creator = creator_in ;
  object_type = object_type_in ;
}



template < typename PP >
PP *
SimpleExpandable < PP > :: create
( const std :: string & object_type_in )
{
  ObjectCreatorsList * creators
    = SimpleExpandable < PP > :: object_creators () ;
  for ( int i = 0 ; i < creators -> size () ; i ++ ) {
    std :: string & s_i = creators -> at ( i ) . object_type ;
    if ( s_i == object_type_in
	 && s_i . length () == object_type_in . length () )
      return creators -> at ( i ) . creator -> create ();
  }
  // ### We should consider throwing an error.
  std :: cerr << "Error: Creator for \"" << object_type_in << "\" not found."
              << std :: endl ;
  exit ( 1 ) ;
  return 0 ;
}



template < typename PP , typename CA >
PP *
Expandable < PP , CA > :: create
( const std :: string & object_type_in ,
  const CA & ca_in )
{
  ObjectCreatorsList * creators
    = Expandable < PP , CA > :: object_creators () ;
  for ( int i = 0 ; i < creators -> size () ; i ++ ) {
    std :: string & s_i = creators -> at ( i ) . object_type ;
    if ( s_i == object_type_in
	 && s_i . length () == object_type_in . length () )
      return creators -> at ( i ) . creator -> create ( ca_in ) ;
  }
  // ### We should consider throwing an error.
  std :: cerr << "Error: Creator for \"" << object_type_in << "\" not found."
              << std :: endl ;
  exit ( 1 ) ;
  return 0 ;
}



template < typename PP , typename CA >
PP *
Expandable < PP , CA > :: create
( const std :: string & object_type_in )
{
  ObjectCreatorsList * creators
    = Expandable < PP , CA > :: object_creators () ;
  for ( int i = 0 ; i < creators -> size () ; i ++ ) {
    std :: string & s_i = creators -> at ( i ) . object_type ;
    if ( s_i == object_type_in
	 && s_i . length () == object_type_in . length () )
      return creators -> at ( i ) . creator -> create ();
  }
  // ### We should consider throwing an error.
  std :: cerr << "Error: Creator for \"" << object_type_in << "\" not found."
              << std :: endl ;
  exit ( 1 ) ;
  return 0 ;
}



template < typename PP >
PP *
SimpleExpandable < PP > :: create
( const char * object_type_in )
{
  return create ( std :: string ( object_type_in ) ) ;
}



template < typename PP , typename CA >
PP *
Expandable < PP , CA > :: create
( const char * object_type_in ,
  const CA & ca_in )
{
  return create ( std :: string ( object_type_in ) , ca_in ) ;
}



template < typename PP , typename CA >
PP *
Expandable < PP , CA > :: create
( const char * object_type_in )
{
  return create ( std :: string ( object_type_in ) ) ;
}



template < typename PP >
void
SimpleExpandable < PP > :: add_object_creator
( const typename SimpleExpandable < PP > :: ObjectCreatorEntry &
  object_creator_in )
{
  ObjectCreatorsList * creators
    = SimpleExpandable < PP > :: object_creators () ;
  creators -> push_back ( object_creator_in ) ;
}



template < typename PP , typename CA >
void
Expandable < PP , CA > :: add_object_creator
( const typename Expandable < PP , CA > :: ObjectCreatorEntry &
  object_creator_in )
{
  ObjectCreatorsList * creators
    = Expandable < PP , CA > :: object_creators () ;
  creators -> push_back ( object_creator_in ) ;
}



template < typename PP >
void
SimpleExpandable < PP > :: ObjectCreator :: add_to_creators_list
( const char * object_type_in )
{
  SimpleExpandable < PP > :: add_object_creator
    ( ObjectCreatorEntry ( this , object_type_in ) ) ;
}


template < typename PP , typename CA >
void
Expandable < PP , CA > :: ObjectCreator :: add_to_creators_list
( const char * object_type_in )
{
  Expandable < PP , CA > :: add_object_creator
    ( ObjectCreatorEntry ( this , object_type_in ) ) ;
}

#endif	// Expandable_HH
