#include <iostream>
#include "gmapping/scanmatcher/scanmatcherprocessor.h"
#include "gmapping/scanmatcher/eig3.h"

//#define SCANMATHCERPROCESSOR_DEBUG
namespace GMapping {

using namespace std;

ScanMatcherProcessor::ScanMatcherProcessor(const ScanMatcherMap& m)
  : m_map(m.getCenter(), m.getWorldSizeX(), m.getWorldSizeY(), m.getResolution()), 
    m_pose(0,0,0){
  m_regScore=300;
  m_critScore=.5*m_regScore;
  m_maxMove=1;
  m_beams=0;
  m_computeCovariance=false;
  //m_eigenspace=gsl_eigen_symmv_alloc(3);
  useICP=false;
}


ScanMatcherProcessor::ScanMatcherProcessor 
  (double xmin, double ymin, double xmax, double ymax, double delta, double patchdelta):
  m_map(Point((xmax+xmin)*.5, (ymax+ymin)*.5), xmax-xmin, ymax-ymin, patchdelta), m_pose(0,0,0){
	m_regScore=300;
	m_critScore=.5*m_regScore;
	m_maxMove=1;
	m_beams=0;
	m_computeCovariance=false;
	//m_eigenspace=gsl_eigen_symmv_alloc(3);
	useICP=false;
}

ScanMatcherProcessor::~ScanMatcherProcessor (){
	//gsl_eigen_symmv_free(m_eigenspace);
}


void ScanMatcherProcessor::setSensorMap(const SensorMap& smap, std::string sensorName){
	m_sensorMap=smap;
	
	/*
	 Construct the angle table for the sensor
	 
	 FIXME has to be extended to more than one laser... 
	*/
	
	SensorMap::const_iterator laser_it=m_sensorMap.find(sensorName);
	assert(laser_it!=m_sensorMap.end());
	const RangeSensor* rangeSensor=dynamic_cast<const RangeSensor*>((laser_it->second));
	assert(rangeSensor && rangeSensor->beams().size());
	
	m_beams=static_cast<unsigned int>(rangeSensor->beams().size());
	double* angles=new double[rangeSensor->beams().size()];
	for (unsigned int i=0; i<m_beams; i++){
		angles[i]=rangeSensor->beams()[i].pose.theta;
	}
	m_matcher.setLaserParameters(m_beams, angles, rangeSensor->getPose());
	delete [] angles;
	
	
}

void ScanMatcherProcessor::init(){
	m_first=true;
	m_pose=OrientedPoint(0,0,0);
	m_count=0;
}

void ScanMatcherProcessor::processScan(const RangeReading & reading){
	/**retireve the position from the reading, and compute the odometry*/
	OrientedPoint relPose=reading.getPose();
	if (!m_count){
		m_odoPose=relPose;
	}

	//compute the move in the scan m_matcher
	//reference frame

	OrientedPoint move=relPose-m_odoPose;

	double dth=m_odoPose.theta-m_pose.theta;
	// cout << "rel-move x="<< move.x <<  " y=" << move.y << " theta=" << move.theta << endl;

	double lin_move=move*move;
	if (lin_move>m_maxMove){
		cerr << "Too big jump in the log file: " << lin_move << endl;
		cerr << "relPose=" << relPose.x << " " <<relPose.y << endl;
		cerr << "ignoring" << endl;
		return;
		//assert(0);
		dth=0;
		move.x=move.y=move.theta=0;
	}
	
	double s=sin(dth), c=cos(dth);
	OrientedPoint dPose;
	dPose.x=c*move.x-s*move.y;
	dPose.y=s*move.x+c*move.y;
	dPose.theta=move.theta;

#ifdef SCANMATHCERPROCESSOR_DEBUG
	cout << "abs-move x="<< dPose.x <<  " y=" << dPose.y << " theta=" << dPose.theta << endl;
#endif
	m_pose=m_pose+dPose;
	m_pose.theta=atan2(sin(m_pose.theta), cos(m_pose.theta));

#ifdef SCANMATHCERPROCESSOR_DEBUG
	cout << "StartPose: x="
	<< m_pose.x << " y=" << m_pose.y << " theta=" << m_pose.theta << endl;
#endif

	m_odoPose=relPose; //update the past pose for the next iteration


	//FIXME here I assume that everithing is referred to the center of the robot,
	//while the offset of the laser has to be taken into account
	
	assert(reading.size()==m_beams);
/*	
	double * plainReading = new double[m_beams];
#ifdef SCANMATHCERPROCESSOR_DEBUG
	cout << "PackedReadings ";
#endif
	for(unsigned int i=0; i<m_beams; i++){
		plainReading[i]=reading[i];
#ifdef SCANMATHCERPROCESSOR_DEBUG
		cout << plainReading[i] << " ";
#endif
	}
*/
	double * plainReading = new double[m_beams];
	reading.rawView(plainReading, m_map.getDelta());
	
	
#ifdef SCANMATHCERPROCESSOR_DEBUG
	cout << endl;
#endif
	//the final stuff: scan match the pose
	double score=0;
	OrientedPoint newPose=m_pose;
	if (m_count){
		if(m_computeCovariance){
			ScanMatcher::CovarianceMatrix cov;
			score=m_matcher.optimize(newPose, cov, m_map, m_pose, plainReading);
                        /*
			gsl_matrix* m=gsl_matrix_alloc(3,3);
			gsl_matrix_set(m,0,0,cov.xx); gsl_matrix_set(m,0,1,cov.xy); gsl_matrix_set(m,0,2,cov.xt);
			gsl_matrix_set(m,1,0,cov.xy); gsl_matrix_set(m,1,1,cov.yy); gsl_matrix_set(m,1,2,cov.yt);
			gsl_matrix_set(m,2,0,cov.xt); gsl_matrix_set(m,2,1,cov.yt); gsl_matrix_set(m,2,2,cov.tt);
			gsl_matrix* evec=gsl_matrix_alloc(3,3);
			gsl_vector* eval=gsl_vector_alloc(3);
                        */
                        double m[3][3];
                        double evec[3][3];
                        double eval[3];
			m[0][0] = cov.xx; 
                        m[0][1] = cov.xy; 
                        m[0][2] = cov.xt;
			m[1][0] = cov.xy;
                        m[1][1] = cov.yy;
                        m[1][2] = cov.yt;
			m[2][0] = cov.xt;
                        m[2][1] = cov.yt;
                        m[2][2] = cov.tt;

			//gsl_eigen_symmv (m, eval,  evec, m_eigenspace);
                        eigen_decomposition(m,evec,eval);
#ifdef SCANMATHCERPROCESSOR_DEBUG
			//cout << "evals=" << gsl_vector_get(eval, 0) <<  " " << gsl_vector_get(eval, 1)<< " " << gsl_vector_get(eval, 2)<<endl;
			cout << "evals=" << eval[0] <<  " " << eval[1]<< " " << eval[2]<<endl;
#endif
			//gsl_matrix_free(m);
			//gsl_matrix_free(evec);
			//gsl_vector_free(eval);
		} else {
			if (useICP){
				cerr << "USING ICP" << endl;
				score=m_matcher.icpOptimize(newPose, m_map, m_pose, plainReading);
			}else
				score=m_matcher.optimize(newPose, m_map, m_pose, plainReading);
		}
		
		
	}
	//...and register the scan
	if (!m_count || score<m_regScore){
#ifdef SCANMATHCERPROCESSOR_DEBUG
		cout << "Registering" << endl;
#endif
		m_matcher.invalidateActiveArea();
		if (score<m_critScore){
#ifdef SCANMATHCERPROCESSOR_DEBUG
			cout << "New Scan added, using odo pose" << endl;
#endif
			m_matcher.registerScan(m_map, m_pose, plainReading);
		} else {
			m_matcher.registerScan(m_map, newPose, plainReading);
#ifdef SCANMATHCERPROCESSOR_DEBUG
			cout << "New Scan added, using matched pose" << endl;
#endif
		}	
	}

#ifdef SCANMATHCERPROCESSOR_DEBUG
	cout << " FinalPose: x="
		<< newPose.x << " y=" << newPose.y << " theta=" << newPose.theta << endl;
	cout << "score=" << score << endl;
#endif
	m_pose=newPose;
	delete [] plainReading;
	m_count++;
}

void ScanMatcherProcessor::setMatchingParameters
	(double urange, double range, double sigma, int kernsize, double lopt, double aopt, int iterations, bool computeCovariance){
	m_matcher.setMatchingParameters(urange, range,  sigma, kernsize, lopt, aopt, iterations);
	m_computeCovariance=computeCovariance;
}

void ScanMatcherProcessor::setRegistrationParameters(double regScore, double critScore){
	m_regScore=regScore;
	m_critScore=critScore;
}
		

OrientedPoint ScanMatcherProcessor::getPose() const{
	return m_pose;
}

};

