/*
 * (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 AxisMethod.cc
    \brief Implementation of the Template class AxisMethod.
    
    Magics Team - ECMWF 2005
    
    Started: Fri 7-Oct-2005
    
    Changes:
    
*/

#include <limits>

#include "AxisMethod.h"
#include "Axis.h"
#include "Transformation.h"

using namespace magics;

AxisMethod::AxisMethod() 
{
}

AxisMethod::~AxisMethod() 
{
}

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

void LogarithmicAxisMethod::prepare(const Axis& axis,  AxisItems& items)
{

	    vector<int> factor;
	    factor.push_back(1);
	    factor.push_back(2);
	    factor.push_back(5);

	    double min = axis.min();
	    double max = axis.max();

	    int s1 = (min < 0) ? -1: 1;
	    int s2 = (max < 0) ? -1: 1;


	    int log1 =  s1 *((min) ? log10(s1*min) : -5);
	    int log2 =  s2 *((max) ? log10(s2*max) : -5);

	    bool reduce = (min*max) < 0;

	    std::set<double> ticks;

	    double from = std::min(min, max);
	    double to = std::max(min, max);

	    for (int i = std::min(log1, log2); i <= std::max(log1, log2); i +=1 ) {
	        for ( vector<int>::iterator f = factor.begin(); f != factor.end(); ++f) {

	        	double x;
	        	if ( !reduce || ( reduce && i > -4)  ) {
	        		x = (*f) * pow(10.,i);
	        		if ( from <= x  && x <= to && x != 0 )
	        			ticks.insert(x);
	        		x = -x;
	        		if ( from <= x  && x <= to && x != 0 )
	        			ticks.insert(x);
	        	}
	        	if ( !reduce || ( reduce && -i > -4   )) {
	        		x = (*f) * pow(10.,-i);
	        		if ( from <= x  && x <= to && x != 0 )
	        			ticks.insert(x);
	        		x = -x;
	        		if ( from <= x  && x <= to && x != 0)
	                	ticks.insert(x);
	        	}
	        }
	    }



	for ( std::set<double>::const_iterator step = ticks.begin(); step != ticks.end(); ++step) {
		items.push_back(new AxisItem(*step, axis.label_format_));
	}
}

void  HyperAxisMethod::updateX(const Transformation& transformation)
{
	hyperMin_ = transformation.getDataVectorMinX();
	hyperMax_ = transformation.getDataVectorMaxX();
	
}

void  HyperAxisMethod::updateY(const Transformation& transformation)
{
	hyperMin_ = transformation.getDataVectorMinY();
	hyperMax_ = transformation.getDataVectorMaxY();
}

void HyperAxisMethod::prepare(const Axis& axis, AxisItems& items)
{
	double inc;
	int nb = 7;
	double step;
	double log, ws;
	bool horizontal = false;

	double min = hyperMin_.front();
	double max = hyperMax_.front();
	double wmax = std::max(hyperMin_.front(), hyperMax_.front());
	double wmin = std::min(hyperMin_.front(), hyperMax_.front());
	if ( wmin==wmax) {
		wmax = std::max(hyperMin_.back(), hyperMax_.back());
		wmin = std::min(hyperMin_.back(), hyperMax_.back());
		min = hyperMin_.back();
		max = hyperMax_.back();
		horizontal = true;
	}

	
	if (axis.interval_ == INT_MAX ) {
	
		while (nb < 10) {
			step = (wmax-wmin)/nb;
			log = log10(step);
			ws = pow(10., int(log));
			inc = ceil(step/ws)*ws;
			MagLog::dev() << "Automatic method ---> increment = " << inc << " ---> try base=" << inc/ws << endl;
			
			 if ( wmax-wmin/inc > 5 && (inc/ws == 1 || inc/ws == 2 || inc/ws == 3 || inc/ws == 5 || inc/ws == 10) ) {
				MagLog::dev() << "Automatic method ---> increment " << inc << " OK! " << endl;
				break;
			}
			nb++;
			
		}
	}
	
	else {
		inc = axis.interval_;
	}
	std::set<double> list;
	
	

    if ( min < max ) {
		double first = floor(min/inc) *inc;
		double last  = 0.;    
		int i = 0;
		for (double val = first;  val <= max; val = first + (inc*i) ) {
			if (val >= min && val <=max) {
				list.insert(value(val));
        		last = val;
			}
			i++;
		}
		list.insert(value(last+inc));
		
	}
	else {
		double first = floor(min/inc) *inc;    
		double last  = 0.;  
		int i = 0;
		for (double val = first;  val >= max; val = first - ( inc * i) ) {
			if (val >= wmin && val <= wmax) {
				list.insert(value(val));       		
        		last = val;
			}
			i++;
		}
		list.insert(value(last-inc));
	}
    
    double imin = hyperMin_[0];
    double imax = hyperMax_[0];
    double jmin = hyperMin_[1];
    double jmax = hyperMax_[1];;
    double iw = imax - imin;
    double jw = jmax - jmin;
    
    for (std::set<double>::iterator i = list.begin(); i != list.end(); ++i) {
    	vector<double> val;
    	if ( horizontal ) {
    		val.push_back(imin);

    		val.push_back(*i);
    		items.push_back( new AxisHyperItem(*i, val));
    	}
    	else {

        	val.push_back(*i);
        	double j = jmin+((*i)-imin)*(jw/iw);
        	val.push_back(j);
        	items.push_back( new AxisHyperItem(*i, val));
    	}
    }
}


void AxisMethod::prepare(const Axis& axis, AxisItems& items)
{
	double inc;
	int nb = 7;
	double step;
	double log, ws;


	double min = axis.min();
	double max = axis.max();

	double wmax = std::max(min, max);
	double wmin = std::min(min, max);

	bool automatic = false;
	
	if (axis.interval_ == INT_MAX ) {
		automatic = true;
		while (nb < 20) {
			step = (wmax-wmin)/nb;
			log = log10(step);
			ws = pow(10., int(log));
			inc = ceil(step/ws)*ws;
			MagLog::dev() << "Automatic method ---> increment = " << inc << " ---> try base=" << inc/ws << endl;
			
			 if ( wmax-wmin/inc > 5 && (inc/ws == 1 || inc/ws == 2 || inc/ws == 3 || inc/ws == 5 || inc/ws == 10) ) {
				MagLog::dev() << "Automatic method ---> increment " << inc << " OK! " << endl;
				break;
			}
			nb++;
			
		}
	}
	
	else {
		inc = axis.interval_;
	}
	std::set<double> list;

	if ( min < max )
	{
		double first = floor(min/inc) *inc;
		double last  = 0.;    
	    int i = 0;
		for (double val = first;  val <= max; val = first + (i*inc) ) {
			if (val >= min && val <=max) {
				list.insert(value(val));
        		last = val;

			}
			i++;
		}
		if ( last-inc <= max )
			list.insert(value(last+inc));

	}
	else
	{
		double first = floor(min/inc) * inc;
		double last  = 0.;  
		int i = 0;
		for (double val = first;  val >= max; val = first - (i*inc)) {
			if (val >= wmin && val <= wmax) {
				list.insert(value(val));       		
        			last = val;
			}
			i++;
		}
		if ( last-inc >= max )
			list.insert(value(last-inc));
	}
	std::set<double> slist;
	int mod  = (list.size() / 10);
	mod++;



	int i = 0;
	for ( std::set<double>::iterator e = list.begin(); e !=  list.end(); ++e) {
		if ( !automatic )
			slist.insert(*e);
		else if ( i %  mod == 0 )
			slist.insert(*e);

		i++;
	}


	// First we add the minor tich before the first one...
	std::set<double>::iterator front = slist.begin();
	double first = *front - inc;
	step = inc/(axis.minor_tick_count_+1);
	for ( double ii = first; ii < *front; ii+=step ) {
		items.push_back(new AxisMinorTickItem(ii));
	}

	double last = std::numeric_limits<double>::max();
	for (std::set<double>::iterator i = front; i != slist.end(); ++i) {
		// Add the minor Axis Items!!! 
		if ( last != std::numeric_limits<double>::max()) {
			double step = (*i-last)/(axis.minor_tick_count_+1);
			for ( double ii = last +step; ii < *i; ii+=step ) {
				items.push_back(new AxisMinorTickItem(ii));
			}
		}
		last = *i;
		addItem(items,*i, axis.label_format_);
	}
}

void AxisMethod::updateX(const Transformation& transformation)
{
	min_ = transformation.getMinX();
	max_ = transformation.getMaxX();



}

void AxisMethod::updateY(const Transformation& transformation)
{
	min_ = transformation.getMinY();
	max_ = transformation.getMaxY();

}

void     PositionListAxisMethod::prepare(const Axis& axis, AxisItems& items)
{
	for ( vector<double>::const_iterator tick = axis.positions_.begin(); tick != axis.positions_.end(); ++tick)
	{
		items.push_back(new AxisItem(*tick, axis.label_format_));
	}
}

