/*
 * (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/ListContent.h"
#include "eckit/parser/JSON.h"

#include "eckit/utils/Hash.h"


namespace eckit {

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

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


ListContent::ListContent()
{
}

ListContent::ListContent(const ValueList & v)
{
    std::copy(v.begin(), v.end(), std::back_inserter(value_));
}

ListContent::ListContent(const Value& v)
{
    value_.push_back(v);
}

ListContent::ListContent(Stream& s):
    Content(s)
{
    long count;
    s >> count;
    for (int i = 0; i < count; i++) {
        value_.push_back(Value(s));
    }

}

Content* ListContent::clone() const {
    ValueList v;
    v.reserve(value_.size());
    for (size_t i = 0; i < value_.size(); ++i) {
        v.push_back(value_[i].clone());
    }
    return new ListContent(v);
}

void ListContent::encode(Stream& s) const
{
    Content::encode(s);
    long count = value_.size();
    s << count;
    for (int i = 0; i < count; ++i) {
        s << value_[i];
    }

}

ListContent::~ListContent()
{
}

size_t ListContent::size() const {
    return value_.size();
}

void ListContent::value(ValueList& v) const
{
    v = value_;
}

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

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

}

void ListContent::json(JSON& s) const
{
    s.startList();

    for (size_t i = 0; i < value_.size(); i++) {
        s << value_[i];
    }

    s.endList();
}


void ListContent::print(std::ostream& s) const
{
    s << '(';

    for (size_t i = 0; i < value_.size(); i++)
    {
        if (i > 0) {s << ',';}
        s << value_[i];
    }

    s << ')';
}


Content* ListContent::add(const Content& other) const
{
    return other.addList(*this);
}

Content* ListContent::addList(const ListContent& other) const
{
    ValueList tmp;
    std::copy(other.value_.begin(), other.value_.end(), std::back_inserter(tmp));
    std::copy(value_.begin(), value_.end(), std::back_inserter(tmp));
    return new ListContent(tmp);
}

Content* ListContent::sub(const Content& other) const
{
    return other.subList(*this);
}

Content* ListContent::mul(const Content& other) const
{
    return other.mulList(*this);
}

Content* ListContent::div(const Content& other) const
{
    return other.divList(*this);
}

Content* ListContent::mod(const Content& other) const
{
    return other.modList(*this);
}


void ListContent::value(long long& n) const
{
    if (value_.size() == 1) {n = value_[0];}
    else {Content::value(n);}
}

void ListContent::value(bool& n) const
{
    if (value_.size() == 1) {n = value_[0];}
    else {Content::value(n);}
}

void ListContent::value(double& n) const
{
    if (value_.size() == 1) {n = value_[0];}
    else {Content::value(n);}
}

void ListContent::value(std::string& n) const
{
    if (value_.size() == 1) {n = std::string(value_[0]);}
    else {Content::value(n);}
}

void ListContent::value(Date& n) const
{
    if (value_.size() == 1) {n = value_[0];}
    else {Content::value(n);}
}

void ListContent::value(Time& n) const
{
    if (value_.size() == 1) {n = value_[0];}
    else {Content::value(n);}
}

void ListContent::value(DateTime& n) const
{
    if (value_.size() == 1) {n = value_[0];}
    else {Content::value(n);}
}

Value& ListContent::element(const Value& v)
{
    long long n = v;
    ASSERT( n >= 0 && (size_t) n < value_.size() );
    return value_.at(n);
}

bool ListContent::contains(const Value& v) const {
    long long n = v;
    return ( n >= 0 && (size_t) n < value_.size() );
}

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

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

    out << '[' << std::endl;

    for (size_t i = 0; i < value_.size(); i++)
    {
        if (i > 0) {
            out << ',' << std::endl;
        }
        value_[i].dump(out, depth + 3);
    }

    out << ']';
}

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

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

} // namespace eckit

