// -*- c-basic-offset: 4; related-file-name: "../../lib/nameinfo.cc" -*-
#ifndef CLICK_NAMEINFO_HH
#define CLICK_NAMEINFO_HH
#include <click/string.hh>
#include <click/vector.hh>
#include <click/straccum.hh>
CLICK_DECLS
class Element;
class NameDB;
class ErrorHandler;

class NameInfo { public:

    /** @brief Construct a NameInfo element.
     *
     * Users never need to call this. */
    NameInfo();

    /** @brief Destroy a NameInfo object.
     *
     * Also destroys all NameDB objects installed on this NameInfo.
     *
     * Users never need to call this. */
    ~NameInfo();

    /** @brief Static initialization for NameInfo.
     *
     * Creates the global NameInfo used for databases unconnected to any
     * router.  Users never need to call this. */
    static void static_initialize();

    /** @brief Static cleanup for NameInfo.
     *
     * Destroys the global NameInfo used for databases unconnected to any
     * router.  Users never need to call this. */
    static void static_cleanup();

    /** @brief Known name database types. */
    enum DBType {
	T_NONE = 0,			///< Nonexistent names database
	T_SCHEDULEINFO = 0x00000001,	///< ScheduleInfo database
	T_ANNOTATION = 0x00000002,	///< Packet annotation database
	T_SCRIPT_INSN = 0x00000003,	///< Script instruction names database
	T_SIGNO = 0x00000004,		///< User-level signal names database
	T_SPINLOCK = 0x00000005,	///< Spinlock names database
	T_ETHERNET_ADDR = 0x01000001,	///< Ethernet address names database
	T_IP_ADDR = 0x04000001,		///< IP address names database
	T_IP_PREFIX = 0x04000002,	///< IP prefix names database
	T_IP_PROTO = 0x04000003,	///< IP protocol names database
	T_IPFILTER_TYPE = 0x04000004,	///< IPFilter instruction names database
	T_TCP_OPT = 0x04000005,		///< TCP option names database
	T_IPREWRITER_PATTERN = 0x04000006, ///< IPRewriterPattern database
	T_ICMP_TYPE = 0x04010000,	///< ICMP type names database
	T_ICMP_CODE = 0x04010100,	///< ICMP code names database
	T_IP_PORT = 0x04020000,		///< Starting point for IP per-protocol port names databases
	T_TCP_PORT = 0x04020006,	///< TCP port names database
	T_UDP_PORT = 0x04020011,	///< UDP port names database
	T_IP_FIELDNAME = 0x04030000,	///< Starting point for IP per-protocol field names databases
	T_ICMP_FIELDNAME = 0x04030001,	///< ICMP field names database
	T_TCP_FIELDNAME = 0x04030006,	///< TCP field names database
	T_UDP_FIELDNAME = 0x04030011,	///< UDP field names database
	T_IP6_ADDR = 0x06000001,	///< IPv6 address names database
	T_IP6_PREFIX = 0x06000002	///< IPv6 prefix names database
    };

    /** @brief Find or create a name database.
     * @param type database type
     * @param context compound element context
     * @param value_size size of values stored in database
     * @param create whether to create a DynamicNameDB if no database exists
     *
     * Returns an installed name database matching @a type and @a context.
     * (If @a context is non-null, then the database matches the implied
     * router and compound element context.  Otherwise, the database is the
     * unique global database for the given @a type.)  If @a create is true,
     * and no database exists exactly matching @a type and @a context, then a
     * DynamicNameDB is created and installed with that @a type, @a context,
     * and @a value_size.  Otherwise, the search bubbles up through the
     * prefixes of @a context until an installed database is found.
     *
     * Returns null if no installed database is found.  @a value_size must
     * match the value size in the returned database.
     *
     * Most users will use query() and define() directly, not call getdb().
     */
    static NameDB *getdb(uint32_t type, const Element *context,
			 size_t value_size, bool create);

    /** @brief Install a name database.
     * @param db name database
     * @param context compound element context
     *
     * Installs the given name database for the compound element context
     * implied by @a context.  (If @a context is non-null, then the database
     * is installed for the implied router and compound element context.  If
     * it is null, the database is installed globally.)  The query() and
     * define() operations apply only to installed databases.
     *
     * It is an error to install a database that has already been installed.
     * It is also an error to install a database for a compound element
     * context that already has a different database installed.  An installed
     * NameDB is automatically destroyed when its containing NameInfo is
     * destroyed (for example, when @a context's router is destroyed).
     */
    static void installdb(NameDB *db, const Element *context);

    /** @brief Uninstall a name database.
     * @param db name database
     *
     * Undoes the effects of installdb().  The given database will no longer
     * be used for query() and define() operations.
     */
    static void uninstalldb(NameDB *db);

    /** @brief Query installed databases for @a name.
     * @param type database type
     * @param context compound element context
     * @param name name to look up
     * @param value_store value storage
     * @param value_size size of value storage
     * @return true iff the query succeeded
     *
     * Queries all installed @a type databases that apply to the compound
     * element @a context, returning the most specific value matching @a name.
     * The value is stored in @a value_store.  The installed databases must
     * have the given @a value_size.
     */
    static bool query(uint32_t type, const Element *context,
		      const String &name, void *value_store, size_t value_size);

    /** @brief Query installed databases for @a name, returning a 32-bit integer value.
     * @param type database type
     * @param context compound element context
     * @param name name to look up
     * @param value_store value storage
     * @return true iff the query succeeded
     *
     * Queries all installed @a type databases that apply to the compound
     * element @a context, returning the most specific value matching @a name.
     * The value is stored in @a value_store.  The installed databases must
     * have a value size of 4.
     *
     * If no matching name is found, query_int checks whether @a name unparses
     * into a 32-bit integer value (for example, "30").  If so, *@a
     * value_store is set to the corresponding integer and true is returned.
     * Otherwise, false is returned.
     */
    static bool query_int(uint32_t type, const Element *context,
			  const String &name, int32_t *value_store);

    /** @overload */
    static bool query_int(uint32_t type, const Element *context,
			  const String &name, uint32_t *value_store);

    /** @brief Query installed databases for @a value.
     * @param type database type
     * @param context compound element context
     * @param value points to value to look up
     * @param value_size size of value
     * @return the name, or the empty string if the query failed
     *
     * Queries all installed @a type databases that apply to the compound
     * element @a context, returning the name in the most specific database
     * whose value matches @a value, or the empty string if the relevant
     * databases don't support reverse queries or no such value exists.  The
     * installed databases must have the given @a value_size.
     */
    static String revquery(uint32_t type, const Element *context,
			   const void *value, size_t value_size);

    /** @brief Query installed databases for a 32-bit integer @a value.
     * @param type database type
     * @param context compound element context
     * @param value value to look up
     * @return the name, or the empty string if the query failed
     *
     * Queries all installed @a type databases that apply to the compound
     * element @a context, returning the name in the most specific database
     * whose value matches @a value, or the empty string if the relevant
     * databases don't support reverse queries or no such value exists.  The
     * installed databases must have value size 4.
     */
    static inline String revquery_int(uint32_t type, const Element *context,
				      int32_t value);

    /** @brief Define @a name to equal @a value in the installed databases.
     * @param type database type
     * @param context compound element context
     * @param name name to define
     * @param value points to defined value
     * @param value_size size of value
     * @return true iff the name was defined
     *
     * Defines the given @a name to @a value in the installed @a type database
     * with compound element @a context.  If no database exists exactly
     * matching that @a type and @a context, a new DynamicNameDB is created
     * and installed with those values (and the given @a value_size).  A name
     * might not be defined if the existing database for that @a type and @a
     * context doesn't support definitions, or if no new database can be
     * created.  If any database exists, it must match the given @a
     * value_size.
     */
    static inline bool define(uint32_t type, const Element *context,
			      const String &name, const void *value, size_t value_size);

    /** @brief Define @a name to equal 32-bit integer @a value in the installed databases.
     * @param type database type
     * @param context compound element context
     * @param name name to define
     * @param value defined value
     * @return true iff the value was defined
     *
     * Defines the given @a name to @a value in the installed @a type database
     * with compound element @a context.  If no database exists exactly
     * matching that @a type and @a context, a new DynamicNameDB is created
     * and installed with those values (and value size 4).  A name might not
     * be defined if the existing database for that @a type and @a context
     * doesn't support definitions, or if no new database can be created.  If
     * any database exists, it must have value size 4.
     */
    static inline bool define_int(uint32_t type, const Element *context,
				  const String &name, int32_t value);

#if CLICK_NAMEDB_CHECK
    /** @cond never */
    void check(ErrorHandler *);
    static void check(const Element *, ErrorHandler *);
    /** @endcond never */
#endif

  private:

    Vector<NameDB *> _namedb_roots;
    Vector<NameDB *> _namedbs;

    inline NameDB *install_dynamic_sentinel() { return (NameDB *) this; }
    NameDB *namedb(uint32_t type, size_t size, const String &prefix, NameDB *installer);

#if CLICK_NAMEDB_CHECK
    uintptr_t _check_generation;
    void checkdb(NameDB *db, NameDB *parent, ErrorHandler *errh);
#endif

};

class NameDB { public:

    /** @brief Construct a database.
     * @param type database type
     * @param context database compound element context, as a String
     * @param value_size database value size
     *
     * @a value_size must be greater than 0. */
    inline NameDB(uint32_t type, const String &context, size_t value_size);

    /** @brief Destroy a database.
     *
     * Destroying an installed database automatically uninstalls it.
     * See NameInfo::uninstalldb(). */
    virtual ~NameDB() {
	NameInfo::uninstalldb(this);
    }

    /** @brief Return the database type. */
    uint32_t type() const {
	return _type;
    }

    /** @brief Return the database's compound element context as a string. */
    const String &context() const {
	return _context;
    }

    /** @brief Return the contextual parent database, if any.
     *
     * The contextual parent database is the unique database, for the same
     * router, with the same type(), whose context() is a prefix of this
     * database's context(), that has the longest context() of any matching
     * database.  If there is no such database returns null. */
    NameDB *context_parent() const {
	return _context_parent;
    }

    /** @brief Return the database's value size. */
    size_t value_size() const {
	return _value_size;
    }

    /** @brief Query this database for a given name.
     * @param name name to look up
     * @param value points to value storage
     * @param value_size size of value storage
     * @return true iff the query succeeded
     *
     * The @a value_size parameter must equal this database's value size. */
    virtual bool query(const String &name, void *value, size_t value_size) = 0;

    /** @brief Query this database for a given value.
     * @param value points to value to look up
     * @param value_size size of value storage
     * @return the name for the given value, or an empty string if the value
     * has not been defined
     *
     * The @a value_size parameter must equal this database's value size.
     * The default implementation always returns the empty string. */
    virtual String revquery(const void *value, size_t value_size);

    /** @brief Define a name in this database to a given value.
     * @param name name to define
     * @param value points to value to define
     * @param value_size size of value storage
     * @return true iff the name was defined
     *
     * The @a value_size parameter must equal this database's value size.
     * The default implementation always returns false. */
    virtual bool define(const String &name, const void *value, size_t value_size);

    /** @brief Define a name in this database to a 32-bit integer value.
     * @param name name to define
     * @param value value to define
     * @return true iff the name was defined
     *
     * The database's value size must equal 4.  The implementation is the same
     * as <code>define(name, &value, 4)</code>. */
    inline bool define_int(const String &name, int32_t value);

#if CLICK_NAMEDB_CHECK
    /** @cond never */
    virtual void check(ErrorHandler *);
    /** @endcond never */
#endif

  private:

    uint32_t _type;
    String _context;
    size_t _value_size;
    NameDB *_context_parent;
    NameDB *_context_sibling;
    NameDB *_context_child;
    NameInfo *_installed;

#if CLICK_NAMEDB_CHECK
    uintptr_t _check_generation;
#endif

    friend class NameInfo;

};

class StaticNameDB : public NameDB { public:

    struct Entry {
	const char *name;
	uint32_t value;
    };

    /** @brief Construct a static name database.
     * @param type database type
     * @param context database compound element context, as a String
     * @param entry pointer to static entry list
     * @param nentry number of entries
     *
     * The entry array specifies the contents of the database.  It must be
     * sorted by name: entry[i].name < entry[i+1].name for all 0 <= i <
     * nentry-1.  The entry array must also persist as long as the database is
     * in use; the database doesn't copy the entry array into its own memory,
     * but continues to use the array passed in.  The resulting database has
     * value_size() 4. */
    inline StaticNameDB(uint32_t type, const String &context,
			const Entry *entry, size_t nentry);

    /** @brief Query this database for a given name.
     * @param name name to look up
     * @param value points to value storage
     * @param value_size size of value storage
     * @return true iff the query succeeded
     *
     * The @a value_size parameter must equal 4. */
    bool query(const String &name, void *value, size_t value_size);

    /** @brief Query this database for a given value.
     * @param value points to value to look up
     * @param value_size size of value storage
     * @return the name for the given value, or an empty string if the value
     * has not been defined
     *
     * The @a value_size parameter must equal 4. */
    String revquery(const void *value, size_t value_size);

#if CLICK_NAMEDB_CHECK
    /** @cond never */
    void check(ErrorHandler *);
    /** @endcond never */
#endif

  private:

    const Entry *_entries;
    size_t _nentries;

};

class DynamicNameDB : public NameDB { public:

    /** @brief Construct a dynamic name database.
     * @param type database type
     * @param context database compound element context, as a String
     * @param value_size database value size
     *
     * @a value_size must be greater than 0.  The database is initially
     * empty. */
    inline DynamicNameDB(uint32_t type, const String &context, size_t value_size);

    /** @brief Query this database for a given name.
     * @param name name to look up
     * @param value points to value storage
     * @param value_size size of value storage
     * @return true iff the query succeeded
     *
     * The @a value_size parameter must equal this database's value size. */
    bool query(const String &name, void *value, size_t value_size);

    /** @brief Query this database for a given value.
     * @param value points to value to look up
     * @param value_size size of value storage
     * @return the name for the given value, or an empty string if the value
     * has not been defined
     *
     * The @a value_size parameter must equal this database's value size. */
    String revquery(const void *value, size_t value_size);

    /** @brief Define a name in this database to a given value.
     * @param name name to define
     * @param value points to value to define
     * @param value_size size of value storage
     * @return true iff the name was defined
     *
     * The @a value_size parameter must equal this database's value size. */
    bool define(const String &name, const void *value, size_t value_size);

#if CLICK_NAMEDB_CHECK
    /** @cond never */
    void check(ErrorHandler *);
    /** @endcond never */
#endif

  private:

    Vector<String> _names;
    StringAccum _values;
    int _sorted;

    void *find(const String &name, bool create);
    void sort();

};


inline
NameDB::NameDB(uint32_t type, const String &context, size_t vsize)
    : _type(type), _context(context), _value_size(vsize),
      _context_parent(0), _context_sibling(0), _context_child(0), _installed(0)
{
#if CLICK_NAMEDB_CHECK
    _check_generation = 0;
#endif
    assert(_value_size > 0);
}

inline
StaticNameDB::StaticNameDB(uint32_t type, const String &context, const Entry *entry, size_t nentry)
    : NameDB(type, context, sizeof(entry->value)), _entries(entry), _nentries(nentry)
{
}

inline
DynamicNameDB::DynamicNameDB(uint32_t type, const String &context, size_t vsize)
    : NameDB(type, context, vsize), _sorted(0)
{
}

inline String
NameInfo::revquery_int(uint32_t type, const Element *e, int32_t value)
{
    return revquery(type, e, &value, sizeof(value));
}

inline bool
NameInfo::define(uint32_t type, const Element *e, const String &name, const void *value, size_t vsize)
{
    if (NameDB *db = getdb(type, e, vsize, true))
	return db->define(name, value, vsize);
    else
	return false;
}

inline bool
NameInfo::define_int(uint32_t type, const Element *e, const String &name, const int32_t value)
{
    if (NameDB *db = getdb(type, e, sizeof(value), true))
	return db->define(name, &value, sizeof(value));
    else
	return false;
}

inline bool
NameDB::define_int(const String &name, const int32_t value)
{
    return define(name, &value, sizeof(value));
}

CLICK_ENDDECLS
#endif
