/*
 * (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 SymbolAdvancedTableMode.h
    \brief Implementation of the Template class SymbolAdvancedTableMode.

    Magics Team - ECMWF 2004

    Started: Wed 21-Jan-2004

    Changes:

*/


#include "SymbolAdvancedTableMode.h"
#include "CartesianTransformation.h"
#include "HistoVisitor.h"
#include "Histogram.h"
#include "LegendVisitor.h"
#include "Symbol.h"
#include "SymbolPlotting.h"
#include "UserPoint.h"
using namespace magics;

map<string, SymbolAdvancedTableMode::TextHandler> SymbolAdvancedTableMode::textHandlers_;

SymbolAdvancedTableMode::SymbolAdvancedTableMode() {
    /*
    if ( textHandlers_.empty() ) {
        textHandlers_["none"] = &SymbolAdvancedTableMode::none;
        textHandlers_["centre"] = &SymbolAdvancedTableMode::centre;
        textHandlers_["top"] = &SymbolAdvancedTableMode::top;
        textHandlers_["bottom"] = &SymbolAdvancedTableMode::bottom;
        textHandlers_["right"] = &SymbolAdvancedTableMode::right;
    }
    */
}

SymbolAdvancedTableMode::~SymbolAdvancedTableMode() {}

/*!
 Class information are given to the output-stream.
*/
void SymbolAdvancedTableMode::print(ostream& out) const {
    out << "SymbolAdvancedTableMode[";
    SymbolAdvancedTableModeAttributes::print(out);
    out << "]";
}


SymbolProperties SymbolAdvancedTableMode::operator()(double value) const {
    return map_.find(value, SymbolProperties());
}


bool SymbolAdvancedTableMode::accept(double value) const {
    return map_.accept(value);
}


void SymbolAdvancedTableMode::prepare() {}


void SymbolAdvancedTableMode::adjust(double min, double max, bool scale, const Transformation& transformation,
                                     double scaling) {
    static map<string, TextSymbol::TextPosition> texthandlers;
    if (texthandlers.empty()) {
        texthandlers["none"]   = TextSymbol::M_NONE;
        texthandlers["left"]   = TextSymbol::M_LEFT;
        texthandlers["top"]    = TextSymbol::M_ABOVE;
        texthandlers["bottom"] = TextSymbol::M_BELOW;
        texthandlers["right"]  = TextSymbol::M_RIGHT;
    }
    map_.clear();
    MagLog::dev() << "Data going from " << min << " to " << max << endl;
    levels_->set(*this);


    levels_->calculate(min, max, false);
    if (levels_->size() == 1) {
        levels_->push_back(levels_->front());
    }
    colourMethod_->set(*this);
    height_method_->set(*this);
    colourMethod_->prepare(*levels_, *levels_);
    height_method_->prepare(*levels_);

    if (markers_.empty()) {
        markers_.push_back(15);
    }
    if (markers_names_.empty()) {
        markers_names_.push_back("ww_01");
    }

    TextSymbol::TextPosition position;


    if (text_list_.empty()) {
        position = TextSymbol::M_NONE;
        text_list_.push_back("");
    }
    else {
        string type                                         = lowerCase(text_display_type_);
        map<string, TextSymbol::TextPosition>::iterator pos = texthandlers.find(type);
        position = (pos != texthandlers.end()) ? pos->second : TextSymbol::M_NONE;
    }

    MagFont font(text_font_name_);
    font.colour(*text_font_colour_);
    font.size(text_font_size_);
    font.style(text_font_style_);


    LevelSelection::const_iterator level       = levels_->begin();
    vector<int>::const_iterator marker         = markers_.begin();
    vector<string>::const_iterator marker_name = markers_names_.begin();
    const bool index                           = magCompare(parent_->marker_mode_, "index");
    SymbolProperties last;
    vector<string>::const_iterator text = text_list_.begin();
    while (true) {
        if (level + 1 == levels_->end())
            break;

        MagLog::debug() << "[" << *level << ", " << *(level + 1) << "]=" << *marker << "(marker)" << *text << "(text)"
                        << endl;
        double height = height_method_->height(*level);
        if (scale)
            height = transformation.ratio() * height * scaling;
        SymbolProperties properties;
        if (index)
            properties = SymbolProperties(colourMethod_->right(*level), height, *marker, *text);
        else
            properties = SymbolProperties(colourMethod_->right(*level), height, *marker_name, *text);

        properties.position_ = position;
        properties.font_     = font;

        properties.outline_          = parent_->outline_;
        properties.outlineColour_    = *parent_->outline_colour_;
        properties.outlineStyle_     = parent_->outline_style_;
        properties.outlineThickness_ = parent_->outline_thickness_;

        properties.connectLine_ = parent_->connect_;
        properties.connectLineColour_ =
            (parent_->automatic_connect_colour_) ? colourMethod_->right(*level) : *parent_->connect_colour_;
        properties.connectLineStyle_         = parent_->connect_style_;
        properties.connectLineThickness_     = parent_->connect_thickness_;
        properties.blanking_                 = parent_->text_blanking_;
        last                                 = properties;
        map_[Interval(*level, *(level + 1))] = properties;

        if (marker + 1 == markers_.end()) {
            if (marker_policy_ == M_CYCLE)
                marker = markers_.begin();
        }
        else
            marker++;
        if (marker_name + 1 == markers_names_.end()) {
            if (marker_policy_ == M_CYCLE)
                marker_name = markers_names_.begin();
        }
        else
            marker_name++;
        if (text + 1 == text_list_.end()) {
            if (text_policy_ == M_CYCLE)
                text = text_list_.begin();
        }
        else
            text++;
        level++;
    }
    // Here we add a last interval to close to the right the lst Interval.

    MagLog::debug() << "[" << *level << ", " << (*level) + EPSILON << "]=" << colourMethod_->right(*level) << *marker
                    << "(marker)" << *text << "(text)" << endl;
    map_[Interval(*level, (*level) + EPSILON)] = last;
}

void build(const IntervalMap<SymbolProperties>& in, IntervalMap<Colour>& out) {
    // Here we want to get the last interval ;


    for (IntervalMap<SymbolProperties>::const_iterator interval = in.begin(); interval != in.end(); ++interval)
        out.insert(make_pair(Interval(interval->first.min_, interval->first.max_), interval->second.colour_));
}


void SymbolAdvancedTableMode::visit(Data& data, LegendVisitor& legend) {
    legend.newLegend();
    IntervalMap<SymbolProperties>::const_iterator last = map_.end();
    --last;
    IntervalMap<SymbolProperties>::iterator first = map_.begin();

    switch (legend.legendType()) {
        case LegendMethod::CONTINUOUS: {
            for (IntervalMap<SymbolProperties>::const_iterator 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.colour_);
                box->setFilled(true);

                legend.add(new BoxEntry(min, max, box));
            }
            break;
        }
        case LegendMethod::DISJOINT: {
            for (IntervalMap<SymbolProperties>::iterator interval = first; interval != last; ++interval) {
                if (magCompare(interval->second.marker_, "none"))
                    // ignore entry
                    continue;
                Symbol* symbol = new Symbol();
                (*symbol).setColour(interval->second.colour_);
                (*symbol).setSymbol(interval->second.marker_);
                (*symbol).setHeight(interval->second.height_);

                string str = data.legendText(interval->first.min_, interval->first.max_);
                if (str.empty()) {
                    ostringstream text;
                    text << interval->first.min_ << "-" << interval->first.max_;
                    str = text.str();
                }
                legend.add(
                    new SimpleSymbolEntry(interval->second.label_.empty() ? str : interval->second.label_, symbol));
            }
            break;
        }
        case LegendMethod::HISTOGRAM: {
            IntervalMap<Colour> beans;
            build(map_, beans);
            Histogram helper;
            // here we need the transformation.. to know wich points where displayed!
            IntervalMap<int>& histogram = helper.histogram(beans, data.points(legend.transformation(), false));
            double mean                 = helper.mean();

            int maxh = 0;
            for (IntervalMap<int>::const_iterator interval = histogram.begin(); interval != histogram.end();
                 ++interval) {
                if (maxh < interval->second)
                    maxh = interval->second;
            }
            bool first = true;
            for (IntervalMap<SymbolProperties>::const_iterator 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.colour_);
                box->setFilled(true);
                BoxEntry* entry = new BoxEntry(min, max, box);
                int count       = histogram.find(min, -1);
                // here we have to add the last interval!
                if (max == levels_->back()) {
                    count += histogram.find(max, 0);
                }
                entry->population(count);
                entry->totalPopulation(maxh);
                if (min <= mean && mean <= max) {
                    entry->mean(mean);
                }
                if (first) {
                    entry->first();
                    first = false;
                }


                legend.add(entry);
            }
            break;
        }
    }
    legend.last();
}


void SymbolAdvancedTableMode::visit(Data& data, HistoVisitor& visitor) {
    IntervalMap<Colour> beans;
    if (!visitor.basic())  // Why did we put this possibility???
        build(map_, beans);
    Histogram helper;
    helper.visit(beans, data, data.points(*visitor.dataLayoutTransformation(), false), visitor);
}
