#ifndef STRUCTURAL_H
#define STRUCTURAL_H
#include "hub.h"
#include <list>
namespace hub {
// =====================================================================
//	anything that links to a net
// ---------------------------------------------------------------------
class PortProto;
class Net;
class Joinable {
public:
    virtual bool predicate (const AttrIndex <PortProto>& ) const  = 0;
    virtual const AttrSet <PortProto, 32>& flags (void) const =0;
    virtual void connect (Net* nat)=0;
    virtual const std::string& name (void) const=0;
    virtual ~Joinable ();
};
// =====================================================================
//	a port of an instance or architecture
// ---------------------------------------------------------------------
class Net;
template <class PortParent>
class Port: public Joinable, public Numbered, public Named {
protected:
    PortProto*  proto_;		// prototype of this port
    PortParent* parent_;	// parent instance or architecture
    Net*	net_;		// connected net - outside of this entity
public:
    Port (PortProto* proto, PortParent* inst): proto_(proto), parent_(inst), net_(NULL) {}
    const Name& name (void) const 							{ return proto_->name();}
    number_t number (void) const 							{ return proto_->number();}
    bool predicate (const AttrIndex <PortProto>& i) const	{ return proto_->predicate (i); }
    const PortProto :: flagsType& flags (void) const 		{ return proto_->flags(); }
    void connect (Net* net)									{ net_ = net; }
    void Parent (PortParent* parent)						{ parent_ = parent; }
    PortParent* Parent (void)								{ return parent_; }
};
// =====================================================================
//	Net
// ---------------------------------------------------------------------
class Net: public NameHaving {
protected:
    std::list<Joinable *> join_;
public:
    Net (const std::string& name): NameHaving (name) {}
    void Join (Joinable* port) { join_.push_back (port); port->connect (this); }
    typedef std::list<Joinable *> :: const_iterator JoinIter;
    JoinIter JoinBeg() { return join_.begin(); }
    JoinIter JoinEnd() { return join_.end(); }
    
};
// =====================================================================
//	Property
// ---------------------------------------------------------------------
class Property: public NameHaving {
    std::string value_;
public:   
    Property (const std::string& name, const std::string& value): 
	NameHaving(name), value_ (value) {}
};
// =====================================================================
//	Interface - a collection of Ports
// ---------------------------------------------------------------------
template <class Parent>
class Interface: public  NumberSpace <Port <Parent> > {
};
// =====================================================================
//	Instance
// ---------------------------------------------------------------------
class Instance: public NameHaving {
protected:
    Entity*			entity_;	// instantiated entity
    Interface <Instance>	interface_;	// port list
    Architecture*		architecture_;	// current architecture of the instance
    NameSpace <Property>	properties_;	// individual properties of the instance
    StructArch*			parent_;	// structural architecture impl.
    
public:
    Instance (const std::string& name, Entity* ent=NULL, Architecture* arch=NULL):
	NameHaving (name),
        entity_ (ent),
        architecture_ (arch)
        {}
    // --------------- properties --------------------------------------
    Property* CreateProperty (const std::string& name, const std::string& value);
    // --------------- ports -------------------------------------------
    Port <Instance>* FindPort (int number) { 
	return interface_.Find (number); 
    }
    Port <Instance>* FindPort (int number, const PortProto::flagsType& dir);
    Port <Instance>* FindPort (const std::string& name) { 
	return interface_.Find (entity_->FindPort(name)->number()); 
    }
    Port <Instance>* CreatePort (PortProto*);
    void reserveIF (size_t s) { interface_.reserve (s); }
    // -------------- parent arch implementation -----------------------
    StructArch* Parent (void) { return parent_; }
    void Parent (StructArch* archImpl) { parent_ = archImpl; }
    // ------------- target entity ------------------------------------
    Entity* entity (void) { return entity_; }
};
// =====================================================================
//	Structural architecture
//	has entity instances and nets
// ---------------------------------------------------------------------
class StructArch: public ArchImpl {
protected:
    NameSpace <Instance> instances_;
    NameSpace <Net> nets_;
    Interface <StructArch> interface_;
 
public:
    static const AttrUnique <Architecture> is;
    StructArch (void) { flags_.set(is); }
    virtual ~StructArch() {}
    // -------------- traversals ---------------------------------------
    typedef NameSpace <Instance> :: const_iterator InstIter;
    InstIter InstBeg (void) { return instances_.begin(); }
    InstIter InstEnd (void) { return instances_.end(); }
    typedef NameSpace <Net> :: const_iterator NetIter;
    NetIter NetBeg (void) { return nets_.begin(); }
    NetIter NetEnd (void) { return nets_.end(); }
    typedef NumberSpace <Port <StructArch> > :: const_iterator PortIter;
    PortIter PortBeg (void) { return interface_.begin(); }
    PortIter PortEnd (void) { return interface_.end(); }
    // -------------- nets ---------------------------------------------
    Net* CreateNet (const std::string netName) { return nets_.Create <Net>(netName); }
    Net* FindOrCreateNet (const std::string netName) { return nets_.FindOrCreate <Net>(netName); }
    Net* FindNet (const std::string netName) { return nets_.Find (netName); }
    // -------------- instances ----------------------------------------
    Instance* FindInstance (const std::string instName) { return instances_.Find (instName); }
    Instance* CreateInstance (const std::string instName) { 
	Instance* inst= instances_.Create <Instance>(instName);
	if (inst) inst->Parent(this); 
        return inst;
    }
    Instance* CreateInstance (const std::string instName, Entity*, Architecture*);
    // -------------- ports --------------------------------------------
    Port <StructArch>* FindPort (int number) { 
	return interface_.Find (number); 
    }
    Port <StructArch>* FindPort (const std::string& name) { 
	    return interface_.Find (Parent()->Parent()->FindPort(name)->number()); 
    }
    Port <StructArch>* FindPort (int number, const PortProto::flagsType& dir);
    Port <StructArch>* CreatePort (PortProto*);
    void reserveIF (int s) { interface_.reserve (s); }
};
}
#endif
