//
// .NAME:
//  Prototype
//
// .AUTHOR:
//  Gilberto Camara, Baudoin Raoult and Fernando Ii
//
// .SUMMARY:
//  Implements a template for the "prototype" pattern.
//
//  The "prototype" pattern is an extension of the "singleton"
//  pattern, for a hierarchy of classes which is instantiated
//  at runtime.
//
//  Each subclass of factory "registers" itself at compile time;
//  therefore, the addition of a new factory requires no change
//  the parent class.
//
// .CLIENTS:
//
//
//
// .RESPONSABILITIES:
//
//
//
//
// .COLLABORATORS:
//
//
//
// .BASE CLASS:
//
//
// .DERIVED CLASSES:
//
//
// .REFERENCES:
//  This technique is described in the article
//  "A New and Useful Template Technique: 'Traits'",
//  included in the book "C++ Gems"
//

#pragma once

#include <map>
#include <functional>

#include "Cached.h"

// Class Device Prototype - clones a new Device on Request

// struct BuilderTraits {
//	typedef Builder       Type;
//      static  Type&         DefaultObject();
// }


template <class Trait>
class Prototype
{
    typedef std::map<Cached, typename Trait::Type*, std::less<Cached> > Map;

    static Map* map_;

    Cached name_;

public:
    // -- Normal Constructor
    Prototype(const Cached& prototypeName, typename Trait::Type* proto);
    // -- Destructor
    virtual ~Prototype();

    // -- Virtual Constructor
    static typename Trait::Type& Find(const char*);
    static typename Trait::Type* Lookup(const char*);

    const Cached& Name() const { return name_; }
};

// Initialisation of static variable

template <class Trait>
typename Prototype<Trait>::Map* Prototype<Trait>::map_ = 0;

// Constructor

template <class Trait>
Prototype<Trait>::Prototype(const Cached& name, typename Trait::Type* proto) :
    name_(name)
{
    if (map_ == 0)
        map_ = new typename Prototype<Trait>::Map();

    (*map_)[name_] = proto;
}

// Destructor
template <class Trait>
Prototype<Trait>::~Prototype()
{
    map_->erase(name_);
}

// Virtual Constructor

template <class Trait>
typename Trait::Type&
Prototype<Trait>::Find(const char* subclassName)
{
    if (subclassName == 0)
        return Trait::DefaultObject();

    typename Map::iterator i = map_->find(subclassName);

    if (i == map_->end())
        return Trait::DefaultObject();

    typename Trait::Type* proto = (*i).second;

    return *proto;
}

template <class Trait>
typename Trait::Type*
Prototype<Trait>::Lookup(const char* subclassName)
{
    if (subclassName == 0)
        return Trait::DefaultObject();

    typename Map::iterator i = map_->find(subclassName);

    if (i == map_->end())
        return Trait::DefaultObject();

    typename Trait::Type* proto = (*i).second;

    return proto;
}
