/******************************** 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 NetcdfMatrixInterpretor.h
    \brief Implementation of the Template class NetcdfMatrixInterpretor.
    
    Magics Team - ECMWF 2004
    
    Started: Tue 17-Feb-2004
    
    Changes:
    
*/

#include <limits>

#include "NetcdfMatrixInterpretor.h"
#include "Factory.h"
#include "Netcdf.h"
#include "Coordinate.h"
#include "Layer.h"
#include "TextVisitor.h"

using namespace magics;

NetcdfMatrixInterpretor::NetcdfMatrixInterpretor() : baseDateX_(""), baseDateY_("")
{
}


NetcdfMatrixInterpretor::~NetcdfMatrixInterpretor() 
{
}


void NetcdfMatrixInterpretor::interpretAsMatrix(Matrix** matrix)
{
	
	MagLog::debug() << "NetcdfMatrixInterpretor::interpret()--->" << *this << "\n";
	if ( *matrix ) return;
	
	matrix_ = new Matrix();
	
	*matrix = matrix_;
	if ( !matrix_->empty() ) return;
    
	matrix_->missing(std::numeric_limits<double>::max());

	Netcdf netcdf(path_);
	string title = netcdf.getAttribute("title", "NO TITLE");


    x();
    y();
	// get the data ...
	try {
		map<string, string> first, last;
		setDimensions(dimension_, first, last);
		MagLog::debug() << "matrix_" << matrix_->size() << "\n";
        
		netcdf.get(field_, *matrix_, first, last);
		
		MagLog::debug() << "matrix_[" << matrix_->size() << ", " << scaling_ << ", " << offset_ << "]" << "\n";

		matrix_->multiply(scaling_);
		matrix_->plus(offset_);
        
       
        
		MagLog::debug() << "matrix_[" << matrix_->size() << ", " << scaling_ << ", " << offset_ << "\n";
        
		
		vector<double> col;
	  
		string missing = netcdf.getVariableAttribute(field_, missing_attribute_, "");
		if ( !missing.empty() ) {
			std::stringstream ss(missing);		
			double m;
			ss >> m;
            matrix_->missing(m);
		}
		
		
		matrix_->setColumnsAxis(columns_);
		matrix_->setRowsAxis(rows_);
		
    
		MagLog::dev() << *matrix_ << "\n";
	}
	catch (MagicsException& e)
	{
		MagLog::error() << e << "\n";
	}    
}



/*!
 Class information are given to the output-stream.
*/		
void NetcdfMatrixInterpretor::print(ostream& out)  const
{
	out << "NetcdfMatrixInterpretor[";
	NetcdfInterpretor::print(out);
	NetcdfMatrixInterpretorAttributes::print(out);
	out << "]";
}
static map<string, double> _factors;
	
void NetcdfMatrixInterpretor::x() {
    if ( !columns_.empty() ) return;
    if ( _factors.empty() ) {
		_factors["hours"] = 3600;
		_factors["days"] = 24*3600;
	}
    Netcdf netcdf(path_);
    map<string, string> first, last;
    setDimensions(dimension_, first, last);

    try {
        netcdf.get(x_,columns_, first, last);
        string date = netcdf.getVariableAttribute(x_, "reference_date", "");
		if ( !date.empty() ) {
			// here we are in a Date coordinate system!
			string units = netcdf.getVariableAttribute(x_, "units", "");
			baseDateX_ = date;
			double diff = ( reference_.empty() ) ? 0 : DateTime(date) - DateTime(reference_) ;
			map<string, double>::const_iterator factor = _factors.find(units);
			if ( factor != _factors.end() )
				std::transform(columns_.begin(), columns_.end(),  columns_.begin(), Multiply(factor->second, std::numeric_limits<double>::max()));	
			std::transform(columns_.begin(), columns_.end(),  columns_.begin(), Plus(diff, std::numeric_limits<double>::max()));
		}
		else
			baseDateX_ = "";
	} 
    catch (...) {
        int x = netcdf.getDimension(x_);
        for (int i = 0; i < x; i++) 
            columns_.push_back(i);
    }

    if ( aux_.empty() )
    	return;
    try {

    	vector<double> aux;

    	netcdf.get(aux_,aux, first, last);
    	if ( !aux.empty() ) {
    		ostringstream geominx, geomaxx;
    		geominx << columns_.front() << "/" << aux.front() << endl;
    		geomaxx << columns_.back() << "/" << aux.back() << endl;
    		geoMinX_ = geominx.str();
    		geoMaxX_ = geomaxx.str();
    	}
    }
    catch (...) {
    }

}

void NetcdfMatrixInterpretor::y() {
    if ( !rows_.empty() ) return;
    if ( _factors.empty() ) {
		_factors["hours"] = 3600;
		_factors["days"] = 24*3600;
	}
    Netcdf netcdf(path_);
    map<string, string> first, last;
    setDimensions(dimension_, first, last);
    try {
        netcdf.get(y_,rows_, first, last);
        string date = netcdf.getVariableAttribute(y_, "reference_date", "");
		if ( !date.empty()) {
			// here we are in a Date coordinate system!
			baseDateY_ = date;
			string units = netcdf.getVariableAttribute(y_, "units", "");
			map<string, double>::const_iterator factor = _factors.find(units);
			if ( factor != _factors.end() )
				std::transform(rows_.begin(), rows_.end(),  rows_.begin(), Multiply(factor->second, std::numeric_limits<double>::max()));	

			DateTime reference = reference_.empty() ?  DateTime(date) + Second(rows_.front()) : DateTime(reference_);

			double diff = DateTime(date) - reference ;
            MagLog::debug() << "DATE--->" << date << "--->" << reference_ << " [" << diff << "]\n";

			std::transform(rows_.begin(), rows_.end(),  rows_.begin(), Plus(diff, std::numeric_limits<double>::max()));


		}
		else
			baseDateY_ = "";

	} 
    catch (...) {
        int y = netcdf.getDimension(y_);
        for (int i = 0; i < y; i++) 
            rows_.push_back(i);
    } 
}
void NetcdfMatrixInterpretor::visit(Transformation& transformation)
{
	// get the data ...
		// by default, respect the internal organisation of the data..
		try {

			reference_ = ( transformation.getAutomaticX() ) ? "" : transformation.getReferenceX();
			x();
			reference_ = ( transformation.getAutomaticY() ) ? "" : transformation.getReferenceY();
			y();

			if ( transformation.getAutomaticX() ) {
				if ( !this->baseDateX_.empty() ) {
					transformation.setDataMinX(columns_.front(), this->baseDateX_);
					transformation.setDataMaxX(columns_.back(), this->baseDateX_);
				}
				else if ( !this->geoMinX_.empty() ) {
					transformation.setDataMinX(columns_.front(), this->geoMinX_);
					transformation.setDataMaxX(columns_.back(), this->geoMaxX_);
				}
				else
				{
					transformation.setMinX(columns_.front());
					transformation.setMaxX(columns_.back());
				}
			}
			if ( transformation.getAutomaticY() ) {
				if ( !this->baseDateY_.empty() ) {
					transformation.setDataMinY(rows_.front(), this->baseDateY_);
					transformation.setDataMaxY(rows_.back(), this->baseDateY_);
				}
				else {
					transformation.setMinY(rows_.front());
					transformation.setMaxY(rows_.back());
				}
			}

		}
		catch ( ... ) {}
		

}


void NetcdfMatrixInterpretor::visit(MetaDataCollector&)
{
	
}

void NetcdfMatrixInterpretor::visit(ValuesCollector& points)
{
	points.setCollected(true);
	
	assert(matrix_); 
	//const Transformation& transformation = points.transformation();	
	
	for (ValuesCollector::iterator point =  points.begin(); point != points.end(); ++point) {
		//PaperPoint pp((*point).x(), (*point).y());
		UserPoint up((*point).x(), (*point).y());
		//transformation.revert(pp, up);	
		(*point).push_back(ValuesCollectorData(up.x(),up.y(),matrix_->interpolate(up.y(), up.x()),-1.));
	}
}

void NetcdfMatrixInterpretor::visit(TextVisitor& title)
{
	vector<string> titles;

	title.titles(titles);


	for ( vector<string>::const_iterator t = titles.begin(); t != titles.end(); ++t ) {
		cout << "NetcdfMatrixInterpretor::visit" << *t << endl;

	}
	Netcdf netcdf(path_);
	title.add(new TextEntry(netcdf.getAttribute("title", "NO TITLE")));
}
