// 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_SERIALIZATION_H_
#define MOJO_PUBLIC_CPP_BINDINGS_LIB_MAP_SERIALIZATION_H_

#include "mojo/public/cpp/bindings/lib/array_internal.h"
#include "mojo/public/cpp/bindings/lib/map_data_internal.h"
#include "mojo/public/cpp/bindings/lib/map_internal.h"
#include "mojo/public/cpp/bindings/lib/string_serialization.h"
#include "mojo/public/cpp/bindings/map.h"

namespace mojo {

template <typename Key, typename Value>
inline size_t GetSerializedSize_(const Map<Key, Value>& input);

template <typename ValidateParams, typename E, typename F>
inline void SerializeArray_(Array<E> input,
                            internal::Buffer* buf,
                            internal::Array_Data<F>** output);

namespace internal {

template <typename MapType,
          typename DataType,
          bool kValueIsMoveOnlyType = IsMoveOnlyType<MapType>::value>
struct MapSerializer;

template <typename MapType, typename DataType>
struct MapSerializer<MapType, DataType, false> {
  static size_t GetBaseArraySize(size_t count) {
    return Align(count * sizeof(DataType));
  }
  static size_t GetItemSize(const MapType& item) { return 0; }
};

template <>
struct MapSerializer<bool, bool, false> {
  static size_t GetBaseArraySize(size_t count) {
    return Align((count + 7) / 8);
  }
  static size_t GetItemSize(bool item) { return 0; }
};

template <typename H>
struct MapSerializer<ScopedHandleBase<H>, H, true> {
  static size_t GetBaseArraySize(size_t count) {
    return Align(count * sizeof(H));
  }
  static size_t GetItemSize(const H& item) { return 0; }
};

template <typename S>
struct MapSerializer<S, typename S::Data_*, true> {
  static size_t GetBaseArraySize(size_t count) {
    return count * sizeof(internal::StructPointer<typename S::Data_>);
  }
  static size_t GetItemSize(const S& item) { return GetSerializedSize_(item); }
};

template <>
struct MapSerializer<String, String_Data*, false> {
  static size_t GetBaseArraySize(size_t count) {
    return count * sizeof(internal::StringPointer);
  }
  static size_t GetItemSize(const String& item) {
    return GetSerializedSize_(item);
  }
};

}  // namespace internal

// TODO(erg): This can't go away yet. We still need to calculate out the size
// of a struct header, and two arrays.
template <typename MapKey, typename MapValue>
inline size_t GetSerializedSize_(const Map<MapKey, MapValue>& input) {
  if (!input)
    return 0;
  typedef typename internal::WrapperTraits<MapKey>::DataType DataKey;
  typedef typename internal::WrapperTraits<MapValue>::DataType DataValue;

  size_t count = input.size();
  size_t struct_overhead = sizeof(mojo::internal::Map_Data<DataKey, DataValue>);
  size_t key_base_size =
      sizeof(internal::ArrayHeader) +
      internal::MapSerializer<MapKey, DataKey>::GetBaseArraySize(count);
  size_t value_base_size =
      sizeof(internal::ArrayHeader) +
      internal::MapSerializer<MapValue, DataValue>::GetBaseArraySize(count);

  size_t key_data_size = 0;
  size_t value_data_size = 0;
  for (auto it = input.begin(); it != input.end(); ++it) {
    key_data_size +=
        internal::MapSerializer<MapKey, DataKey>::GetItemSize(it.GetKey());
    value_data_size +=
        internal::MapSerializer<MapValue, DataValue>::GetItemSize(
            it.GetValue());
  }

  return struct_overhead + key_base_size + key_data_size + value_base_size +
         value_data_size;
}

// We don't need a KeyValidateParams, because we konw exactly what params are
// needed. (Keys are primitive types or non-nullable strings.)
template <typename ValueValidateParams,
          typename MapKey,
          typename MapValue,
          typename DataKey,
          typename DataValue>
inline void SerializeMap_(Map<MapKey, MapValue> input,
                          internal::Buffer* buf,
                          internal::Map_Data<DataKey, DataValue>** output) {
  if (input) {
    internal::Map_Data<DataKey, DataValue>* result =
        internal::Map_Data<DataKey, DataValue>::New(buf);
    if (result) {
      Array<MapKey> keys;
      Array<MapValue> values;
      input.DecomposeMapTo(&keys, &values);
      SerializeArray_<internal::MapKeyValidateParams<DataKey>>(
          keys.Pass(), buf, &result->keys.ptr);
      SerializeArray_<ValueValidateParams>(
          values.Pass(), buf, &result->values.ptr);
    }
    *output = result;
  } else {
    *output = nullptr;
  }
}

template <typename MapKey,
          typename MapValue,
          typename DataKey,
          typename DataValue>
inline void Deserialize_(internal::Map_Data<DataKey, DataValue>* input,
                         Map<MapKey, MapValue>* output) {
  if (input) {
    Array<MapKey> keys;
    Array<MapValue> values;

    Deserialize_(input->keys.ptr, &keys);
    Deserialize_(input->values.ptr, &values);

    *output = Map<MapKey, MapValue>(keys.Pass(), values.Pass());
  } else {
    output->reset();
  }
}

}  // namespace mojo

#endif  // MOJO_PUBLIC_CPP_BINDINGS_LIB_MAP_SERIALIZATION_H_
