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

    Magics Team - ECMWF 2005

    Started: Fri 16-Sep-2005

    Changes:

*/

#include "InputMatrixInterpretor.h"
#include <limits>
#include "GribDecoder.h"
#include "InputMatrix.h"

using namespace magics;

InputMatrixInterpretor::InputMatrixInterpretor() {
    mappers_["upper_left"]             = &InputMatrixInterpretor::upperLeft;
    mappers_["lower_left"]             = &InputMatrixInterpretor::lowerLeft;
    mappers_["upper_right"]            = &InputMatrixInterpretor::upperRight;
    mappers_["lower_right"]            = &InputMatrixInterpretor::lowerRight;
    mappers_["upper_left_transposed"]  = &InputMatrixInterpretor::upperLeftTransposed;
    mappers_["lower_left_transposed"]  = &InputMatrixInterpretor::lowerLeftTransposed;
    mappers_["upper_right_transposed"] = &InputMatrixInterpretor::upperRightTransposed;
    mappers_["lower_right_transposed"] = &InputMatrixInterpretor::lowerRightTransposed;
}

InputMatrixInterpretor::~InputMatrixInterpretor() {}

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


InputMatrixRegularInterpretor::InputMatrixRegularInterpretor() {}

InputMatrixRegularInterpretor::~InputMatrixRegularInterpretor() {}

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

Matrix* InputMatrixRegularInterpretor::geoInterpret(Matrix* in, const InputMatrix& info) {
    std::map<string, Mapper>::iterator mapper = mappers_.find(lowerCase(info.mapping_));

    in->akimaEnabled();


    if (mapper == mappers_.end())
        MagLog::warning() << "unknow input matrix mapping " << info.mapping_ << endl;
    else
        (this->*mapper->second)();


    if (in->columnsAxis().empty() == false)
        // The initialisation has already been done return;
        return in;

    // Apply scaling !
    double scaling;
    double offset;
    info.scaling(scaling, offset);
    for (int i = 0; i < in->size(); ++i)
        (*in)[i] = ((*in)[i] * scaling) + offset;

    if (longitudes_.size() && latitudes_.size()) {
        for (auto lon = longitudes_.begin(); lon != longitudes_.end(); ++lon) {
            in->columnsAxis().push_back(*lon);
        }
        for (auto lat = latitudes_.begin(); lat != latitudes_.end(); ++lat) {
            in->rowsAxis().push_back(*lat);
        }
    }

    else {
        int nblon  = in->columns();
        double lon = longitude_;

        for (int i = 0; i < nblon; i++) {
            lon = longitude_ + (i * longitude_step_);
            in->columnsAxis().push_back(lon);
        }

        int nblat  = in->rows();
        double lat = latitude_;

        for (int i = 0; i < nblat; i++) {
            lat = latitude_ + (i * latitude_step_);
            in->rowsAxis().push_back(lat);
        }
    }

    in->setMapsAxis();
    in->missing(std::numeric_limits<double>::max());
    return in;
}

Matrix* InputMatrixRegularInterpretor::xyInterpret(Matrix* in, const InputMatrix& info) {
    in->akimaEnabled();
    in->missing(std::numeric_limits<double>::max());
    if (!in->rowsAxis().empty())
        // WE have already initialised the matrix ..
        return in;
    if (x_coords_.size() == in->columns() && y_coords_.size() == in->rows()) {
        in->setRowsAxis(y_coords_);
        in->setColumnsAxis(x_coords_);

        return in;
        // the user has defined the vector of coordinates
        // We use them
    }
    // if y a date or an normal axis..

    if (!y_first_date_.empty()) {
        DateTime ref(dateY_);
        DateTime from(y_first_date_);
        DateTime to(y_last_date_);
        y_first_ = from - ref;
        y_last_  = to - ref;
    }
    if (!x_first_date_.empty()) {
        DateTime ref(dateX_);
        DateTime from(x_first_date_);
        DateTime to(x_last_date_);
        x_first_ = from - ref;
        x_last_  = to - ref;
    }
    double y    = y_first_;
    double step = (y_last_ - y_first_) / (in->rows() - 1);
    for (int i = 0; i < in->rows(); i++) {
        y = y_first_ + (i * step);
        in->rowsAxis().push_back(y);
    }

    double x = x_first_;
    step     = (x_last_ - x_first_) / (in->columns() - 1);
    for (int i = 0; i < in->columns(); i++) {
        x = x_first_ + (i * step);
        in->columnsAxis().push_back(x);
        x += step;
    }
    in->missing(std::numeric_limits<double>::max());
    in->setMapsAxis();
    return in;
}


InputMatrixIrregularInterpretor::InputMatrixIrregularInterpretor() {}

InputMatrixIrregularInterpretor::~InputMatrixIrregularInterpretor() {}

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

Matrix* InputMatrixIrregularInterpretor::geoInterpret(Matrix* in, const InputMatrix& info) {
    ProjectedMatrix* matrix = new ProjectedMatrix(in->rows(), in->columns());
    vector<double>& values  = matrix->values();
    vector<double>& rows    = matrix->rowsArray();
    vector<double>& columns = matrix->columnsArray();

    double scaling;
    double offset;
    info.scaling(scaling, offset);
    for (int i = 0; i < in->size(); ++i)
        (*in)[i] = ((*in)[i] * scaling) + offset;
    for (vector<double>::iterator val = in->begin(); val != in->end(); ++val) {
        values.push_back(((*val) * scaling) + offset);
    }


    for (vector<double>::iterator y = latitudes_.begin(); y != latitudes_.end(); ++y) {
        // MagLog::dev() << *y << endl;
        rows.push_back(*y);
    }

    for (vector<double>::iterator x = longitudes_.begin(); x != longitudes_.end(); ++x) {
        // MagLog::dev() << *x << endl;
        columns.push_back(*x);
    }

    matrix->missing(std::numeric_limits<double>::max());
    matrix->getReady();
    return matrix;
}

Matrix* InputMatrixIrregularInterpretor::xyInterpret(Matrix* in, const InputMatrix& info) {
    // here we have to create a projected matrix!

    ProjectedMatrix* matrix = new ProjectedMatrix(in->rows(), in->columns());


    vector<double>& values  = matrix->values();
    vector<double>& rows    = matrix->rowsArray();
    vector<double>& columns = matrix->columnsArray();

    for (int row = 0; row < in->rows(); row++)
        for (int column = 0; column < in->columns(); column++)
            values.push_back((*in)(row, column));

    for (vector<double>::iterator y = y_.begin(); y != y_.end(); ++y) {
        // MagLog::dev() << *y << endl;
        rows.push_back(*y);
    }

    for (vector<double>::iterator x = x_.begin(); x != x_.end(); ++x) {
        // MagLog::dev() << *x << endl;
        columns.push_back(*x);
    }
    matrix->missing(std::numeric_limits<double>::max());
    matrix->getReady();

    return matrix;
}


void InputMatrixInterpretor::upperLeft() {}

void InputMatrixInterpretor::lowerLeft() {}

void InputMatrixInterpretor::upperRight() {}

void InputMatrixInterpretor::lowerRight() {}


void InputMatrixInterpretor::upperLeftTransposed() {}

void InputMatrixInterpretor::lowerLeftTransposed() {}

void InputMatrixInterpretor::upperRightTransposed() {}

void InputMatrixInterpretor::lowerRightTransposed() {}

void InputMatrixRegularInterpretor::getReady(const Transformation& transformation) {
    if (transformation.xAxisType() != "date")
        x_first_date_.clear();
    else
        dateX_ = transformation.getReferenceX();

    if (transformation.yAxisType() != "date")
        y_first_date_.clear();
    else
        dateY_ = transformation.getReferenceY();
}
