/*
 * (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 "ArrowPlotting.h"
#include "Histogram.h"
#include "LegendVisitor.h"

using namespace magics;

void ArrowPlotting::operator()(bool north, const PaperPoint& point, double x, double y, double val) {
    const double speed = this->speed(x, y, val);
    if (maxVelocity_ < speed)
        maxVelocity_ = speed;

    double value = this->value(x, y, val);

    if (!levels_->empty() && (value < levels_->front() || value > levels_->back()))
        return;
    if (speed < min_speed_ || speed > max_speed_)
        return;

    if ((*this->calm_)(point, x, y))
        return;


    Colour colour = this->colour(*colour_, x, y, val);
    //// RV Ajout support fleches a taille fixe ////
    if (fixed_velocity_ > 0 && !(zero(fixed_velocity_)) && !(zero(speed))) {
        double ratio = fixed_velocity_ / speed;
        x *= ratio;
        y *= ratio;
    }
    //// RV ////
    if (north)
        northArrow(colour)->push_back(ArrowPoint(x, y, point));
    else
        southArrow(colour)->push_back(ArrowPoint(x, y, point));
}


//! Setting for south Hemisphere wind
Arrow* ArrowPlotting::southArrow(const Colour& colour) {
    map<Colour, Arrow*>::iterator arrow = southArrows_.find(colour);
    if (arrow != southArrows_.end())
        return arrow->second;

    Arrow* south = new Arrow();
    south->setColour(colour);
    south->setThickness(this->thickness_);
    south->setStyle(this->style_);
    south->setScale(this->unit_velocity_);
    south->setHemisphere(Hemisphere::SOUTH);
    south->setArrowPosition(this->origin_position_);
    south->setHeadIndex(this->head_);
    south->setHeadRatio(this->ratio_);
    southArrows_.insert(make_pair(colour, south));
    return south;
}


//! Setting for north Hemisphere wind
Arrow* ArrowPlotting::northArrow(const Colour& colour) {
    map<Colour, Arrow*>::iterator arrow = northArrows_.find(colour);
    if (arrow != northArrows_.end())
        return arrow->second;

    Arrow* north = new Arrow();
    north->setColour(colour);
    north->setThickness(this->thickness_);
    north->setStyle(this->style_);
    north->setScale(this->unit_velocity_);
    north->setHemisphere(Hemisphere::NORTH);
    north->setArrowPosition(this->origin_position_);
    north->setHeadIndex(this->head_);
    north->setHeadRatio(this->ratio_);
    northArrows_.insert(make_pair(colour, north));
    return north;
}

void ArrowPlotting::prepare(BasicGraphicsObjectContainer& out) {
    // Calm Indicator
    (*this->calm_).colour(*this->colour_);
    (*this->calm_).height(this->calm_indicator_size_);
    (*this->calm_).below(this->calm_below_);
    (*this->calm_).prepare(out);
    // We also empty the containers to get a clean new plot
    northArrows_.clear();
    southArrows_.clear();
}

void ArrowPlotting::prepare(BasicGraphicsObjectContainer& out, double res) {
    // Calm Indicator
    (*this->calm_).colour(*this->colour_);
    (*this->calm_).height(this->calm_indicator_size_);
    (*this->calm_).below(this->calm_below_);
    (*this->calm_).prepare(out);

    if (magCompare(unit_system_, "automatic")) {
        // adjust the scaling factor!

        this->unit_velocity_ /= res;
        MagLog::dev() << "unit_velocity_-->" << this->unit_velocity_ << endl;
    }
    else if (magCompare(unit_system_, "paper")) {
        // adjust the scaling factor!
        // this->unit_velocity_ /= out.transformation().unitToCm(out.absoluteWidth(), out.absoluteHeight());
        MagLog::dev() << "unit_velocity_-->" << this->unit_velocity_ << endl;
    }

    this->maxVelocity_ = -1;

    // We also empty the containers to get a clean new plot
    northArrows_.clear();
    southArrows_.clear();
}


struct SortHelper {
    SortHelper() {}
    ~SortHelper() {}
    MAGICS_NO_EXPORT bool operator()(const Arrow* first, const Arrow* second) { return first->size() > second->size(); }
};

void ArrowPlotting::finish(BasicGraphicsObjectContainer& out) {
    if (legendOnly_) {
        // and now we reset
        northArrows_.clear();
        southArrows_.clear();
        return;
    }
    unit_velocity_ = (unit_velocity_ == 0) ? maxVelocity_ : unit_velocity_;
    vector<Arrow*> arrows;
    for (map<Colour, Arrow*>::iterator arrow = northArrows_.begin(); arrow != northArrows_.end(); ++arrow)
        arrows.push_back(arrow->second);

    for (map<Colour, Arrow*>::iterator arrow = southArrows_.begin(); arrow != southArrows_.end(); ++arrow)
        arrows.push_back(arrow->second);

    std::sort(arrows.begin(), arrows.end(), SortHelper());

    // Now we feed the task...
    for (vector<Arrow*>::iterator arrow = arrows.begin(); arrow != arrows.end(); ++arrow) {
        if (!(*arrow)->empty()) {
            (*arrow)->setScale(this->unit_velocity_);
            out.push_back(*arrow);
        }
    }
}


void ArrowPlotting::visit(LegendVisitor& legend) {
    if (!this->legend_)
        return;

    WindPlotting::visit(legend);
    // const double scale = legend.transformation().unitToCm(legend.absoluteWidth(), legend.absoluteHeight());
    const double scale = 1.;

    const double speed = (this->unit_velocity_) ? this->unit_velocity_ : this->maxVelocity_;
    Arrow* arrow       = new Arrow();
    (*arrow).setColour(*this->colour_);
    arrow->setThickness(this->thickness_);
    arrow->setScale(scale);

    arrow->setArrowPosition(this->origin_position_);
    arrow->setHeadIndex(this->head_);
    arrow->setHeadRatio(this->ratio_);

    if (legend_text_.empty() || legend_text_ == "vector") {
        ostringstream text;
        text << speed << " " << legend_unit_;
        legend_text_ = text.str();
    }

    LegendEntry* entry = new ArrowEntry(legend_text_, arrow);

    legend.add(entry);
    legend.add(new EmptyEntry());
}


void ArrowPlotting::visit(Data& data, PointsHandler& points, HistoVisitor& visitor) {
    IntervalMap<Colour> beans;
    if (!visitor.basic()) {
        vector<double>::iterator from = this->levels_->begin();
        vector<double>::iterator to   = this->levels_->begin();
        ++to;
        for (; to != this->levels_->end(); ++to) {
            Colour colour;
            if (magCompare(advanced_method_, "advanced") || magCompare(advanced_method_, "on")) {
                colour = colourMethod_->right(*from);
            }
            else {
                colour = *this->colour_;
                ;
            }
            beans.insert(make_pair(Interval(*from, *to), colour));
            ++from;
        }
    }

    Histogram helper;
    helper.visit(beans, data, points, visitor);
}
