/*
 * (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 MagTranslator.h
    \brief Definition of MagTranslator class.

    Magics Team - ECMWF 2004

    Started: Jan 2004

    Changes:

*/

#ifndef MPP_MagTranslator_H
#define MPP_MagTranslator_H

// Forward declarations
#include "ParameterManager.h"
#include "Tokenizer.h"
#include "magics.h"


namespace magics {

class Matrix;

template <class From, class To>
class MagTranslator {
public:
    virtual ~MagTranslator() {}
    // Default template calls cast
    To* operator()(From from) { return new To(from); }

    To* magics(const string& param) {
        From from;
        ParameterManager::get(param, from);
        return (*this)(from);
    }
};

template <>
class MagTranslator<string, double> {
public:
    double operator()(string value) { return atof(value.c_str()); }

    double magics(const string& param) {
        double from;
        ParameterManager::get(param, from);
        return from;
    }
};


template <>
class MagTranslator<double, double> {
public:
    double operator()(double value) { return value; }

    double magics(const string& param) {
        double from;
        ParameterManager::get(param, from);
        return (*this)(from);
    }
};


template <>
class MagTranslator<doublearray, doublearray> {
public:
    virtual ~MagTranslator() {}

    doublearray operator()(doublearray value) { return value; }

    doublearray magics(const string& param) {
        doublearray from;
        ParameterManager::get(param, from);
        return (*this)(from);
    }
};

template <>
class MagTranslator<string, doublearray> {
public:
    doublearray operator()(const string& value) {
        doublearray array;
        vector<string> list;
        Tokenizer tokenizer("/");
        tokenizer(value, list);
        for (vector<string>::const_iterator item = list.begin(); item != list.end(); ++item) {
            array.push_back(atof((*item).c_str()));
        }
        return array;
    }
    doublearray magics(const string& param) {
        doublearray from;
        ParameterManager::get(param, from);
        return from;
    }
};
template <>
class MagTranslator<string, Matrix> {
public:
    Matrix operator()(const string&) {
        Matrix array;
        return array;
    }
    Matrix magics(const string& param) {
        Matrix from;
        ParameterManager::get(param, from);
        return from;
    }
};

template <>
class MagTranslator<string, int> {
public:
    int operator()(const string& value) { return atoi(value.c_str()); }
    int magics(const string& param) {
        int from;
        ParameterManager::get(param, from);
        return from;
    }
};

template <>
class MagTranslator<int, int> {
public:
    int operator()(int value) { return value; }
    int magics(const string& param) {
        int from;
        ParameterManager::get(param, from);
        return (*this)(from);
    }
};

template <>
class MagTranslator<string, intarray> {
public:
    intarray operator()(const string& value) {
        intarray array;
        vector<string> list;
        Tokenizer tokenizer("/");
        tokenizer(value, list);

        for (vector<string>::const_iterator item = list.begin(); item != list.end(); ++item) {
            array.push_back(atoi((*item).c_str()));
        }
        return array;
    }
    intarray magics(const string& param) {
        intarray from;
        ParameterManager::get(param, from);
        return from;
    }
};

template <>
class MagTranslator<string, longintarray> {
public:
    longintarray operator()(const string& value) {
        longintarray array;
        vector<string> list;
        Tokenizer tokenizer("/");
        tokenizer(value, list);

        for (vector<string>::const_iterator item = list.begin(); item != list.end(); ++item) {
            array.push_back(atoi((*item).c_str()));
        }
        return array;
    }
    longintarray magics(const string& param) {
        longintarray from;
        ParameterManager::get(param, from);
        return from;
    }
};
template <>
class MagTranslator<intarray, intarray> {
public:
    intarray operator()(intarray value) { return value; }
    intarray magics(const string& param) {
        intarray from;
        ParameterManager::get(param, from);
        return (*this)(from);
    }
};
template <>
class MagTranslator<longintarray, longintarray> {
public:
    longintarray operator()(longintarray value) { return value; }
    longintarray magics(const string& param) {
        longintarray from;
        ParameterManager::get(param, from);
        return (*this)(from);
    }
};

template <>
class MagTranslator<const string&, const string&> {
public:
    const string& operator()(const string& value) { return value; }
    const string& magics(const string& param) {
        string from;
        ParameterManager::get(param, from);
        return (*this)(from);
    }
};
template <>
class MagTranslator<string, string> {
public:
    string operator()(const string& value) { return value; }
    string magics(const string& param) {
        string from;
        ParameterManager::get(param, from);
        return (*this)(from);
    }
};

template <>
class MagTranslator<stringarray, stringarray> {
public:
    stringarray operator()(stringarray value) { return value; }
    stringarray magics(const string& param) {
        stringarray from;
        ParameterManager::get(param, from);
        return (*this)(from);
    }
};

template <>
class MagTranslator<string, stringarray> {
public:
    stringarray operator()(string value) {
        stringarray result;

        Tokenizer tokenizer("/");  // enum	LineStyle { SOLID , DASH , DOT , CHAIN_DASH , CHAIN_DOT };
        tokenizer(value, result);
        return result;
    }

    stringarray magics(const string& param) {
        stringarray from;
        ParameterManager::get(param, from);
        return from;
    }
};

template <>
class MagTranslator<bool, const string&> {
public:
    string operator()(bool value) {
        ostringstream out;
        out << value << ends;
        return out.str();
    }
};

template <>
class MagTranslator<const string&, bool> {
public:
    bool operator()(const string& s) {
        if (s == "no" || s == "off" || s == "false")
            return false;
        if (s == "yes" || s == "on" || s == "true")
            return true;

        // Catter for ints
        return atoi(s.c_str());
    }
    bool magics(const string& param) {
        string from;
        ParameterManager::get(param, from);
        return (*this)(from);
    }
};

template <>
class MagTranslator<string, bool> {
public:
    bool operator()(const string& s) {
        string val = lowerCase(s);
        if (val == "no" || val == "off" || val == "false")
            return false;
        if (val == "yes" || val == "on" || val == "true")
            return true;

        // Catter for ints
        return atoi(val.c_str());
    }
    bool magics(const string& param) {
        string from;
        ParameterManager::get(param, from);
        return (*this)(from);
    }
};

template <>
class MagTranslator<string, magics::Hemisphere> {
public:
    magics::Hemisphere operator()(const string& s) {
        string val = lowerCase(s);

        if (val == "north")
            return NORTH;
        if (val == "south")
            return SOUTH;

        return NORTH;
    }
    magics::Hemisphere magics(const string& param) {
        string from;
        ParameterManager::get(param, from);
        return (*this)(from);
    }
};

template <>
class MagTranslator<string, magics::ArrowPosition> {
public:
    magics::ArrowPosition operator()(const string& s) {
        string val = lowerCase(s);

        if (val == "tail")
            return M_TAIL;
        if (val == "centre")
            return M_CENTRE;

        return M_TAIL;
    }
    magics::ArrowPosition magics(const string& param) {
        string from;
        ParameterManager::get(param, from);
        return (*this)(from);
    }
};

template <>
class MagTranslator<string, magics::LineStyle> {
public:
    magics::LineStyle operator()(const string& s) {
        string val = lowerCase(s);
        if (val == "solid")
            return M_SOLID;
        if (val == "dash")
            return M_DASH;
        if (val == "dot")
            return M_DOT;
        if (val == "chain_dash")
            return M_CHAIN_DASH;
        if (val == "chain_dot")
            return M_CHAIN_DOT;
        return M_SOLID;
    }
    magics::LineStyle magics(const string& param) {
        string from;
        ParameterManager::get(param, from);
        return (*this)(from);
    }
};

template <>
class MagTranslator<string, magics::Position> {
public:
    magics::Position operator()(const string& s) {
        string val = lowerCase(s);
        if (val == "automatic")
            return M_AUTOMATIC;
        if (val == "left")
            return M_LEFT;
        if (val == "right")
            return M_RIGHT;
        if (val == "bottom")
            return M_BOTTOM;
        if (val == "top")
            return M_TOP;
        return M_AUTOMATIC;
    }
    magics::Position magics(const string& param) {
        string from;
        ParameterManager::get(param, from);
        return (*this)(from);
    }
};

template <>
class MagTranslator<string, magics::Justification> {
public:
    magics::Justification operator()(const string& s) {
        string val = lowerCase(s);
        // enum	Justification { MLEFT , MCENTRE, MRIGHT }; };
        if (val == "left")
            return MLEFT;
        if (val == "centre")
            return MCENTRE;
        if (val == "right")
            return MRIGHT;
        return MCENTRE;
    }
    magics::Justification magics(const string& param) {
        string from;
        ParameterManager::get(param, from);
        return (*this)(from);
    }
};

template <>
class MagTranslator<string, magics::ListPolicy> {
public:
    magics::ListPolicy operator()(const string& s) {
        string val = lowerCase(s);

        // enum	ListPolicy { M_LASTONE , M_CYCLE }; };
        if (val == "lastone")
            return M_LASTONE;
        if (val == "cycle")
            return M_CYCLE;
        MagLog::warning() << "Invalid value : " << val << "for a List Policy-->"
                          << " changed to lastone" << endl;
        return M_LASTONE;
    }
    magics::ListPolicy magics(const string& param) {
        string from;
        ParameterManager::get(param, from);
        return (*this)(from);
    }
};
template <>
class MagTranslator<string, magics::AxisAutomaticSetting> {
public:
    magics::AxisAutomaticSetting operator()(const string& s) {
        string val = lowerCase(s);
        // enum	ListPolicy {  m_off, m_both, m_min_only, m_max_only }; };
        if (val == "off")
            return m_off;
        if (val == "no")
            return m_off;
        if (val == "false")
            return m_off;
        if (val == "on")
            return m_both;
        if (val == "yes")
            return m_both;
        if (val == "true")
            return m_both;
        if (val == "both")
            return m_both;
        if (val == "min_only")
            return m_min_only;
        if (val == "max_only")
            return m_max_only;

        MagLog::warning() << "Invalid value : " << val << "for an  Axis Automatic Setting-->"
                          << " changed to off" << endl;
        return m_off;
    }
    magics::AxisAutomaticSetting magics(const string& param) {
        string from;
        ParameterManager::get(param, from);
        return (*this)(from);
    }
};

template <>
class MagTranslator<string, magics::DisplayType> {
public:
    magics::DisplayType operator()(const string& s) {
        string val = lowerCase(s);
        // enum	Justification { MLEFT , MCENTRE, MRIGHT }; };
        if (val == "inline")
            return M_DT_INLINE;
        if (val == "block")
            return M_DT_BLOCK;
        if (val == "none")
            return M_DT_NONE;
        if (val == "hidden")
            return M_DT_HIDDEN;
        if (val == "absolute")
            return M_DT_ABSOLUTE;
        return M_DT_ABSOLUTE;
    }
    magics::DisplayType magics(const string& param) {
        string from;
        ParameterManager::get(param, from);
        return (*this)(from);
    }
};

template <>
class MagTranslator<Matrix, Matrix> {
public:
    Matrix operator()(Matrix value) { return value; }
    Matrix magics(const string& param) {
        Matrix from;
        ParameterManager::get(param, from);
        return (*this)(from);
    }
};

}  // namespace magics
#endif
