// Copyright (c) 2012 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 TOOLS_JSON_SCHEMA_COMPILER_UTIL_H__
#define TOOLS_JSON_SCHEMA_COMPILER_UTIL_H__

#include <string>
#include <vector>

#include "base/memory/linked_ptr.h"
#include "base/memory/scoped_ptr.h"
#include "base/values.h"

namespace json_schema_compiler {

namespace util {

// Creates a new item at |out| from |from|[|index|]. These are used by template
// specializations of |Get(Optional)ArrayFromList|.
bool GetItemFromList(const base::ListValue& from, int index, int* out);
bool GetItemFromList(const base::ListValue& from, int index, bool* out);
bool GetItemFromList(const base::ListValue& from, int index, double* out);
bool GetItemFromList(const base::ListValue& from, int index, std::string* out);
bool GetItemFromList(const base::ListValue& from,
                     int index,
                     linked_ptr<base::Value>* out);
bool GetItemFromList(const base::ListValue& from,
                     int index,
                     linked_ptr<base::DictionaryValue>* out);

// This template is used for types generated by tools/json_schema_compiler.
template<class T>
bool GetItemFromList(const base::ListValue& from,
                     int index,
                     linked_ptr<T>* out) {
  const base::DictionaryValue* dict;
  if (!from.GetDictionary(index, &dict))
    return false;
  scoped_ptr<T> obj(new T());
  if (!T::Populate(*dict, obj.get()))
    return false;
  *out = linked_ptr<T>(obj.release());
  return true;
}

// Populates |out| with |list|. Returns false if there is no list at the
// specified key or if the list has anything other than |T|.
template <class T>
bool PopulateArrayFromList(
    const base::ListValue& list, std::vector<T>* out) {
  out->clear();
  T value;
  for (size_t i = 0; i < list.GetSize(); ++i) {
    if (!GetItemFromList(list, i, &value))
      return false;
    out->push_back(value);
  }

  return true;
}

// Populates |out| with |from|.|name|. Returns false if there is no list at
// the specified key or if the list has anything other than |T|.
template <class T>
bool PopulateArrayFromDictionary(
    const base::DictionaryValue& from,
    const std::string& name,
    std::vector<T>* out) {
  const base::ListValue* list = NULL;
  if (!from.GetListWithoutPathExpansion(name, &list))
    return false;

  return PopulateArrayFromList(*list, out);
}

// Creates a new vector containing |list| at |out|. Returns
// true on success or if there is nothing at the specified key. Returns false
// if anything other than a list of |T| is at the specified key.
template <class T>
bool PopulateOptionalArrayFromList(
    const base::ListValue& list,
    scoped_ptr<std::vector<T> >* out) {
  out->reset(new std::vector<T>());
  T value;
  for (size_t i = 0; i < list.GetSize(); ++i) {
    if (!GetItemFromList(list, i, &value)) {
      out->reset();
      return false;
    }
    (*out)->push_back(value);
  }

  return true;
}

// Creates a new vector containing |from|.|name| at |out|. Returns
// true on success or if there is nothing at the specified key. Returns false
// if anything other than a list of |T| is at the specified key.
template <class T>
bool PopulateOptionalArrayFromDictionary(
    const base::DictionaryValue& from,
    const std::string& name,
    scoped_ptr<std::vector<T> >* out) {
  const base::ListValue* list = NULL;
  {
    const base::Value* maybe_list = NULL;
    // Since |name| is optional, its absence is acceptable. However, anything
    // other than a ListValue is not.
    if (!from.GetWithoutPathExpansion(name, &maybe_list))
      return true;
    if (!maybe_list->IsType(base::Value::TYPE_LIST))
      return false;
    list = static_cast<const base::ListValue*>(maybe_list);
  }

  return PopulateOptionalArrayFromList(*list, out);
}

// Appends a Value newly created from |from| to |out|. These used by template
// specializations of |Set(Optional)ArrayToList|.
void AddItemToList(const int from, base::ListValue* out);
void AddItemToList(const bool from, base::ListValue* out);
void AddItemToList(const double from, base::ListValue* out);
void AddItemToList(const std::string& from, base::ListValue* out);
void AddItemToList(const linked_ptr<base::Value>& from,
                   base::ListValue* out);
void AddItemToList(const linked_ptr<base::DictionaryValue>& from,
                   base::ListValue* out);

// This template is used for types generated by tools/json_schema_compiler.
template<class T>
void AddItemToList(const linked_ptr<T>& from, base::ListValue* out) {
  out->Append(from->ToValue().release());
}

// Set |out| to the the contents of |from|. Requires GetItemFromList to be
// implemented for |T|.
template <class T>
void PopulateListFromArray(
    const std::vector<T>& from,
    base::ListValue* out) {
  out->Clear();
  for (typename std::vector<T>::const_iterator it = from.begin();
      it != from.end(); ++it) {
    AddItemToList(*it, out);
  }
}

// Set |out| to the the contents of |from| if |from| is non-NULL. Requires
// GetItemFromList to be implemented for |T|.
template <class T>
void PopulateListFromOptionalArray(
    const scoped_ptr<std::vector<T> >& from,
    base::ListValue* out) {
  if (from.get())
    PopulateListFromArray(*from, out);

}

template <class T>
scoped_ptr<base::Value> CreateValueFromArray(const std::vector<T>& from) {
  base::ListValue* list = new base::ListValue();
  PopulateListFromArray(from, list);
  return scoped_ptr<base::Value>(list);
}

template <class T>
scoped_ptr<base::Value> CreateValueFromOptionalArray(
    const scoped_ptr<std::vector<T> >& from) {
  if (from.get())
    return CreateValueFromArray(*from);
  return scoped_ptr<base::Value>();
}

std::string ValueTypeToString(Value::Type type);

}  // namespace util
}  // namespace json_schema_compiler

#endif // TOOLS_JSON_SCHEMA_COMPILER_UTIL_H__