//
// .NAME:
//  Factory
//
// .AUTHOR:
//  Gilberto Camara, Baudoin Raoult and Fernando Ii
//
// .SUMMARY:
//  Implements a template for the "factory" pattern.
//
//  The "factory" pattern is a technique for creating new
//  instances of objects which defines an abstract interface,
//  (represented by the "Make" module). The subclasses of
//  factory decide which class to instantiate.
//
//  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 <functional>
#include <map>

#include "Cached.h"

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

// struct GETraits {
//	typedef GraphicEngine Type;
//	typedef MvRequest&    Parameter;
//      static  Cached        FactoryName  ( Parameter );
//      static  Type*         DefaultObject( Parameter );
// }


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

    static Map* map_;

    Cached name_;

    virtual typename Trait::Type* Build(typename Trait::Parameter) = 0;

public:
    // -- Normal Constructor

    Factory(const Cached& decoderName);
    virtual ~Factory();

    // -- Virtual Constructor
    static typename Trait::Type* Make(typename Trait::Parameter);
};

// Initialisation of static variable

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

// Constructor

template <class Trait>
Factory<Trait>::Factory(const Cached& name) :
    name_(name)
{
    if (map_ == 0)
        map_ = new typename Factory<Trait>::Map();

    // Put the object in the factory dictionary
    (*map_)[name_] = this;
}

// Destructor

template <class Trait>
Factory<Trait>::~Factory()
{
    // Remove the object from the factory dictionary
    map_->erase(name_);
}

// Virtual Constructor

template <class Trait>
typename Trait::Type*
Factory<Trait>::Make(typename Trait::Parameter param)
{
    // Retrieve the name of the factory
    const char* name = Trait::FactoryName(param);

    // No name ? Clone a default object
    if (name == 0)
        return Trait::DefaultObject(param);

    // try to find the name on the factory dictionary
    typename Map::iterator i = map_->find(name);

    // Not found ?  Clone a default object
    if (i == map_->end())
        return Trait::DefaultObject(param);

    // Create an object, based on the input parameters
    return (*i).second->Build(param);
}
