// libTorrent - BitTorrent library
// Copyright (C) 2005-2008, Jari Sundell
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
// In addition, as a special exception, the copyright holders give
// permission to link the code of portions of this program with the
// OpenSSL library under certain conditions as described in each
// individual source file, and distribute linked combinations
// including the two.
//
// You must obey the GNU General Public License in all respects for
// all of the code used other than OpenSSL.  If you modify file(s)
// with this exception, you may extend this exception to your version
// of the file(s), but you are not obligated to do so.  If you do not
// wish to do so, delete this exception statement from your version.
// If you delete this exception statement from all source files in the
// program, then also delete it here.
//
// Contact:  Jari Sundell <jaris@ifi.uio.no>
//
//           Skomakerveien 33
//           3185 Skoppum, NORWAY

#ifndef LIBTORRENT_STATIC_MAP_H
#define LIBTORRENT_STATIC_MAP_H

#include <vector>
#include <torrent/common.h>
#include <torrent/object.h>
#include <torrent/simple_string.h>

// StaticMap: holds a pre-defined subset of possible bencode keys and stores
// their values in a flat array for fast decoding, key access and encoding.
// Makes no copies, so the underlying data buffer must outlive the map object.

// With this, the complexity for bencoding and bdecoding a StaticMap object
// is O(n). The access to any of the pre-defined keys is O(1). Access to
// other keys is not supported, they are dropped while bdecoding. Decoded
// Object types are either VALUE, SSTRING or NONE (if key was not present).

// To use, define an enum of all required keys, and use this type along with
// the number of possible keys in the StaticMap template arguments. Define
// the enum -> key string as array of StaticMapKeys::mapping_type. Define
// the static keyMap variable, most simply by defining base_type in your
// derived map class, like this:
// template<> const Derived::key_map_init Derived::base_type::keyMap(key_list);

// The argument of the constructor of this static keyMap object is a list
// of mapping_type entries. For efficiency, they must be ordered in
// increasing number of the index, and increasing alphabetical order
// (or more specifically, the bencode order) at the same time. In other words,
// the original enum must also be in alphabetical order of the keys the enum
// values refer to.

// Format of the key specifications ("..." may contain any number of further keys):
// "foo::..."   makes foo a bencode dictionary
// "foo[0]..."  makes foo a bencode list
// "foo::"      makes foo an undecoded bencode value (may contain arbitrary bencode data)
// "foo[]"      makes foo an undecoded list of bencode values (like the above but adding the 'l' and 'e' indicators)
// "foo"        makes foo an integer or string value (automatic)
//
// Examples:
// "baz"            refers to a single value for key "baz"
// "foo::a[0]::bar" refers to a single value for key "bar" in the dictionary at index 0 of the list for key "a" in dictionary "foo"
// "foo::a[1]"      refers to a single value at index 1 of the list for key "a" in the dictionary "foo"
// "zoo::"          refers to a bdecoded value for key "zoo"
//
// If the four values are 4, 5, "6" and 7, this would be bencoded as d3:bazi4e3:food1:ald3:bari5ee1:6ee3:zooi7ee
//
// Note that sparse lists are not possible, you must explicitly specify all needed entries starting from index 0,
// and when bencoding, the first unset value terminates the list.

namespace torrent {

// Hierarchical structure mapping bencode keys to flat array indices.
class LIBTORRENT_EXPORT StaticMapKeys : public std::vector<StaticMapKeys> {
public:
  typedef std::vector<StaticMapKeys> base_type;

  struct mapping_type {
    size_t      index;
    const char* key;
  };

  enum value_type {
    TYPE_VALUE,
    TYPE_LIST,
    TYPE_DICT,
    TYPE_BENCODE,
    TYPE_BENCODE_LIST,
  };

  StaticMapKeys(const mapping_type* key_list, size_t length);

  void               set_end(size_t end)   { m_indexEnd = end; }

  size_t             index_begin() const   { return m_indexBegin; }
  size_t             index_end() const     { return m_indexEnd; }

  value_type         type() const          { return m_type; }

  SimpleString       key() const           { return m_key; }

private:
  StaticMapKeys(SimpleString key, value_type type, size_t begin, size_t end)
    : m_key(key), m_indexBegin(begin), m_indexEnd(end), m_type(type) {}

  int                check_key_order(SimpleString key);

  SimpleString       m_key;
  size_t             m_indexBegin;
  size_t             m_indexEnd;
  value_type         m_type;
};

template<typename tmpl_key_type, size_t tmpl_length>
class LIBTORRENT_EXPORT StaticMap {
public:
  typedef Object&         value_type;
  typedef tmpl_key_type   key_type;
  typedef StaticMapKeys   key_map_type;
  typedef Object          list_type[tmpl_length];

  Object&              operator [] (key_type key)        { return m_values[key]; }
  const Object&        operator [] (key_type key) const  { return m_values[key]; }

  const key_map_type&  map() const                       { return keyMap; }

  list_type&           values()                          { return m_values; }
  const list_type&     values() const                    { return m_values; }

  static const size_t  length = tmpl_length;

private:
  struct key_map_init : public key_map_type {
    key_map_init(key_map_type::mapping_type* key_list) : key_map_type(key_list, tmpl_length) {};
  };
  static const key_map_init                keyMap;

  list_type      m_values;
};

}

#endif
