/******************************** LICENSE ********************************

 Copyright 2007 European Centre for Medium-Range Weather Forecasts (ECMWF)

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at 

    http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.

 ******************************** LICENSE ********************************/

/*! \file Coordinate.h
    \brief Definition of the Template class Coordinate.
    
    Magics Team - ECMWF 2006
    
    Started: Thu 10-Aug-2006
    
    Changes:
    
*/

#ifndef Coordinate_H
#define Coordinate_H

#include "magics.h"
#include "MagTranslator.h"
#include "Factory.h"

#include "XCoordinateAttributes.h"
#include "YCoordinateAttributes.h"

#include "XDateCoordinateAttributes.h"
#include "YDateCoordinateAttributes.h"

#include "XHyperCoordinateAttributes.h"
#include "YHyperCoordinateAttributes.h"
#include "DateTime.h"

#include "XmlNode.h"

#include <limits>
namespace magics {

class XmlNode;

class Coordinate {

public:
	Coordinate();
	virtual ~Coordinate();
	
	virtual double min() { return 0; }
    virtual double max() { return 100; }
    virtual double minpc() { return 0; }
    virtual double maxpc() { return 100; }
    virtual vector<double> maxs() { vector<double> vmax; vmax.push_back(max()); return vmax; }
    virtual vector<double> mins() { vector<double> vmin; vmin.push_back(min()); return vmin; }
    virtual string reference() { return ""; }
    
    virtual void min(double) { }
    virtual void max(double) { }
    
    virtual double operator()(double c) { return c; }
    virtual double revert(double c) { return c; }
    
    virtual void set(const XmlNode&) {
        MagLog::dev() << "(const XmlNode&)---> to be checked!...\n";
    }
    virtual void set(const map<string, string>&) {
        MagLog::dev() << "(const map<string, string&)---> to be checked!...\n";
        
    }
    virtual void metadata(ostream& out) { out << "axis : {}"; }
    virtual void set() {}
    virtual bool accept(const string&) {
        MagLog::dev() << "(const map<string, string&)---> to be checked!...\n";
        return false;
    }
    
    virtual void toxml(ostream&, int=0) const {}
    virtual AxisAutomaticSetting automatic() { return m_off; }
    virtual void automatic(bool) {  }
    virtual double operator()(const string& val) const { return tonumber(val); }
    virtual string type() const { return "regular"; }
    
    
    virtual void dataMin(double m) { 
    	if ( automatic() == m_off || automatic()== m_max_only) return;
//    	MagLog::dev()<< "new min-->" << m << "< " << min() << ???" << endl;  
    	if (min() > m) min(m); 
    }
    virtual void dataMax(double m) { 
    	if ( automatic()== m_off || automatic() == m_min_only) return;
//   	MagLog::dev()<< "new max-->" << m << endl; 
//    	MagLog::dev()<< "new max-->" << m << "> " << max() << "???" << endl;  
    	if (  max() < m ) max(m); 
    }
    virtual void dataMin(double m, const string&) { if (min() > m) dataMin(m); }
    virtual void dataMax(double m, const string&) { if (max() < m)dataMax(m); }    
    virtual void setAutomatic(AxisAutomaticSetting) {}
  
protected:
     //! Method to print string about this class on to a stream of type ostream (virtual).
	 virtual void print(ostream&) const; 

private:
    //! Copy constructor - No copy allowed
	Coordinate(const Coordinate&);
    //! Overloaded << operator to copy - No copy allowed
	Coordinate& operator=(const Coordinate&);

// -- Friends
    //! Overloaded << operator to call print().
	friend ostream& operator<<(ostream& s,const Coordinate& p)
		{ p.print(s); return s; }

};

class XCoordinate : public Coordinate
{
public:
	XCoordinate() {}
	virtual ~XCoordinate() {}
	virtual XCoordinate* clone() const {
        MagLog::dev() << "(const map<string, string&)---> to be checked!...\n";
        return new XCoordinate();
    }
	//virtual void set() {}
	virtual void toxml(ostream&, int =0) const {}
    virtual void setMin(double) {}
    virtual void setMax(double) {}
    virtual void automatic(bool) {}
    virtual AxisAutomaticSetting automatic() { return m_off; }
};

class YCoordinate : public Coordinate
{
public:
	YCoordinate() {}
	virtual ~YCoordinate() {}
	virtual void toxml(ostream&, int =0) const {}
	virtual YCoordinate* clone() const {
        MagLog::dev() << "(const map<string, string&)---> to be checked!...\n";
        return new YCoordinate();
    }
	//virtual void set() {}
    virtual void setMin(double) {}
    virtual void setMax(double) {}
    virtual void automatic(bool) {}
    virtual AxisAutomaticSetting automatic() { return m_off; }
};

class RegularCoordinate 
{
public:
	RegularCoordinate() {}
	virtual ~RegularCoordinate() {}
};

class XRegularCoordinate : public RegularCoordinate, public XCoordinateAttributes, public XCoordinate
{
public:
	XRegularCoordinate() { 
		set();	
	}
	virtual ~XRegularCoordinate() {}

	void set() {
		switch ( automatic_ ) {
			case m_both:
				min_ = std::numeric_limits<double>::max();
				max_ = -min_;
				break;
			case m_min_only:
				min_ = std::numeric_limits<double>::max();
				break;
			case m_max_only:
				max_ = -std::numeric_limits<double>::max();
				break;
			default:
				break;
		}
	}
	
	virtual void set(const XmlNode& node) {
        	XCoordinateAttributes::set(node);
        	set();
	}
    virtual bool accept(const string& node) { return XCoordinateAttributes::accept(node); }
	double min() { return min_; }
    double max() { return max_; }
    double minpc() { return min_; }
    double maxpc() { return max_; }
    
    AxisAutomaticSetting automatic() { return automatic_; }
    virtual void setAutomatic(AxisAutomaticSetting automatic) { XCoordinateAttributes::setAutomatic(automatic); }
    virtual void  automatic(bool automatic) {XCoordinateAttributes::setAutomatic(automatic?m_both:m_off);}
    virtual void toxml(ostream& out, int tab=0) const
    	{ XCoordinateAttributes::toxml(out, tab); }
    void min(double m) {
      	switch ( automatic_ ) {
      					case m_both:
      					case m_min_only:
      						min_ = m;
      						break;
      					default:
      						break;
      				}
      }
      void max(double m) {
      	switch ( automatic_ ) {
      		case m_both:
      		case m_min_only:
      			max_ = m;
      			break;
      		default:
      			break;
      	}
      }
    
    virtual XCoordinate* clone() const {
    	XRegularCoordinate* x = new XRegularCoordinate();
    	x->copy(*this);
        return x;
    }
    
    void setMin(double min) { XCoordinateAttributes::setMin(min); }
    void setMax(double max) { XCoordinateAttributes::setMax(max); }
    
  
    
protected:
	virtual void print(ostream& out) const  {
		XCoordinateAttributes::print(out);
	}
};

class YRegularCoordinate : public RegularCoordinate, public YCoordinateAttributes, public YCoordinate
{
public:
	YRegularCoordinate() {
		set();
	}
	virtual ~YRegularCoordinate() {}
	void set() {
		switch ( automatic_ ) {
				case m_both:
					min_ = std::numeric_limits<double>::max();
					max_ = -min_;
					break;
				case m_min_only:
					min_ = std::numeric_limits<double>::max();
					break;
				case m_max_only:
					max_ = -std::numeric_limits<double>::max();
					break;
				default:
					break;
			}
	}
	double min() { return min_; }
    double max() { return max_; }
    double minpc() { return min_; }
    double maxpc() { return max_; }
    virtual void setAutomatic(AxisAutomaticSetting automatic) { YCoordinateAttributes::setAutomatic(automatic); }
    void min(double m) {
    	switch ( automatic_ ) {
    					case m_both:
    					case m_min_only:
    						min_ = m;
    						break;
    					default:
    						break;
    				}
    }
    void max(double m) {
    	switch ( automatic_ ) {
    		case m_both:
    		case m_min_only:
    			max_ = m;
    			break;
    		default:
    			break;
    	}
    }
    virtual void toxml(ostream& out, int tab =0) const
		{ YCoordinateAttributes::toxml(out, tab); }
    virtual void automatic(bool automatic) {YCoordinateAttributes::setAutomatic(automatic?m_both:m_off);}
    virtual YCoordinate* clone() const {
    	YRegularCoordinate* y = new YRegularCoordinate();
    	y->copy(*this);
        return y;
    }
    void set(const XmlNode&  node ) { 
	YCoordinateAttributes::set(node); 
	set(); }
    void set(const map<string, string>& map) { 
    	YCoordinateAttributes::set(map); 
    	set();
	}
    bool accept(const string& node) { return YCoordinateAttributes::accept(node); }

    void setMin(double min) { YCoordinateAttributes::setMin(min); }
    void setMax(double max) { YCoordinateAttributes::setMax(max); }
    AxisAutomaticSetting automatic() { return automatic_; }
    
   
protected:
	virtual void print(ostream& out) const  {
		YCoordinateAttributes::print(out);
	}
};


class LogarithmicCoordinate : public Coordinate
{
public:
	LogarithmicCoordinate() {}
	virtual ~LogarithmicCoordinate() {}
	string type() const { return "logarithmic"; }

};

class XLogarithmicCoordinate : public LogarithmicCoordinate, public XCoordinateAttributes, public XCoordinate
{
public:
	XLogarithmicCoordinate() {}
	virtual ~XLogarithmicCoordinate() {}
	double min() { return min_; }
    double max() { return max_; }
    double minpc() { return log10(min_); }
    double maxpc() { return log10(max_); }
    virtual void set(const XmlNode& node) {
	    if ( !magCompare(node.name(), "x_logarithmic") ) return; 
		XmlNode regular = node;
		regular.name("x_regular");
        XCoordinateAttributes::set(regular);
    }
    virtual void toxml(ostream& out, int tab=0) const
   		{ XCoordinateAttributes::toxml(out, tab); }
    void min(double m) {
    	switch ( automatic_ ) {
    					case m_both:
    					case m_min_only:
    						min_ = m;
    						break;
    					default:
    						break;
    				}
    }
    void max(double m) {
    	switch ( automatic_ ) {
    		case m_both:
    		case m_min_only:
    			max_ = m;
    			break;
    		default:
    			break;
    	}
    }

    double operator()(double c ) { return log10(c); }
    double revert(double c ) { return pow(c, 10); }
    
     virtual XCoordinate* clone() const {
    	XLogarithmicCoordinate* x = new XLogarithmicCoordinate();
    	x->copy(*this);
        MagLog::dev() << "(const map<string, string&)---> to be checked!...\n";
        return x;
    }
    
    void setMin(double min) { XCoordinateAttributes::setMin(min); }
    void setMax(double max) { XCoordinateAttributes::setMax(max); }
    AxisAutomaticSetting automatic() { return automatic_; }
    virtual void automatic(bool automatic) {XCoordinateAttributes::setAutomatic(automatic?m_both:m_off);}
    virtual void setAutomatic(AxisAutomaticSetting automatic) { XCoordinateAttributes::setAutomatic(automatic); }
protected:
	virtual void print(ostream& out) const  {
		out << "XLogarithmicCoordinate[";
		XCoordinateAttributes::print(out);
		out << "]";
	}
};


class YLogarithmicCoordinate : public LogarithmicCoordinate, public YCoordinateAttributes, public YCoordinate
{
public:
	YLogarithmicCoordinate() {}
	virtual ~YLogarithmicCoordinate() {}
	virtual void set(const XmlNode& node) {
		if ( !magCompare(node.name(), "y_logarithmic") ) return; 
		XmlNode regular = node;
		regular.name("y_regular");
        YCoordinateAttributes::set(regular);
    }
	double min() { return min_; }
    double max() { return max_; }
    double minpc() { return log10(min_); }
    double maxpc() { return log10(max_); }
    virtual void toxml(ostream& out, int tab=0) const
       		{ YCoordinateAttributes::toxml(out, tab); }
    void min(double m) {
    	switch ( automatic_ ) {
    					case m_both:
    					case m_min_only:
    						min_ = m;
    						break;
    					default:
    						break;
    				}
    }
    void max(double m) {
    	switch ( automatic_ ) {
    		case m_both:
    		case m_min_only:
    			max_ = m;
    			break;
    		default:
    			break;
    	}
    }
    AxisAutomaticSetting automatic() { return automatic_; }
    
    void setMin(double min) { YCoordinateAttributes::setMin(min); }
    void setMax(double max) { YCoordinateAttributes::setMax(max); }
    double operator()(double c ) { return log10(c); }
    double revert(double c ) { return exp10(c); }
    virtual YCoordinate* clone() const {
    	YLogarithmicCoordinate* y = new YLogarithmicCoordinate();
    	y->copy(*this);
        return y;
    }

    virtual void automatic(bool automatic) {YCoordinateAttributes::setAutomatic(automatic?m_both:m_off);}
    virtual void setAutomatic(AxisAutomaticSetting automatic) { YCoordinateAttributes::setAutomatic(automatic); }
    
protected:
	virtual void print(ostream& out) const  {
		out << "YLogarithmicCoordinate[";
		YCoordinateAttributes::print(out);
		out << "]";
	}
};


class DateCoordinate 
{
public:
	DateCoordinate() {}
	virtual ~DateCoordinate() {}   
	virtual string type() const { return "date"; }

};

class XDateCoordinate : public DateCoordinate, public XDateCoordinateAttributes, public XCoordinate
{
public:
	XDateCoordinate() { anna_ = false; }
	virtual ~XDateCoordinate() {}
	
	double min() { return 0; }
    double max() { return DateTime(max_) - DateTime(min_); }
    	
    double minpc() { return anna_ ? min_anna_ : 0; }
    double maxpc() { return anna_ ? max_anna_ : max(); }
    
    void min(double min) { min_anna_ = min; anna_ = true; }
    void max(double max) { max_anna_ = max; anna_ = true;}
    virtual void toxml(ostream& out, int tab=0) const
       		{ XDateCoordinateAttributes::toxml(out, tab); }
    string reference() { return  DateTime(min_); }
    virtual XCoordinate* clone() const {
    	XDateCoordinate* x = new XDateCoordinate();
    	x->copy(*this);
        return x;
    }
    virtual string type() const { return "date"; }
    AxisAutomaticSetting automatic() { return automatic_ ? m_both:  m_off;}
    void set(const XmlNode& node) { XDateCoordinateAttributes::set(node); }
    void set(const map<string, string>& map) { XDateCoordinateAttributes::set(map); }
    bool accept(const string& xml) { return XDateCoordinateAttributes::accept(xml); }
    
    void setMin(double) { }
    void setMax(double) { }
    
    void automatic(bool automatic) { XDateCoordinateAttributes::setAutomatic(automatic?m_both:m_off); }
    virtual void setAutomatic(AxisAutomaticSetting automatic) { XDateCoordinateAttributes::setAutomatic(automatic); }
    
    void setMin(const string& min) { XDateCoordinateAttributes::setMin(min); }
    void setMax(const string& max) { XDateCoordinateAttributes::setMax(max);}
    
    void dataMin(double min, const string& date) { 
    	switch ( automatic_ ) {
    		case m_both:
    		case m_min_only: {
    			DateTime newmin(date);
    			newmin = newmin + Second(min);
    			XDateCoordinateAttributes::setMin(newmin);
    			break;
    		}
    		default:
    			break;
    	}

    }
    void dataMax(double max, const string& date) { 
    	switch ( automatic_ ) {
			case m_both:
			case m_max_only: {
				DateTime newmax(date);
				newmax = newmax + Second(max);
				XDateCoordinateAttributes::setMax(newmax);
				break;
			}
			default:
				break;
		}
    } 
     
    double operator()(double c ) { return c; }
    
    double operator()(const string& val) const  { 
    	DateTime date(val); 
    	return date - DateTime(min_);
    }

    
	
	
protected:
	virtual void print(ostream& out) const  {
		XDateCoordinateAttributes::print(out);
	}
	// patch for anna! 
	
	double min_anna_;
	double max_anna_;
	bool anna_;

};


class YDateCoordinate : public DateCoordinate, public YDateCoordinateAttributes, public YCoordinate
{
public:
	YDateCoordinate() {}
	virtual ~YDateCoordinate() {}
	double min()   { return 0; }
    double max()   { return DateTime(max_) - DateTime(min_); }
    double minpc()     { return 0; }
    double maxpc()     { return DateTime(max_) - DateTime(min_);}
    string reference() { return DateTime(min_); }
    virtual string type() const { return "date"; }
     bool accept(const string& xml) { return YDateCoordinateAttributes::accept(xml); }
    void set(const XmlNode& node) { YDateCoordinateAttributes::set(node); }
    void set(const map<string, string>& map) { YDateCoordinateAttributes::set(map); }
    virtual void setAutomatic(AxisAutomaticSetting automatic) { YDateCoordinateAttributes::setAutomatic(automatic); }
    void automatic(bool automatic) { YDateCoordinateAttributes::setAutomatic(automatic?m_both:m_off); }
    virtual void toxml(ostream& out, int tab=0) const
          		{ YDateCoordinateAttributes::toxml(out, tab); }
    virtual YCoordinate* clone() const {
    	YDateCoordinate* y = new YDateCoordinate();
    	y->copy(*this);
        return y;
    }
    AxisAutomaticSetting automatic() { return automatic_ ? m_both:  m_off; }
    
    void setMin(double) { }
    void setMax(double) { }
    
    void setMin(const string& min) { YDateCoordinateAttributes::setMin(min); }
    void setMax(const string& max) { YDateCoordinateAttributes::setMax(max);}
    
    double operator()(double c ) { return c; }
    double operator()(const string& val) const  { 
    	DateTime date(val); 
    	return date -  DateTime(min_);
    }
    void dataMin(double min, const string& date) { 
        	switch ( automatic_ ) {
        		case m_both:
        		case m_min_only: {
        			DateTime newmin(date);
        			newmin = newmin + Second(min);
        			setMin(string(newmin));
        			break;
        		}
        		default:
        			break;
        	}

        }
        void dataMax(double max, const string& date) {
        	switch ( automatic_ ) {
    			case m_both:
    			case m_max_only: {
    				DateTime newmax(date);
    				newmax = newmax + Second(max);
    				setMax(string(newmax));
    				break;
    			}
    			default:
    				break;
    		}
        }

protected:
	virtual void print(ostream& out) const  {
		YDateCoordinateAttributes::print(out);
	}
};


class YHyperCoordinate : public YHyperCoordinateAttributes, public YCoordinate
{
public:
	YHyperCoordinate() {}
	virtual ~YHyperCoordinate() {}
	double min()   { return min_lon_; }
    double max()   { return max_lon_; }
    double minpc()     { return min_lon_; }
    double maxpc()     { return max_lon_; }
    string reference() { return ""; }
  
     bool accept(const string& xml) { return YHyperCoordinateAttributes::accept(xml); }
    void set(const XmlNode& node) { YHyperCoordinateAttributes::set(node); }
    void set(const map<string, string>& map) { YHyperCoordinateAttributes::set(map); }
    virtual void setAutomatic(bool automatic) { YHyperCoordinateAttributes::setAutomatic(automatic?m_both:m_off); }
    void automatic(bool automatic) { YHyperCoordinateAttributes::setAutomatic(automatic?m_both:m_off); }
    
    virtual YCoordinate* clone() const {
    	YHyperCoordinate* y = new YHyperCoordinate();
    	y->copy(*this);
        return y;
    }
    AxisAutomaticSetting automatic() { return automatic_ ; }
    
    void setMin(double) { }
    void setMax(double) { }
    
    vector<double> mins() { vector<double> mins; mins.push_back(min_lon_); mins.push_back(min_lat_);  return mins; }
     vector<double> maxs() {  vector<double> maxs; maxs.push_back(max_lon_); maxs.push_back(max_lat_);  return maxs; }
     string type() const { return "geoline"; }

protected:
	virtual void print(ostream& out) const  {
		YHyperCoordinateAttributes::print(out);
	}
};

class XHyperCoordinate : public XHyperCoordinateAttributes, public XCoordinate
{
public:
	XHyperCoordinate() {}
	virtual ~XHyperCoordinate() {}
	double min()   { return min_lon_; }
    double max()   { return max_lon_; }
    double minpc()     { return min_lon_; }
    double maxpc()     { return max_lon_; }
    string reference() { return ""; }
    
     bool accept(const string& xml) { return XHyperCoordinateAttributes::accept(xml); }
    void set(const XmlNode& node) { XHyperCoordinateAttributes::set(node); }
    void set(const map<string, string>& map) { XHyperCoordinateAttributes::set(map); }
    virtual void setAutomatic(bool automatic) { XHyperCoordinateAttributes::setAutomatic(automatic?m_both:m_off); }
    void automatic(bool automatic) {  XHyperCoordinateAttributes::setAutomatic(automatic?m_both:m_off); }
    vector<double> mins() { vector<double> mins; mins.push_back(min_lon_); mins.push_back(min_lat_);  return mins; }
    vector<double> maxs() {  vector<double> maxs; maxs.push_back(max_lon_); maxs.push_back(max_lat_);  return maxs; }
    
    virtual XCoordinate* clone() const {
    	XHyperCoordinate* x = new XHyperCoordinate();
    	x->copy(*this);
        return x;
    }
    AxisAutomaticSetting automatic() { return automatic_ ? m_both:  m_off; }
    
    void setMin(double) { }
    void setMax(double) { }
    
     string type() const { return "geoline"; }
     void dataMin(double min, const string& info) {
    	 cout << "XHyperCoordinatedataMin" << info << endl;
    	 //interpret the info : lonmin/latmin
    	 Tokenizer tokenizer("/");
    	 vector<string> tokens;
    	 tokenizer(info, tokens);
            	switch ( automatic_ ) {
            		case m_both:
            		case m_min_only: {
            			min_lon_ = tonumber(tokens[0]);
            			min_lat_ = tonumber(tokens[1]);
            			break;
            		}
            		default:
            			break;
            	}

            }
            void dataMax(double max, const string& info) {
            	cout << "XHyperCoordinatedataMin" << info << endl;
            	//interpret the info : lonmin/latmin
            	Tokenizer tokenizer("/");
            	vector<string> tokens;
            	tokenizer(info, tokens);
            	switch ( automatic_ ) {
        			case m_both:
        			case m_max_only: {
        				max_lon_ = tonumber(tokens[0]);
        				max_lat_ = tonumber(tokens[1]);
        				break;
        			}
        			default:
        				break;
        		}
            }

protected:
	virtual void print(ostream& out) const  {
		XHyperCoordinateAttributes::print(out);
	}
};

template <>
class MagTranslator<string, XCoordinate> { 
public:
	XCoordinate* operator()(const string& val )
	{
		return SimpleObjectMaker<XCoordinate>::create(val);
	}     

	XCoordinate* magics(const string& param)
	{
		XCoordinate* object;
		ParameterManager::update(param, object);
		return object;
	}
};
template <>
class MagTranslator<string, YCoordinate> { 
public:
	YCoordinate* operator()(const string& val )
	{
		return SimpleObjectMaker<YCoordinate>::create(val);
	}     

	YCoordinate* magics(const string& param)
	{
		YCoordinate* object;
		ParameterManager::update(param, object);
		return object;
	}
};
} // namespace magics
#endif
