/*
 * (C) Copyright 1996- ECMWF.
 *
 * This software is licensed under the terms of the Apache Licence Version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
 * In applying this licence, ECMWF does not waive the privileges and immunities
 * granted to it by virtue of its status as an intergovernmental organisation nor
 * does it submit to any jurisdiction.
 */


#include "eckit/value/MapContent.h"
#include "eckit/log/JSON.h"

#include "eckit/utils/Hash.h"


namespace eckit {

//----------------------------------------------------------------------------------------------------------------------


ClassSpec MapContent::classSpec_ = {
    &Content::classSpec(),
    "MapContent",
};
Reanimator<MapContent> MapContent::reanimator_;


MapContent::MapContent() {}

MapContent::MapContent(const ValueMap& v) :
    value_(v) {}


MapContent::MapContent(Stream& s) :
    Content(s) {
    bool more;
    s >> more;
    while (more) {
        Value k(s);
        Value v(s);
        value_[k] = v;
        s >> more;
    }
}

void MapContent::encode(Stream& s) const {
    Content::encode(s);
    for (ValueMap::const_iterator j = value_.begin(); j != value_.end(); ++j) {
        s << true;
        s << (*j).first;
        s << (*j).second;
    }
    s << false;
}

MapContent::~MapContent() {}

void MapContent::value(ValueMap& v) const {
    v = value_;
}


Value MapContent::keys() const {
    ValueList list;
    for (ValueMap::const_iterator j = value_.begin(); j != value_.end(); ++j) {
        list.push_back((*j).first);
    }
    return Value::makeList(list);
}


Value MapContent::remove(const Value& key) {
    Value result = value_[key];
    value_.erase(key);
    return result;
}


Value& MapContent::element(const Value& key) {
    return value_[key];
}

bool MapContent::contains(const Value& key) const {
    return value_.find(key) != value_.end();
}

int MapContent::compare(const Content& other) const {
    return -other.compareMap(*this);
}

int MapContent::compareMap(const MapContent& other) const {
    if (value_ == other.value_) {
        return 0;
    }
    if (value_ < other.value_) {
        return -1;
    }
    return 1;
}

void MapContent::print(std::ostream& s) const {
    s << '{';
    for (ValueMap::const_iterator j = value_.begin(); j != value_.end(); ++j) {
        if (j != value_.begin()) {
            s << " , ";
        }
        s << (*j).first;
        s << " => ";
        s << (*j).second;
    }
    s << '}';
}

void MapContent::json(JSON& s) const {
    s.startObject();
    for (ValueMap::const_iterator j = value_.begin(); j != value_.end(); ++j) {
        s << (*j).first;
        s << (*j).second;
    }
    s.endObject();
}


Content* MapContent::clone() const {
    ValueMap v;
    for (ValueMap::const_iterator j = value_.begin(); j != value_.end(); ++j) {
        v[(*j).first.clone()] = (*j).second.clone();
    }
    return new MapContent(v);
}


Content* MapContent::add(const Content& other) const {
    return other.addMap(*this);
}

Content* MapContent::sub(const Content& other) const {
    return other.subMap(*this);
}

Content* MapContent::mul(const Content& other) const {
    return other.mulMap(*this);
}

Content* MapContent::div(const Content& other) const {
    return other.divMap(*this);
}

Content* MapContent::mod(const Content& other) const {
    return other.modMap(*this);
}


void MapContent::dump(std::ostream& out, size_t depth, bool indent) const {

    if (indent) {
        size_t n = depth;
        while (n-- > 0) {
            out << ' ';
        }
    }

    out << "{";
    const char* sep = "\n";

    for (ValueMap::const_iterator j = value_.begin(); j != value_.end(); ++j) {
        out << sep;
        (*j).first.dump(out, depth + 3);
        out << ": ";
        (*j).second.dump(out, depth + 3, false);
        sep = ",\n";
    }

    if (!value_.empty()) {
        out << '\n';
        size_t n = depth;
        while (n-- > 0) {
            out << ' ';
        }
    }

    out << "}";
}

void MapContent::hash(Hash& h) const {
    for (auto v : value_) {
        v.first.hash(h);
        v.second.hash(h);
    }
}

//----------------------------------------------------------------------------------------------------------------------

}  // namespace eckit
