//@HEADER
// ************************************************************************
//     Genten: Software for Generalized Tensor Decompositions
//     by Sandia National Laboratories
//
// Sandia National Laboratories is a multimission laboratory managed
// and operated by National Technology and Engineering Solutions of Sandia,
// LLC, a wholly owned subsidiary of Honeywell International, Inc., for the
// U.S. Department of Energy's National Nuclear Security Administration under
// contract DE-NA0003525.
//
// Copyright 2017 National Technology & Engineering Solutions of Sandia, LLC
// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S.
// Government retains certain rights in this software.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// ************************************************************************
//@HEADER

#pragma once

#include <iostream>
#include <fstream>
#include "json.hpp"
#include "json-schema.hpp"

namespace Genten {

// A replacement for boost::property_tree using the bundled json parser
class ptree {
private:
  nlohmann::json j;
public:
  ptree() = default;
  ptree(const ptree&) = default;
  ptree(ptree&&) = default;
  ptree& operator=(const ptree&) = default;
  ptree& operator=(ptree&&) = default;

  ptree(const nlohmann::json& json) : j(json) {}

  operator bool() const { return j.size() > 0; }
  ptree& operator*() { return *this; }

  void read(const std::string& file) {
    std::ifstream f(file);
    f >> j;
    f.close();
  }

  void write(std::ostream& os) const { os << j; }

  bool contains(const std::string& key) const { return j.contains(key); }

  template <typename T> T get(const std::string& key, const T& val) {
    if (!j.contains(key))
      j[key] = val;
    return j[key];
  }

  template <typename T> T get(const std::string& key, const T& val) const {
    if (!j.contains(key))
      return val;
    return j[key];
  }

  template <typename T> T get(const std::string& key) const {
    return j[key];
  }

  std::string get(const std::string& key, const char* val) {
    return get<std::string>(key,std::string(val));
  }

  std::string get(const std::string& key, const char* val) const {
    return get<std::string>(key,std::string(val));
  }

  ptree get_child(const std::string& key) const {
    ptree p;
    p.j = j[key];
    return p;
  }

  ptree get_child_optional(const std::string& key) const {
    ptree p;
    if (j.contains(key))
      p.j = j[key];
    return p;
  }

  template <typename T> void add(const std::string& key, const T& val) {
    j[key] = val;
  }

  int count(const std::string& key) const { return j.count(key); }

  std::string dump() const { return j.dump(); }

  void parse(const std::string& s) { j = nlohmann::json::parse(s); }

  void validate(const ptree& schema) const {
    nlohmann::json_schema::json_validator validator;
    validator.set_root_schema(schema.j);
    validator.validate(j);
  }
};

inline void read_json(const std::string& file, ptree& input) { input.read(file); }
inline void write_json(std::ostream& os, const ptree& input) { input.write(os); }

}
