// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef MOJO_PUBLIC_CPP_BINDINGS_LIB_MAP_INTERNAL_H_
#define MOJO_PUBLIC_CPP_BINDINGS_LIB_MAP_INTERNAL_H_

#include <map>

#include "mojo/public/cpp/bindings/array.h"
#include "mojo/public/cpp/bindings/lib/template_util.h"

namespace mojo {
namespace internal {

template <typename Key, typename Value, bool kValueIsMoveOnlyType>
struct MapTraits {};

template <typename Key, typename Value>
struct MapTraits<Key, Value, false> {
  // Map keys can't be move only types.
  static_assert(!internal::IsMoveOnlyType<Key>::value,
                "Map keys can not be move only types.");

  typedef Key KeyStorageType;
  typedef Key& KeyRefType;
  typedef const Key& KeyConstRefType;
  typedef KeyConstRefType KeyForwardType;

  typedef Value ValueStorageType;
  typedef Value& ValueRefType;
  typedef const Value& ValueConstRefType;
  typedef ValueConstRefType ValueForwardType;

  static inline void InitializeFrom(
      std::map<KeyStorageType, ValueStorageType>* m,
      mojo::Array<Key> keys,
      mojo::Array<Value> values) {
    for (size_t i = 0; i < keys.size(); ++i)
      Insert(m, keys[i], values[i]);
  }
  static inline void Decompose(std::map<KeyStorageType, ValueStorageType>* m,
                               mojo::Array<Key>* keys,
                               mojo::Array<Value>* values) {
    keys->resize(m->size());
    values->resize(m->size());
    int i = 0;
    for (typename std::map<KeyStorageType, ValueStorageType>::iterator
             it = m->begin();
         it != m->end();
         ++it, ++i) {
      (*keys)[i] = it->first;
      (*values)[i] = it->second;
    }
  }
  static inline void Finalize(std::map<KeyStorageType, ValueStorageType>* m) {}
  static inline ValueRefType at(std::map<KeyStorageType, ValueStorageType>* m,
                                KeyForwardType key) {
    // We don't have C++11 library support yet, so we have to emulate the crash
    // on a non-existent key.
    auto it = m->find(key);
    MOJO_CHECK(it != m->end());
    return it->second;
  }
  static inline ValueConstRefType at(
      const std::map<KeyStorageType, ValueStorageType>* m,
      KeyForwardType key) {
    // We don't have C++11 library support yet, so we have to emulate the crash
    // on a non-existent key.
    auto it = m->find(key);
    MOJO_CHECK(it != m->end());
    return it->second;
  }
  static inline ValueRefType GetOrInsert(
      std::map<KeyStorageType, ValueStorageType>* m,
      KeyForwardType key) {
    // This is the backing for the index operator (operator[]).
    return (*m)[key];
  }
  static inline void Insert(std::map<KeyStorageType, ValueStorageType>* m,
                            KeyForwardType key,
                            ValueForwardType value) {
    m->insert(std::make_pair(key, value));
  }
  static inline KeyConstRefType GetKey(
      const typename std::map<KeyStorageType, ValueStorageType>::const_iterator&
          it) {
    return it->first;
  }
  static inline ValueConstRefType GetValue(
      const typename std::map<KeyStorageType, ValueStorageType>::const_iterator&
          it) {
    return it->second;
  }
  static inline ValueRefType GetValue(
      const typename std::map<KeyStorageType, ValueStorageType>::iterator& it) {
    return it->second;
  }
  static inline void Clone(
      const std::map<KeyStorageType, ValueStorageType>& src,
      std::map<KeyStorageType, ValueStorageType>* dst) {
    dst->clear();
    for (auto it = src.begin(); it != src.end(); ++it)
      dst->insert(*it);
  }
};

template <typename Key, typename Value>
struct MapTraits<Key, Value, true> {
  // Map keys can't be move only types.
  static_assert(!internal::IsMoveOnlyType<Key>::value,
                "Map keys can not be move only types.");

  typedef Key KeyStorageType;
  typedef Key& KeyRefType;
  typedef const Key& KeyConstRefType;
  typedef KeyConstRefType KeyForwardType;

  struct ValueStorageType {
    // Make 8-byte aligned.
    char buf[sizeof(Value) + (8 - (sizeof(Value) % 8)) % 8];
  };
  typedef Value& ValueRefType;
  typedef const Value& ValueConstRefType;
  typedef Value ValueForwardType;

  static inline void InitializeFrom(
      std::map<KeyStorageType, ValueStorageType>* m,
      mojo::Array<Key> keys,
      mojo::Array<Value> values) {
    for (size_t i = 0; i < keys.size(); ++i)
      Insert(m, keys[i], values[i]);
  }
  static inline void Decompose(std::map<KeyStorageType, ValueStorageType>* m,
                               mojo::Array<Key>* keys,
                               mojo::Array<Value>* values) {
    keys->resize(m->size());
    values->resize(m->size());
    int i = 0;
    for (typename std::map<KeyStorageType, ValueStorageType>::iterator
             it = m->begin();
         it != m->end();
         ++it, ++i) {
      (*keys)[i] = it->first;
      (*values)[i] = GetValue(it).Pass();
    }
  }
  static inline void Finalize(std::map<KeyStorageType, ValueStorageType>* m) {
    for (auto& pair : *m)
      reinterpret_cast<Value*>(pair.second.buf)->~Value();
  }
  static inline ValueRefType at(std::map<KeyStorageType, ValueStorageType>* m,
                                KeyForwardType key) {
    // We don't have C++11 library support yet, so we have to emulate the crash
    // on a non-existent key.
    auto it = m->find(key);
    MOJO_CHECK(it != m->end());
    return GetValue(it);
  }
  static inline ValueConstRefType at(
      const std::map<KeyStorageType, ValueStorageType>* m,
      KeyForwardType key) {
    // We don't have C++11 library support yet, so we have to emulate the crash
    // on a non-existent key.
    auto it = m->find(key);
    MOJO_CHECK(it != m->end());
    return GetValue(it);
  }
  static inline ValueRefType GetOrInsert(
      std::map<KeyStorageType, ValueStorageType>* m,
      KeyForwardType key) {
    // This is the backing for the index operator (operator[]).
    auto it = m->find(key);
    if (it == m->end()) {
      it = m->insert(std::make_pair(key, ValueStorageType())).first;
      new (it->second.buf) Value();
    }

    return GetValue(it);
  }
  static inline void Insert(std::map<KeyStorageType, ValueStorageType>* m,
                            KeyForwardType key,
                            ValueRefType value) {
    // STL insert() doesn't insert |value| if |key| is already part of |m|. We
    // have to use operator[] to initialize into the storage buffer, but we
    // have to do a manual check so that we don't overwrite an existing object.
    auto it = m->find(key);
    if (it == m->end())
      new ((*m)[key].buf) Value(value.Pass());
  }
  static inline KeyConstRefType GetKey(
      const typename std::map<KeyStorageType, ValueStorageType>::const_iterator&
          it) {
    return it->first;
  }
  static inline ValueConstRefType GetValue(
      const typename std::map<KeyStorageType, ValueStorageType>::const_iterator&
          it) {
    return *reinterpret_cast<const Value*>(it->second.buf);
  }
  static inline ValueRefType GetValue(
      const typename std::map<KeyStorageType, ValueStorageType>::iterator& it) {
    return *reinterpret_cast<Value*>(it->second.buf);
  }
  static inline void Clone(
      const std::map<KeyStorageType, ValueStorageType>& src,
      std::map<KeyStorageType, ValueStorageType>* dst) {
    Finalize(dst);
    dst->clear();
    for (auto it = src.begin(); it != src.end(); ++it)
      new ((*dst)[it->first].buf) Value(GetValue(it).Clone());
  }
};

}  // namespace internal
}  // namespace mojo

#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_MAP_INTERNAL_H_
