// -*- c-basic-offset: 4 -*-
#ifndef CLICK_ROUTERT_HH
#define CLICK_ROUTERT_HH
#include "elementt.hh"
#include "eclasst.hh"
#include <click/error.hh>
#include <click/hashmap.hh>
#include <click/archive.hh>
typedef HashMap<String, int> StringMap;

class RouterT : public ElementClassT { public:

    RouterT();
    RouterT(const String &name, const String &landmark, RouterT *declaration_scope = 0);
    virtual ~RouterT();

    // ELEMENTS
    int nelements() const		{ return _elements.size(); }
    int n_live_elements() const		{ return _n_live_elements; }
    
    inline const ElementT *element(const String &) const;
    inline ElementT *element(const String &);
    int eindex(const String &s) const	{ return _element_name_map[s]; }
    
    const ElementT *element(int i) const{ return _elements[i]; }
    ElementT *element(int i)		{ return _elements[i]; }
    
    class iterator;
    class const_iterator;
    class type_iterator;
    class const_type_iterator;
    inline iterator begin_elements();
    inline const_iterator begin_elements() const;
    inline type_iterator begin_elements(ElementClassT *);
    inline const_type_iterator begin_elements(ElementClassT *) const;
    inline iterator end_elements();
    inline const_iterator end_elements() const;
    
    bool elive(int i) const		{ return _elements[i]->live(); }
    bool edead(int i) const		{ return _elements[i]->dead(); }
    inline String ename(int) const;
    inline ElementClassT *etype(int) const;
    inline String etype_name(int) const;
    
    ElementT *get_element(const String &name, ElementClassT *, const String &configuration, const String &landmark);
    ElementT *add_anon_element(ElementClassT *, const String &configuration = String(), const String &landmark = String());
    void change_ename(int, const String &);
    void deanonymize_elements();
    void free_element(ElementT *);
    void free_dead_elements();

    void set_new_eindex_collector(Vector<int> *v) { _new_eindex_collector=v; }

    // TYPES
    ElementClassT *locally_declared_type(const String &) const;
    inline ElementClassT *declared_type(const String &) const;
    void add_declared_type(ElementClassT *, bool anonymous);

    void collect_types(HashMap<ElementClassT *, int> &) const;
    void collect_locally_declared_types(Vector<ElementClassT *> &) const;
    void collect_overloads(Vector<ElementClassT *> &) const;

    // CONNECTIONS
    int nconnections() const			{ return _conn.size(); }
    const Vector<ConnectionT> &connections() const { return _conn; }
    const ConnectionT &connection(int c) const	{ return _conn[c]; }
    bool connection_live(int c) const		{ return _conn[c].live(); }

    void add_tunnel(const String &, const String &, const String &, ErrorHandler *);

    bool add_connection(const PortT &, const PortT &, const String &landmark = String());
    inline bool add_connection(ElementT *, int, ElementT *, int, const String &landmark = String());
    void kill_connection(int);
    void kill_bad_connections();
    void compact_connections();

    inline bool has_connection(const PortT &, const PortT &) const;
    int find_connection(const PortT &, const PortT &) const;
    void change_connection_to(int, PortT);
    void change_connection_from(int, PortT);
    bool find_connection_from(const PortT &, PortT &) const;
    void find_connections_from(const PortT &, Vector<PortT> &) const;
    void find_connections_from(const PortT &, Vector<int> &) const;
    void find_connections_to(const PortT &, Vector<PortT> &) const;
    void find_connections_to(const PortT &, Vector<int> &) const;
    void find_connection_vector_from(ElementT *, Vector<int> &) const;
    void find_connection_vector_to(ElementT *, Vector<int> &) const;

    bool insert_before(const PortT &, const PortT &);
    bool insert_after(const PortT &, const PortT &);
    inline bool insert_before(ElementT *, const PortT &);
    inline bool insert_after(ElementT *, const PortT &);

    // REQUIREMENTS
    void add_requirement(const String &);
    void remove_requirement(const String &);
    const Vector<String> &requirements() const	{ return _requirements; }

    // ARCHIVE
    void add_archive(const ArchiveElement &);
    int narchive() const			{ return _archive.size(); }
    int archive_index(const String &s) const	{ return _archive_map[s]; }
    const Vector<ArchiveElement> &archive() const{ return _archive; }
    ArchiveElement &archive(int i)		{ return _archive[i]; }
    const ArchiveElement &archive(int i) const	{ return _archive[i]; }
    inline ArchiveElement &archive(const String &s);
    inline const ArchiveElement &archive(const String &s) const;

    void add_components_to(RouterT *, const String &prefix = String()) const;

    // CHECKING, FLATTENING AND EXPANDING
    void check() const;

    void remove_duplicate_connections();
    void remove_dead_elements(ErrorHandler * = 0);

    void remove_compound_elements(ErrorHandler *);
    void remove_tunnels(ErrorHandler * = 0);

    void expand_into(RouterT *, const String &prefix, VariableEnvironment &, ErrorHandler *);
    void flatten(ErrorHandler *);

    // UNPARSING
    void unparse(StringAccum &, const String & = String()) const;
    void unparse_requirements(StringAccum &, const String & = String()) const;
    void unparse_declarations(StringAccum &, const String & = String()) const;
    void unparse_connections(StringAccum &, const String & = String()) const;
    String configuration_string() const;

    // COMPOUND ELEMENTS
    String landmark() const		{ return _type_landmark; }
    const ElementTraits *find_traits() const;
    
    bool primitive() const		{ return false; }
    
    int nformals() const		{ return _formals.size(); }
    const Vector<String> &formals() const { return _formals; }
    const Vector<String> &formal_types() const { return _formal_types; }
    inline void add_formal(const String &fname, const String &ftype);
    int ninputs() const			{ return _ninputs; }
    int noutputs() const		{ return _noutputs; }
    
    RouterT *declaration_scope() const	{ return _declaration_scope; }
    int declaration_depth() const	{ return _declaration_depth; }
    ElementClassT *overload_type() const { return _overload_type; }
    void set_overload_type(ElementClassT *);

    int finish_type(ErrorHandler *);
    
    ElementClassT *resolve(int, int, Vector<String> &, ErrorHandler *, const String &landmark);
    ElementT *complex_expand_element(ElementT *, const String &, Vector<String> &, RouterT *, const String &prefix, VariableEnvironment &, ErrorHandler *);

    String unparse_signature() const;
    void unparse_declaration(StringAccum &, const String &, UnparseKind, ElementClassT *);    

    RouterT *cast_router()		{ return this; }

  private:
  
    struct Pair {
	int from;
	int to;
	Pair() : from(-1), to(-1) { }
	Pair(int f, int t) : from(f), to(t) { }
    };

    struct ElementType {
	ElementClassT * const type;
	int scope_cookie;
	int prev_name;
	ElementType(ElementClassT *c, int sc, int pn) : type(c), scope_cookie(sc), prev_name(pn) { assert(type); type->use(); }
	ElementType(const ElementType &o) : type(o.type), scope_cookie(o.scope_cookie), prev_name(o.prev_name) { type->use(); }
	~ElementType()			{ type->unuse(); }
	const String &name() const	{ return type->name(); }
      private:
	ElementType &operator=(const ElementType &);
    };

    StringMap _element_name_map;
    Vector<ElementT *> _elements;
    ElementT *_free_element;
    int _n_live_elements;
    Vector<int> *_new_eindex_collector;

    Vector<ConnectionT> _conn;
    Vector<Pair> _first_conn;
    int _free_conn;
    
    StringMap _declared_type_map;
    Vector<ElementType> _declared_types;

    Vector<String> _requirements;

    StringMap _archive_map;
    Vector<ArchiveElement> _archive;

    RouterT *_declaration_scope;
    int _declaration_scope_cookie;
    int _declaration_depth;
    int _scope_cookie;

    Vector<String> _formals;
    Vector<String> _formal_types;
    int _ninputs;
    int _noutputs;
    ElementClassT *_overload_type;
    String _type_landmark;
    mutable ElementTraits _traits;
    bool _circularity_flag;
    
    RouterT(const RouterT &);
    RouterT &operator=(const RouterT &);

    ElementClassT *declared_type(const String &, int scope_cookie) const;
    void update_noutputs(int);
    void update_ninputs(int);
    ElementT *add_element(const ElementT &);
    void assign_element_name(int);
    void free_connection(int ci);
    void unlink_connection_from(int ci);
    void unlink_connection_to(int ci);
    void expand_tunnel(Vector<PortT> *port_expansions, const Vector<PortT> &ports, bool is_output, int which, ErrorHandler *) const;
    String interpolate_arguments(const String &, const Vector<String> &) const;

    friend class RouterUnparserT;
    
};

class RouterT::const_iterator { public:
    operator bool() const		{ return _e; }
    int eindex() const			{ return _e->eindex(); }
    void operator++(int)		{ if (_e) step(_e->router(), eindex()+1);}
    void operator++()			{ (*this)++; }
    operator const ElementT *() const	{ return _e; }
    const ElementT *operator->() const	{ return _e; }
    const ElementT &operator*() const	{ return *_e; }
  private:
    const ElementT *_e;
    const_iterator()			: _e(0) { }
    const_iterator(const RouterT *r, int ei) { step(r, ei); }
    void step(const RouterT *, int);
    friend class RouterT;
    friend class RouterT::iterator;
};

class RouterT::iterator : public RouterT::const_iterator { public:
    operator ElementT *() const		{ return const_cast<ElementT *>(_e); }
    ElementT *operator->() const	{ return const_cast<ElementT *>(_e); }
    ElementT &operator*() const		{ return const_cast<ElementT &>(*_e); }
  private:
    iterator()				: const_iterator() { }
    iterator(RouterT *r, int ei)	: const_iterator(r, ei) { }
    friend class RouterT;
};

class RouterT::const_type_iterator { public:
    operator bool() const		{ return _e; }
    int eindex() const			{ return _e->eindex(); }
    inline void operator++(int);
    inline void operator++();
    operator const ElementT *() const	{ return _e; }
    const ElementT *operator->() const	{ return _e; }
    const ElementT &operator*() const	{ return *_e; }
  private:
    const ElementT *_e;
    const_type_iterator()		: _e(0) { }
    const_type_iterator(const RouterT *r, ElementClassT *t, int i) { step(r, t, i); }
    void step(const RouterT *, ElementClassT *, int);
    friend class RouterT;
    friend class RouterT::type_iterator;
};

class RouterT::type_iterator : public RouterT::const_type_iterator { public:
    operator ElementT *() const		{ return const_cast<ElementT *>(_e); }
    ElementT *operator->() const	{ return const_cast<ElementT *>(_e); }
    ElementT &operator*() const		{ return const_cast<ElementT &>(*_e); }
  private:
    type_iterator()			: const_type_iterator() { }
    type_iterator(RouterT *r, ElementClassT *t, int ei)	: const_type_iterator(r, t, ei) { }
    friend class RouterT;
};


inline RouterT::iterator
RouterT::begin_elements()
{
    return iterator(this, 0);
}

inline RouterT::const_iterator
RouterT::begin_elements() const
{
    return const_iterator(this, 0);
}

inline RouterT::type_iterator
RouterT::begin_elements(ElementClassT *t)
{
    return type_iterator(this, t, 0);
}

inline RouterT::const_type_iterator
RouterT::begin_elements(ElementClassT *t) const
{
    return const_type_iterator(this, t, 0);
}

inline RouterT::const_iterator
RouterT::end_elements() const
{
    return const_iterator();
}

inline RouterT::iterator
RouterT::end_elements()
{
    return iterator();
}

inline void
RouterT::const_type_iterator::operator++(int)
{
    if (_e)
	step(_e->router(), _e->type(), _e->eindex() + 1);
}

inline void
RouterT::const_type_iterator::operator++()
{
    (*this)++;
}

inline const ElementT *
RouterT::element(const String &s) const
{
    int i = _element_name_map[s];
    return (i >= 0 ? _elements[i] : 0);
}

inline ElementT *
RouterT::element(const String &s)
{
    int i = _element_name_map[s];
    return (i >= 0 ? _elements[i] : 0);
}

inline String
RouterT::ename(int e) const
{
    return _elements[e]->name();
}

inline ElementClassT *
RouterT::etype(int e) const
{
    return _elements[e]->type();
}

inline String
RouterT::etype_name(int e) const
{
    return _elements[e]->type()->name();
}

inline ElementClassT *
RouterT::declared_type(const String &name) const
{
    return declared_type(name, 0x7FFFFFFF);
}

inline bool
RouterT::add_connection(ElementT *from_elt, int from_port, ElementT *to_elt,
			int to_port, const String &landmark)
{
    return add_connection(PortT(from_elt, from_port), PortT(to_elt, to_port), landmark);
}

inline bool
RouterT::has_connection(const PortT &hfrom, const PortT &hto) const
{
    return find_connection(hfrom, hto) >= 0;
}

inline bool
RouterT::insert_before(ElementT *e, const PortT &h)
{
    return insert_before(PortT(e, 0), h);
}

inline bool
RouterT::insert_after(ElementT *e, const PortT &h)
{
    return insert_after(PortT(e, 0), h);
}

inline ArchiveElement &
RouterT::archive(const String &name)
{
    return _archive[_archive_map[name]];
}

inline const ArchiveElement &
RouterT::archive(const String &name) const
{
    return _archive[_archive_map[name]];
}

inline void
RouterT::add_formal(const String &fname, const String &ftype)
{
    _formals.push_back(fname);
    _formal_types.push_back(ftype);
}

inline bool
operator==(const RouterT::const_iterator &i, const RouterT::const_iterator &j)
{
    return i.operator->() == j.operator->();
}

inline bool
operator!=(const RouterT::const_iterator &i, const RouterT::const_iterator &j)
{
    return i.operator->() != j.operator->();
}

inline bool
operator==(const RouterT::const_type_iterator &i, const RouterT::const_type_iterator &j)
{
    return i.operator->() == j.operator->();
}

inline bool
operator!=(const RouterT::const_type_iterator &i, const RouterT::const_type_iterator &j)
{
    return i.operator->() != j.operator->();
}

inline bool
operator==(const RouterT::const_type_iterator &i, const RouterT::const_iterator &j)
{
    return i.operator->() == j.operator->();
}

inline bool
operator!=(const RouterT::const_type_iterator &i, const RouterT::const_iterator &j)
{
    return i.operator->() != j.operator->();
}

#endif
