/*
 * (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 "metkit/mars/StepRange.h"
#include "metkit/mars/TypeTime.h"
#include "metkit/mars/TypeRange.h"

#include "eckit/exception/Exceptions.h"
#include "eckit/persist/DumpLoad.h"
#include "eckit/utils/Tokenizer.h"

using namespace eckit;


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

namespace {

enum TimeUnit {
    Second = 0,
    Minute = 1,
    Hour = 2,
    Day = 3
};

TimeUnit maxUnit(const eckit::Time& t) {
    if (t.seconds() == 0) {
        if (t.minutes() == 0) {
            if (t.hours() == 0) {
                return TimeUnit::Day;
            }
            return TimeUnit::Hour;
        }
        return TimeUnit::Minute;
    }
    return TimeUnit::Second;
}

std::string canonical(const eckit::Time& time) {

    long h = time.hours();
    long m = time.minutes();
    long s = time.seconds();

    std::string out = "";
    if (h!=0 || (m==0 && s==0)) {
        out += std::to_string(h);
        if (m!=0 || s!=0) {
            out += "h";
        }
    }
    if (m!=0) {
        out += std::to_string(m) + "m";
    }
    if (s!=0) {
        out += std::to_string(s) + "s";
    }
    return out;
}

std::string canonical(const eckit::Time& time, TimeUnit unit) {
    switch (unit) {
        case TimeUnit::Second:
            return std::to_string(time.seconds()+60*time.minutes()+3600*time.hours()) + "s";
        case TimeUnit::Minute:
            return std::to_string(time.minutes()+60*time.hours()) + "m";
        case TimeUnit::Day:
        case TimeUnit::Hour:
        default:
            return std::to_string(time.hours());
    }
}

}

namespace metkit::mars {


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

StepRange::operator std::string() const
{
    std::ostringstream os;
    os << *this;
    return os.str();
}

void StepRange::print(std::ostream& s) const
{
	if(from_ == to_) {
        s << canonical(eckit::Time(from_*3600, true));
    }
    else {
        eckit::Time f{static_cast<long>(from_*3600.), true};
        eckit::Time t{static_cast<long>(to_*3600.), true};

        TimeUnit unit = std::min(maxUnit(f), maxUnit(t));
        s << canonical(f, unit) << '-' << canonical(t, unit);
    }
}

StepRange::StepRange(const std::string& s):
	from_(0.),
	to_(0.)
{
	Tokenizer parse("-");
	std::vector<std::string> result;

	parse(s,result);

	switch(result.size())
	{
		case 1:
			to_ = from_ = eckit::Time(result[0], true)/3600.;
			break;

		case 2:
			from_ = eckit::Time(result[0], true)/3600.;
			to_   = eckit::Time(result[1], true)/3600.;
			break;

		default:
            std::ostringstream msg;
            msg << "Bad StepRange [" << s << "]";
            throw eckit::BadValue(msg.str(), Here());
            break;
    }
}

void StepRange::dump(DumpLoad& a) const
{
	a.dump(from_);
	a.dump(to_);
}

void StepRange::load(DumpLoad& a)
{
	a.load(from_);
	a.load(to_);
}

//----------------------------------------------------------------------------------------------------------------------
} // namespace metkit::mars
