// libTorrent - BitTorrent library
// Copyright (C) 2005-2011, 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_DHT_HASH_MAP_H
#define LIBTORRENT_DHT_HASH_MAP_H

#include "config.h"

#include <unordered_map>

#include "dht_node.h"
#include "dht_tracker.h"
#include "torrent/hash_string.h"

namespace torrent {

// Hash functions for HashString keys, and dereferencing HashString pointers.

// Since the first few bits are very similar if not identical (since the IDs
// will be close to our own node ID), we use an offset of 64 bits in the hash
// string. These bits will be uniformly distributed until the number of DHT
// nodes on the planet approaches 2^64 which is... unlikely.
// An offset of 64 bits provides 96 significant bits which is fine as long as
// the size of size_t does not exceed 12 bytes, while still having correctly
// aligned 64-bit access.
static constexpr unsigned int hashstring_hash_ofs = 8;

struct hashstring_ptr_hash {
  size_t operator () (const HashString* n) const {
#if USE_ALIGNED
    size_t result = 0;
    const char *first = n->data() + hashstring_hash_ofs;
    const char *last = first + sizeof(size_t);

    while (first != last)
      result = (result << 8) + *first++;
    
    return result;
#else
    return *reinterpret_cast<const size_t*>(n->data() + hashstring_hash_ofs);
#endif
  }
};

struct hashstring_hash {
  size_t operator () (const HashString& n) const {
#if USE_ALIGNED
    size_t result = 0;
    const char *first = n.data() + hashstring_hash_ofs;
    const char *last = first + sizeof(size_t);

    while (first != last)
      result = (result << 8) + *first++;
    
    return result;
#else
    return *reinterpret_cast<const size_t*>(n.data() + hashstring_hash_ofs);
#endif
  }
};

// Compare HashString pointers by dereferencing them.
struct hashstring_ptr_equal {
  size_t operator () (const HashString* one, const HashString* two) const 
  { return *one == *two; }
};

class DhtNodeList : public std::unordered_map<const HashString*, DhtNode*, hashstring_ptr_hash, hashstring_ptr_equal> {
public:
  using base_type = std::unordered_map<const HashString*, DhtNode*, hashstring_ptr_hash, hashstring_ptr_equal>;

  // Define accessor iterator with more convenient access to the key and
  // element values.  Allows changing the map definition more easily if needed.
  template<typename T>
  struct accessor_wrapper : public T {
    accessor_wrapper(const T& itr) : T(itr) { }

    const HashString&    id() const    { return *(**this).first; }
    DhtNode*             node() const  { return (**this).second; }
  };

  using const_accessor = accessor_wrapper<const_iterator>;
  using accessor       = accessor_wrapper<iterator>;

  DhtNode*            add_node(DhtNode* n);

};

using DhtTrackerList = std::unordered_map<HashString, DhtTracker*, hashstring_hash>;

inline
DhtNode* DhtNodeList::add_node(DhtNode* n) {
  emplace(reinterpret_cast<const HashString*>(n), n);
  return n;
}

} // namespace torrent

#endif
