#ifndef NAMED_H
#define NAMED_H
#include <map>
#include <vector>
#include <iostream>
#include "config.h"
// =====================================================================
//	description of named objects
//	J Schmidt CTU Prague
// =====================================================================
namespace hub {

class Name: public std::string {
public:
	Name (void) {}
	Name (const std::string& s): std::string(s) {}
	Name (const char* s):        std::string(s) {}
	Name& operator=(const std::string& s) { (std::string&)(*this) = s; return *this; }
	Name& operator=(const char* s)        { (std::string&)(*this) = s; return *this; }
//	Name& operator=(const std::string& s) { std::string::operator= (s); return *this; }
//	Name& operator=(const char* s)        { std::string::operator= (s); return *this; }

	//	composition methods - naming conventions etc.
	void cons (const char*, const char *, size_t);
	void cons (const char*, size_t);
	void cons (const std::string&, const char *, size_t);
	void cons (const std::string&, size_t);

	//	augmentation methods - unique identifiers
	void augment (size_t no = 1);
	void nextAugment (void);
};
// =====================================================================
//	interface of anything that has a name 
// ---------------------------------------------------------------------
class Named {
public:
    virtual const Name& name (void) const=0;
    virtual ~Named() {}
};
// =====================================================================
//	something that really stores the name
//	the constructor enforces that the name is known at constr time
// ---------------------------------------------------------------------
    class NameHaving: public Named {
protected:
    Name name_;
public:
    NameHaving (const std::string name): name_(name) {}
    const Name& name (void) const { return name_; }
    void rename (const std::string name) { name_ = name; } 
};
// =====================================================================
//	a thing that shares its name with another
// ---------------------------------------------------------------------
class NameSharing: public Named {
protected:
    const Named* ref_;
public:
    NameSharing (const Named* ref): ref_(ref) {}
	const Name& name (void) const { return ref_->name(); }
};
// =====================================================================
//	namespace for other named Things 
//	also has a (shared) name to produce a readable context
//	is case insensitive
// ---------------------------------------------------------------------
//	TO ANYONE tempted to change "stricmp" for something different:
//	please relax, have a beer or herbal tea and then read config.h.
//	When porting to a new platform, please use platform-specific macros
//	to define stricmp.
// ---------------------------------------------------------------------
								
class CaseComp {
     public:
		 bool operator()(Name const &first,Name const &second) const {
			 return stricmp(first.c_str(), second.c_str()) < 0;
    }
};

// ---------------------------------------------------------------------
template <class Thing> class NameSpace: public std::map < Name, Thing*, CaseComp> {
public:
    typedef typename std::map < Name, Thing*, CaseComp> NameMap;
    typedef typename std::map < Name, Thing*, CaseComp> :: iterator iterator;
    typedef typename std::map < Name, Thing*, CaseComp> :: value_type value_type;
    
	Thing* Find				(const std::string& name);

	Thing* FindOrCreate		(const std::string& name);
	template <class Special> 
	Special* FindOrCreate	(const std::string& name);

	Thing* Create			(const std::string& name);
	template <class Special> 
	Special* Create			(const std::string& name);

	Thing* CreateUnique		(const std::string& name);
	template <class Special> 
	Special* CreateUnique	(const std::string& name);

    bool Accept (Thing*);
};
// ==================== Find ================================================
template <class Thing> 
Thing* NameSpace <Thing> :: Find (const std::string& name) {
    typename NameMap :: iterator where = this->find (name);
    if (where == this->end()) return NULL;
    return where->second;
}

// ==================== FindOrCreate ========================================
template <class Thing>
Thing* NameSpace <Thing> :: FindOrCreate (const std::string& name) {
    std::pair <typename NameMap :: iterator, bool> search;
    search = this->insert (typename_ NameMap :: value_type (name, NULL));
    if (search.second) (*search.first).second = new Thing (name);
    return (*search.first).second; 
}

template <class Thing> template <class Special>
Special* NameSpace <Thing> :: FindOrCreate (const std::string& name) {
    std::pair <typename NameMap :: iterator, bool> search;
    search = this->insert (typename_ NameMap :: value_type (name, NULL));
    if (search.second) (*search.first).second = new Special (name);
    return dynamic_cast <Special*> ((*search.first).second); 
}

// ==================== Create ==============================================
template <class Thing> 
Thing* NameSpace <Thing> :: Create (const std::string& name) {
    std::pair <typename NameMap :: iterator, bool> search;
    search = this->insert (typename_ NameMap :: value_type (name, NULL));
    if (!search.second) return NULL;		// already there
    Thing* spec = new Thing (name);
    (*search.first).second = spec;
    return spec;
}

template <class Thing> template <class Special>
Special* NameSpace <Thing> :: Create (const std::string& name) {
    std::pair <typename NameMap :: iterator, bool> search;
    search = this->insert (typename_ NameMap :: value_type (name, NULL));
    if (!search.second) return NULL;		// already there
    Special* spec = new Special (name);
    (*search.first).second = spec;
    return spec;
}

// ==================== CreateUnique ========================================
template <class Thing> 
Thing* NameSpace <Thing> :: CreateUnique (const std::string& name) {
    std::pair <typename NameMap :: iterator, bool> search;
    search = this->insert (typename_ NameMap :: value_type (name, NULL));
	Thing* spec;
	if (search.second) {
		spec = new Thing (name);
	} else {		// already there
		Name unique = name;
		unique.augment();
		search = this->insert (typename_ NameMap :: value_type (unique, NULL));
		while (!search.second) {
			unique.nextAugment();
			search = this->insert (typename_ NameMap :: value_type (unique, NULL));
		}
		spec = new Thing (unique);
	}	
    (*search.first).second = spec;
    return spec;
}

template <class Thing>  template <class Special>
Special* NameSpace <Thing> :: CreateUnique (const std::string& name) {
    std::pair <typename NameMap :: iterator, bool> search;
    search = this->insert (typename_ NameMap :: value_type (name, NULL));
		Thing* spec;
	if (search.second) {
		spec = new Special (name);
	} else {		// already there
		Name unique = name;
		unique.augment();
		search = this->insert (typename_ NameMap :: value_type (unique, NULL));
		while (!search.second) {
			unique.nextAugment();
			search = this->insert (typename_ NameMap :: value_type (unique, NULL));
		}
		spec = new Special (unique);
	}	
    (*search.first).second = spec;
    return spec;
}

template <class Thing>
bool NameSpace <Thing> :: Accept (Thing* thing) {
    std::pair <typename NameMap :: iterator, bool> search;
    search = this->insert (typename_ NameMap :: value_type (thing->name(), thing));
    return search.second;
}
// =====================================================================
//	interface of anything that has a number
// ---------------------------------------------------------------------
typedef size_t number_t;

class Numbered {
public:
    virtual number_t number (void) const=0;
    virtual ~Numbered() {}
};
// =====================================================================
//	something that really stores the number
//	the constructor enforces that the number isknown at constr time
// ---------------------------------------------------------------------
    class NumberHaving: public Numbered {
protected:
    number_t number_;
public:
    NumberHaving (number_t number): number_(number) {}
    number_t number (void) const { return number_; }
};


// ---------------------------------------------------------------------
template <class Thing> class NumberSpace: 
	public std::vector < Thing*> {
public:
    typedef std::vector < Thing*> NumberMap;

    Thing* Find (number_t number);
    void Insert (number_t number, Thing*);
    number_t  InsertNext (Thing*);
    void FindOrInsert (number_t number, Thing*);
};

template <class Thing> 
Thing* NumberSpace <Thing> :: Find (number_t number) {
    if (number >= this->size()) return NULL;
    return this->operator[] (number);
}

template <class Thing> 
void NumberSpace <Thing> :: Insert (number_t number, Thing* that) {
    if (number >= this->size()) this->resize (number+1);
    (*this) [number] = that;
}

template <class Thing> 
number_t NumberSpace <Thing> :: InsertNext (Thing* that) {
    typename NumberMap :: iterator where;
    where = this->push_back (that);
    return where - this->begin();
}

}
#endif
