#ifndef ENTITY_H
#define ENTITY_H
#include "named.h"
#include "attr.h"
#include "arch.h"


namespace hub {

class StructArch;
template <class Parent> class Port;
class Instance;
class Entity;

// =====================================================================
//	when Entity makes an Instance, 
//	its PortProtos get instantiated to Ports
// ---------------------------------------------------------------------
class PortProto: public NumberHaving, public NameHaving {
protected:
    AttrSet <PortProto, 32> flags_;
public:
    typedef AttrSet   <PortProto, 32> flagsType;
    typedef AttrIndex <PortProto>     indexType;

    PortProto (const std::string& name, number_t number, const AttrSet <PortProto, 32>& flags): 
    NumberHaving (number), NameHaving (name), flags_ (flags) {}
    virtual ~PortProto () {}
    bool predicate (const AttrIndex <PortProto>& i) const { return flags_.test(i); }
    bool predicate (const flagsType& iset) const { return iset.IsSubset (flags_); }
    const flagsType& flags (void) const { return flags_; }
    flagsType& flags (void) { return flags_; }

    static AttrUnique <PortProto> input;
    static AttrUnique <PortProto> output;

    static AttrSet <PortProto, 32> inDir;
    static AttrSet <PortProto, 32> outDir;
    static AttrSet <PortProto, 32> inOutDir;
    static AttrSet <PortProto, 32> noDir;
};

// =====================================================================
//	Interface prototype
//
// ---------------------------------------------------------------------
class InterfaceProto {
    friend class Entity;
    NameSpace <PortProto> byName_;
    NumberSpace <PortProto> byNumber_;
    
public:
    typedef NameSpace <PortProto> :: const_iterator PortProtoIter;
    PortProtoIter PortProtoBeg (void) { return byName_.begin(); }
    PortProtoIter PortProtoEnd (void) { return byName_.end(); }
    
    PortProto* FindPort (const std::string& name) { return byName_.Find (name); }
    PortProto* FindPort (number_t number)         { return byNumber_.Find (number); }
    PortProto* FindPort (number_t number, const PortProto::flagsType& dir);
    PortProto* FindPort (number_t number, const PortProto::indexType& ixx);
    number_t PortNum (void) 			  { return byNumber_.size(); }

    template <class Parent> 
    void Instantiate (Parent*);
};
// =====================================================================
//	Entity
//
// ---------------------------------------------------------------------
class Library;
class Entity: public NameHaving {
protected:
    NameSpace <Architecture>  architectures_;
    InterfaceProto interface_;
    Library* parent_;
    Architecture* current_;
    
public:
    void Parent (Library* lib) { parent_ = lib; }
    Library* Parent (void) { return parent_; }
    Entity (const std::string& name): NameHaving (name), parent_(NULL), current_(NULL) {}
    virtual ~Entity () {}

	// ---------------- architectures manipulation by name ---------------------
    Architecture* FindArch (const std::string& name) { 
		return architectures_.Find (name); 
    }
    Architecture* FindOrCreateArch (const std::string& name) { 
		Architecture* arch =  architectures_.FindOrCreate<Architecture> (name); 
		if (arch) arch->Parent (this);
		return arch;
    }
    Architecture* FindOrCreateArch (const std::string& name, ArchImpl*);
    
    // ---------------- architecture manipulation by attributes ----------------
    // Architecture* FindArch (const ArchImpl::flagsType);  

    template <class AImpl>
    Architecture* FindOrCreateArch (const std::string& name);  	// find first of that class
    template <class AImpl>
    Architecture* FindOrCreateArch (const std::string& name, const ArchImpl::flagsType);  

    template <class AImpl>
    Architecture* CreateArch (const std::string& name);

    // ---------------- current architecture ----------------------------------
    void CurrentArch (Architecture* a) { current_ = a; }
    Architecture* CurrentArch(void) { return current_; }
    
    // ---------------- architecture iterators --------------------------------
    typedef NameSpace <Architecture> :: const_iterator ArchIter;
    ArchIter ArchBeg (void) { return architectures_.begin (); }
    ArchIter ArchEnd (void) { return architectures_.end (); }
    number_t ArchNum (void) { return architectures_.size(); }

    // --------------- interface manipulation ---------------------------------
    PortProto* FindPort (const std::string& name) { 
	return interface_.FindPort (name); 
    }
    PortProto* FindPort (number_t number) { 
	return interface_.FindPort (number); 
    }
    PortProto* FindPort (number_t number, const PortProto::flagsType& dir) { 
	return interface_.FindPort (number, dir); 
    }
    PortProto* FindPort (number_t number, const PortProto::indexType& ixx) { 
	return interface_.FindPort (number, ixx); 
    }
    PortProto* CreateNextPort (const std::string& name, const PortProto::flagsType& dir = PortProto::noDir);

    typedef InterfaceProto :: PortProtoIter PortProtoIter;
    PortProtoIter PortProtoBeg (void) { return interface_.PortProtoBeg(); }
    PortProtoIter PortProtoEnd (void) { return interface_.PortProtoEnd(); }
    number_t PortNum (void) { return interface_.PortNum(); }

    // --------------- instantiation ----------------------------------
    Instance* Instantiate 
	(const std::string instName, 
	 StructArch* parentArch,
	 Architecture* currArch);
};
// --------------------------------------------------------------------
template <class Parent> 
void InterfaceProto :: Instantiate (Parent* parent) {
    parent->reserveIF (byNumber_.size());
    typename NumberSpace <PortProto> :: iterator ai;
    Port <Parent>* port;

    for (ai = byNumber_.begin(); ai != byNumber_.end(); ai++) {
	port = parent->CreatePort (*ai);
    }
}
// --------------------------------------------------------------------
template <class AImpl>
Architecture* Entity :: CreateArch (const std::string& name) { 
    Architecture* arch = architectures_.Create <Architecture> (name);
    if (!arch) return NULL;		// exists - cannot be replaced
    arch->Parent (this);
    arch->template Create <AImpl> ();
    return arch; 
}
// --------------------------------------------------------------------
template <class AImpl>
Architecture* Entity::FindOrCreateArch (const std::string& name) { 	// find first of that class
    ArchIter ai;
    AImpl* impl;
    for (ai = ArchBeg(); ai != ArchEnd(); ai++) {
	impl = dynamic_cast <AImpl*> ((ai->second)->Impl());
	if (impl) return impl->Parent();
    }
    return CreateArch <AImpl> (name);

}
// --------------------------------------------------------------------
template <class AImpl>
Architecture* Entity::FindOrCreateArch (const std::string& name, const ArchImpl::flagsType flags) {
    ArchIter ai;
    AImpl impl;
    for (ai = ArchBeg(); ai != ArchEnd(); ai++) {
	impl = dynamic_cast <AImpl*> ((ai->second)->Impl());	// was *ai->second ??
	if (impl) {
	    if (impl->superset(flags)) return impl->Parent();
	}    
    }
    return CreateArch <AImpl> (name);		// setting flags of the new architecture is MISSING
}
}
#endif
