/*
 * (C) Copyright 1996-2016 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.
 */

/*! \file WindPlotting.cc
    \brief Implementation of the Template class Wind.

    Magics Team - ECMWF 2005

    Started: Thu 17-Mar-2005

    Changes:

*/

#include "WindPlotting.h"
#include "CustomisedPoint.h"
#include "LegendVisitor.h"
#include "Polyline.h"

using namespace magics;

map<string, WindPlotting::AdvancedMethod> WindPlotting::methods_;
map<string, WindPlotting::ColouringMethod> WindPlotting::colouringMethods_;
map<string, WindPlotting::MinMaxMethod> WindPlotting::minMaxMethods_;
map<string, WindPlotting::SettingMethod> WindPlotting::settingMethods_;

WindPlotting::WindPlotting() : legendOnly_(false) {
    if (methods_.empty()) {
        methods_["on"]       = &WindPlotting::advanced;
        methods_["advanced"] = &WindPlotting::advanced;
        methods_["off"]      = &WindPlotting::off;
    }
    if (minMaxMethods_.empty()) {
        minMaxMethods_["on"]       = &WindPlotting::advancedMinMax;
        minMaxMethods_["advanced"] = &WindPlotting::advancedMinMax;
        minMaxMethods_["off"]      = &WindPlotting::offMinMax;
    }
    if (settingMethods_.empty()) {
        settingMethods_["on"]       = &WindPlotting::setAdvanced;
        settingMethods_["advanced"] = &WindPlotting::setAdvanced;
        settingMethods_["off"]      = &WindPlotting::setNormal;
    }
    if (colouringMethods_.empty()) {
        colouringMethods_["speed"]     = &WindPlotting::speed;
        colouringMethods_["parameter"] = &WindPlotting::parameter;
    }
}

void WindPlotting::advancedMinMax(double& min, double& max) {
    min = (minSpeed() == -1.e21) ? min : minSpeed();
    max = (maxSpeed() == 1.e21) ? max : maxSpeed();
    ;
}
void WindPlotting::offMinMax(double& min, double& max) {
    if (min < minSpeed())
        min = minSpeed();
    if (max > maxSpeed())
        max = maxSpeed();
}
void WindPlotting::setAdvanced(double& min, double& max) {
    advancedMinMax(min, max);
    levels_->set(*this);
    colourMethod_->set(*this);
    levels_->calculate(min, max, false);
    LevelSelection::const_iterator level = levels_->begin();
    colourMethod_->prepare(*levels_, *levels_);
    Colour last;
    map_.clear();
    while (true) {
        if (level + 1 == levels_->end())
            break;
        MagLog::debug() << "[" << *level << ", " << *(level + 1) << "]=" << colourMethod_->right(*level) << endl;
        map_[Interval(*level, *(level + 1))] = colourMethod_->right(*level);
        last                                 = colourMethod_->right(*level);
        ++level;
    }
    // we add a last small one for the maax
    map_[Interval(levels_->back(), levels_->back() + EPSILON)] = last;
}

void WindPlotting::adjust(CustomisedPointsList& points, const Transformation& transformation) {
    // MagLog::dev() << "Data going from " << min << " to " << max << endl;

    vector<double> values;
    if (points.empty())
        return;

    for (const auto& point : points) {
        if (transformation.in(point->longitude(), point->latitude()))
            values.push_back(value((*point)["x_component"], (*point)["y_component"], (*point)["colour_component"]));
    }

    if (values.empty())
        return;

    double min = *std::min_element(values.begin(), values.end());
    double max = *std::max_element(values.begin(), values.end());

    string value = lowerCase(advanced_method_);


    map<string, SettingMethod>::const_iterator set = settingMethods_.find(value);
    if (set == settingMethods_.end()) {
        MagLog::warning() << advanced_method_
                          << " is not valid value for  'wind_advanced_method': reset to default[off]" << endl;
        setNormal(min, max);
    }
    else
        (this->*set->second)(min, max);
}


Colour& WindPlotting::colour(Colour& colour, double x, double y, double col) {
    string value                                       = lowerCase(advanced_method_);
    map<string, AdvancedMethod>::const_iterator method = methods_.find(value);
    if (method == methods_.end()) {
        MagLog::warning() << advanced_method_
                          << " is not valid value for  'wind_advanced_method': reset to default[off]" << endl;
        return off(colour, x, y, col);
    }
    else
        return (this->*method->second)(colour, x, y, col);
}

double WindPlotting::parameter(double, double, double col) {
    return col;
}

double WindPlotting::speed(double x, double y, double) {
    return sqrt(x * x + y * y);
}

double WindPlotting::value(double x, double y, double col) {
    string value                                        = lowerCase(colour_method_);
    map<string, ColouringMethod>::const_iterator method = colouringMethods_.find(value);
    if (method == colouringMethods_.end()) {
        MagLog::warning() << colour_method_
                          << " is not valid value for  'wind_advanced_colour_parameter': reset to default[speed]"
                          << endl;
        return speed(x, y, col);
    }
    else
        return (this->*method->second)(x, y, col);
}

Colour& WindPlotting::advanced(Colour& colour, double u, double v, double col) {
    static Colour x("red");

    x = map_.find(value(u, v, col), colour);
    return x;
}

void WindPlotting::visit(LegendVisitor& legend) {
    if (!legend_)
        return;

    if (magCompare(advanced_method_, "advanced") || magCompare(advanced_method_, "on")) {
        legend.newLegend();
        if (map_.empty()) {
            // no legend to plot
            return;
        }
        IntervalMap<Colour>::const_iterator interval;
        IntervalMap<Colour>::const_iterator last = map_.end();
        --last;
        for (interval = map_.begin(); interval != last; ++interval) {
            Polyline* box = new Polyline();

            double min = interval->first.min_;
            double max = interval->first.max_;

            box->setShading(new FillShadingProperties());

            box->setFillColour(interval->second);
            box->setFilled(true);

            legend.add(new BoxEntry(min, max, box));
        }
        legend.back()->last();
    }
}
