/******************************** 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 TableDecoder.cc
    \brief Implementation of the Template class TableDecoder.
    
    Magics Team - ECMWF 2004
    
    Started: Thu 6-May-2004
    
    Changes:
    
*/

#include "TableDecoder.h"
#include "TableReader.h"

using namespace::magics;

inline double convert(const string& str)
{
	try {
		double r;
		std::stringstream ss(str);
		ss >> r;
		return r;
	}
	catch (...)
	{
		return -1;
	}
}
/*!
 Class information are given to the output-stream.
*/		
template <class P>
void TableDecoder<P>::print(ostream& out)  const
{
	out << "TableDecoder[";
    TableDecoderAttributes::print(out);
	out << "]";
}

template <class P>
void  TableDecoder<P>::dateSetting(vector<string>& dates, vector<double>& values, DateTime& base)
{
	if ( dates.empty() )
		return;
	base = DateTime(dates.front());
	for (vector<string>:: iterator date = dates.begin(); date != dates.end(); ++ date ) {
		DateTime d(*date);
		values.push_back(d-base);
	}
}

template <class P>
void  TableDecoder<P>::numberSetting(vector<double>& from, vector<double>& values)
{
	std::copy(from.begin(), from.end(), back_inserter(values));
}

namespace magics {

template  <>
void TableDecoder<GeoPoint>::nameMode(TableReader& reader)
{
	x_name_ = this->lon_;
	y_name_ = this->lat_;
	reader.setFieldContainer(-1, y_name_, this->y_values_, -999);
	reader.setFieldContainer(-1, x_name_, this->x_values_, -999);


	if ( !this->v_values_.empty() ) {
		v_name_ = this->values_;
		reader.setFieldContainer(-1, v_name_, this->v_values_, -999);
	}
	if ( !this->x_component_.empty() ) {
		xc_name_ = this->x_component_;
		reader.setFieldContainer(-1, xc_name_, this->xc_values_, -999);
	}
	if ( !this->y_component_.empty() ) {
		yc_name_ = this->y_component_;
		reader.setFieldContainer(-1, yc_name_, this->yc_values_, -999);
	}
}

template  <>
void TableDecoder<GeoPoint>::indexMode(TableReader& reader)
{
	int x_index_ = convert(this->lon_) -1;
	int y_index_ = convert(this->lat_) -1;
	int val_index= convert(this->values_);
	int xc_index= convert(this->x_component_);
	int yc_index= convert(this->y_component_);

	reader.setFieldContainer(x_index_, x_name_, this->x_values_, -999);
	reader.setFieldContainer(y_index_, y_name_, this->y_values_, -999);


	if ( val_index != -1 ) {
		reader.setFieldContainer(val_index -1, v_name_, this->v_values_, -999);
	}
	if ( xc_index != -1 ) {
		reader.setFieldContainer(xc_index -1, xc_name_, this->xc_values_, -999);
	}
	if ( yc_index != -1 ) {
		reader.setFieldContainer(yc_index -1, yc_name_, this->yc_values_, -999);
	}
}

template<>
void  TableDecoder<GeoPoint>::prepare()
{
	  TableReader reader;

	  bool ok;
	  string error;
	  reader.setPath(this->path_);
	  reader.setHeaderRow(this->header_row_);

	  vector<string> names = reader.fieldNames();


  if ( !this->x_values_.empty() ) return;

  // first set the containers!!
  if ( magCompare(this->name_mode_, "name" ) )
	  nameMode(reader);
  else
	  indexMode(reader);


  // Then read and interpret!
   ok = reader.read(error);
   if ( !ok ) {
  	 MagLog::error() << error << endl;
  	 return;
   }



  vector<double>::iterator x = this->x_values_.begin();
	vector<double>::iterator y = this->y_values_.begin();
	vector<double>::iterator v = this->v_values_.begin();

  while ( x != this->x_values_.end() && x != this->x_values_.end() ) {
  	double val = 0;
  	if ( v != v_values_.end() ) {
  		val = *v;
  		++v;
  	}
  	push_back(GeoPoint(*x, *y, val));
  	++x;
  	++y;
  }

}

template  <>
void TableDecoder<UserPoint>::nameMode(TableReader& reader)
{
	 x_name_ = this->x_;
	 y_name_ = this->y_;
	 if ( magCompare(this->x_type_, "date" ) ) {
		 reader.setFieldContainer(-1, x_name_, this->x_date_values_, "none");
	 }
	 else {
		 reader.setFieldContainer(-1, x_name_, this->x_values_, -999);
	 }
	 if ( magCompare(this->y_type_, "date" ) ) {
		 reader.setFieldContainer(-1, y_name_, this->y_date_values_, "none");
	  }
	  else {
		reader.setFieldContainer(-1, y_name_, this->y_values_, -999);
	  }

	 if ( !this->values_.empty() ) {
		 v_name_ = this->values_;
		 reader.setFieldContainer(-1, v_name_, this->v_values_, -999);
	 }
	 if ( !this->x_component_.empty()  ) {
		 xc_name_ = this->x_component_;
		 reader.setFieldContainer(-1, xc_name_, this->xc_values_, -999);
	 }
	 if ( !this->y_component_.empty() ) {
		 yc_name_ = this->y_component_;
		 reader.setFieldContainer(-1, yc_name_, this->yc_values_, -999);
	 }
}

template  <>
void TableDecoder<UserPoint>::indexMode(TableReader& reader)
{
	int x_index = convert(this->x_) -1;
	int y_index = convert(this->y_) -1;
	int val_index= convert(this->values_);
	int xc_index= convert(this->x_component_);
	int yc_index= convert(this->y_component_);

	if ( magCompare(this->x_type_, "date" ) ) {

	    		reader.setFieldContainer(x_index, x_name_, this->x_date_values_, "none");
	     }
	     else {
	    	 reader.setFieldContainer(x_index, x_name_, this->x_values_, -999);
	     }
	     if ( magCompare(this->y_type_, "date" ) ) {
	    	 reader.setFieldContainer(y_index, y_name_, this->y_date_values_, "none");
	      }
	      else {
	    	reader.setFieldContainer(y_index, y_name_, this->y_values_, -999);
	      }

	     if ( val_index != -1 ) {
	    	 reader.setFieldContainer(val_index-1, v_name_, this->v_values_, -999);
	     }
	     if ( xc_index != -1 ) {
	    	 reader.setFieldContainer(xc_index-1, xc_name_, this->xc_values_, -999);
	     }
	     if ( yc_index != -1 ) {
	    	 reader.setFieldContainer(yc_index-1, yc_name_, this->yc_values_, -999);
	     }
}

template<>
void  TableDecoder<UserPoint>::prepare()
{
	  TableReader reader;

	  bool ok;
	  string error;
	  reader.setPath(this->path_);
	  reader.setHeaderRow(this->header_row_);
	  if ( !this->delimiter_.empty() )
		  reader.setDelimiter(this->delimiter_[0]);
	  reader.setConsecutiveDelimitersAsOne(this->combine_delimiters_);
	  reader.setDataRowOffset(this->data_row_offset_);

	  vector<string> names = reader.fieldNames();


    if ( !this->x_values_.empty() ) return;

    // first set the containers!!
    // first set the containers!!
    if ( magCompare(this->name_mode_, "name" ) )
    	  nameMode(reader);
      else
    	  indexMode(reader);
    // Then read and interpret!
     ok = reader.read(error);
     if ( !ok) {
    	 MagLog::error() << error << endl;
    	 return;
     }

     // Now we interpret
    if ( magCompare(this->x_type_, "date" ) ) {
    	this->dateSetting(this->x_date_values_, this->x_values_, baseDateX_);
    }

    if ( magCompare(this->y_type_, "date" ) ) {
      	this->dateSetting(this->y_date_values_, this->y_values_, baseDateY_);

     }

    vector<double>::iterator x = this->x_values_.begin();
	vector<double>::iterator y = this->y_values_.begin();
	vector<double>::iterator v = this->v_values_.begin();

    while ( x != this->x_values_.end() && x != this->x_values_.end() ) {
    	double val = 0;
    	if ( v != v_values_.end() ) {
    		val = *v;
    		++v;
    	}
    	push_back(UserPoint(*x, *y, val));
    	if ( same(*x, x_missing_) ||  same(*y, y_missing_) )
    		back().flagMissing();
    	++x;
    	++y;
    }

}
}
template <class P>
void TableDecoder<P>::customisedPoints(const Transformation& transformation, const std::set<string>&, CustomisedPointsList& out)
{
	prepare();

	vector<double>::const_iterator x = x_values_.begin();
	vector<double>::const_iterator y = y_values_.begin();
	vector<double>::const_iterator xc = this->xc_values_.begin();
	vector<double>::const_iterator yc = this->yc_values_.begin();
	vector<double>::const_iterator v = this->v_values_.begin();
	while (  x != x_values_.end() || y != y_values_.end()  ) {
		    	CustomisedPoint* point = new CustomisedPoint();
		    	bool todelete = true;
		    	if ( transformation.in(*x, *y) ) {
		    		point->longitude(*x);
		    		point->latitude(*y);
		    		out.push_back(point);
		    		todelete = false;
		    	}

		    	if ( x != x_values_.end() ) (*point)["x"] = *(x++);
		    	if ( y != y_values_.end() ) (*point)["y"] = *(y++);
		        double speed = 0;
		    	if ( xc != this->xc_values_.end() && yc != this->yc_values_.end() ) {
		    		speed = sqrt( (*xc * *xc)  +  (*yc * *yc) );
		    		(*point)["x_component"] = *(xc++);
		    		(*point)["y_component"] = *(yc++);
		    	}
		    	if ( v != this->v_values_.end() ) {
		    		(*point)["colour_component"] = *(v++);
		    	}
		    	else
		    		(*point)["colour_component"] = speed;

		        if ( todelete ) delete point;
		    	
		    }

}

template <class P>
MatrixHandler<P>& TableDecoder<P>::matrix()
{
	if  ( !matrix_ ) {
		prepare();
		matrix_ = (*binning_)(*this);
	}

	this->matrixHandlers_.push_back(new MatrixHandler<P>(*matrix_));
	return *(this->matrixHandlers_.back());
}

template <class P>
void TableDecoder<P>::getReady(const Transformation& transformation)
{
	/*
	try {
		for ( vector<string>::const_iterator x = date_x_.begin(); x != date_x_.end(); ++x )
			x_.push_back(transformation.x(*x));
		}

	catch (...) {}
	try {
		for ( vector<string>::const_iterator y = date_y_.begin(); y != date_y_.end(); ++y )
			y_.push_back(transformation.y(*y));
	}
	catch (...) {}
	*/
}
template <class P>
void TableDecoder<P>::visit(Transformation& transformation)
{
	// get the data ...
	try {
		prepare();

		if ( transformation.getAutomaticX() ) {

			if ( magCompare(this->x_type_, "date" ) ) {
				double min = ( this->x_values_.empty() ) ? 0 : *std::min_element(this->x_values_.begin(), this->x_values_.end());
				double max = ( this->x_values_.empty() ) ? 24*3600 : *std::max_element(this->x_values_.begin(), this->x_values_.end());
				transformation.setDataMinX(min, this->baseDateX_);
				transformation.setDataMaxX(max, this->baseDateX_);
			}
			else {
				double min = ( this->x_values_.empty() ) ? 0 : *std::min_element(this->x_values_.begin(), this->x_values_.end());
				double max = ( this->x_values_.empty() ) ? 100 : *std::max_element(this->x_values_.begin(), this->x_values_.end());
				transformation.setMinX(min);
				transformation.setMaxX(max);
			}
		}
		if ( transformation.getAutomaticY() ) {
			if ( magCompare(this->y_type_, "date" ) ) {
				double min = ( this->y_values_.empty() ) ? 0 : *std::min_element(this->y_values_.begin(), this->y_values_.end());
				double max = ( this->y_values_.empty() ) ? 24*3600 : *std::max_element(this->y_values_.begin(), this->y_values_.end());

				transformation.setDataMinY(min, this->baseDateY_);
				transformation.setDataMaxY(max, this->baseDateY_);
			}
			else {
				double min = ( this->y_values_.empty() ) ? 0 : *std::min_element(this->y_values_.begin(), this->y_values_.end());
				double max = ( this->y_values_.empty() ) ? 100 : *std::max_element(this->y_values_.begin(), this->y_values_.end());

				transformation.setMinY(min);
				transformation.setMaxY(max);
			}
		}
	}
	catch ( ... ) {}
}

template <class P>
void TableDecoder<P>::customisedPoints(const std::set<string>&, CustomisedPointsList& out)
{
	prepare();
	vector<double>::const_iterator x = x_values_.begin();
	vector<double>::const_iterator y = y_values_.begin();
	vector<double>::const_iterator xc = this->xc_values_.begin();
	vector<double>::const_iterator yc = this->yc_values_.begin();
	vector<double>::const_iterator v = this->v_values_.begin();
	while (  x != x_values_.end() || y != y_values_.end()  ) {
		    	CustomisedPoint* point = new CustomisedPoint();


		    		point->longitude(*x);
		    		point->latitude(*y);
		    		out.push_back(point);

		    	if ( x != x_values_.end() ) (*point)["x"] = *(x++);
		    	if ( y != y_values_.end() ) (*point)["y"] = *(y++);
		        double speed = 0;
		    	if ( xc != this->xc_values_.end() && yc != this->yc_values_.end() ) {
		    		speed = sqrt( (*xc * *xc)  +  (*yc * *yc) );
		    		(*point)["x_component"] = *(xc++);
		    		(*point)["y_component"] = *(yc++);
		    	}
		    	if ( v != this->v_values_.end() ) {
		    		(*point)["colour_component"] = *(v++);
		    	}
		    	else
		    		(*point)["colour_component"] = speed;



		    }
}
