#include <list>

#include "vislearning/cbaselib/BasicVectorTransformations.h"

using namespace OBJREC;
using namespace std;
using namespace NICE;

		/** ========= DO NOTHING =========== */

		NICE::Vector IdentityTransform::transform( const NICE::Vector& vec)
		{
			return vec;
		};

		NICE::Vector IdentityTransform::transform( const NICE::Vector& vec, const std::vector<NICE::Vector>& params)
		{
			return vec;
		}

		/** ========= UNIT LENGTH NORMALIZATION OF A VECTOR =========== */

		NICE::Vector UnitLengthTransform::transform( const NICE::Vector& vec)
		{
			NICE::Vector new_vec(vec);
			new_vec.normalizeL2();
			return new_vec;
		};

		NICE::Vector UnitLengthTransform::transform( const NICE::Vector& vec, const std::vector<NICE::Vector>& params)
		{
			//fprintf(stderr,"\nWARNING: NO PARAMETER IS NEEDED FOR UNIT LENGTH NORMALIZATION!\n");
			return transform(vec);
		}

		/** ========= ADD A CONSTANT TO THE VECTOR =========== */

		NICE::Vector ConstantShiftingTransform::transform( const NICE::Vector& vec)
		{
			fprintf(stderr,"\n\nERROR: ONE PARAMETER IS NEEDED FOR CONSTANT SHIFTING TRANSFORM!\n\n");
			exit(-1);
		};

		NICE::Vector ConstantShiftingTransform::transform( const NICE::Vector& vec, const std::vector<NICE::Vector>& params)
		{
			if(params.size()<1 || params[0].size()<1){
				fprintf(stderr,"\n\nERROR: ONE PARAMETER IS NEEDED FOR CONSTANT SHIFTING TRANSFORM!\n\n");
				exit(-1);
			}else{
				NICE::Vector new_vec(vec);
				new_vec+=params[0][0];
				return new_vec;
			}
		}

		/** ========= CONSTANT SCALING OF A VECTOR =========== */

		NICE::Vector ConstantScalingTransform::transform( const NICE::Vector& vec)
		{
			fprintf(stderr,"\n\nERROR: ONE PARAMETER IS NEEDED FOR CONSTANT SCALING TRANSFORM!\n\n");
			exit(-1);
		};

		NICE::Vector ConstantScalingTransform::transform( const NICE::Vector& vec, const std::vector<NICE::Vector>& params)
		{
			if(params.size()<1 || params[0].size()<1){
				fprintf(stderr,"\n\nERROR: ONE PARAMETER IS NEEDED FOR CONSTANT SCALING TRANSFORM!\n\n");
				exit(-1);
			}else{
				NICE::Vector new_vec(vec);
				new_vec*=params[0][0];
				return new_vec;
			}
		}


		/** ========= ELEMENTWISE ADDITION OF VECTORS =========== */

		NICE::Vector VectorShiftingTransform::transform( const NICE::Vector& vec)
		{
			fprintf(stderr,"\n\nERROR: ONE PARAMETER VECTOR IS NEEDED FOR VECTOR SHIFTING TRANSFORM!\n\n");
			exit(-1);
		};

		NICE::Vector VectorShiftingTransform::transform( const NICE::Vector& vec, const std::vector<NICE::Vector>& params)
		{
			if(params.size()<1 || params[0].size()!=vec.size() ){
				fprintf(stderr,"\n\nERROR: ONE PARAMETER VECTOR WITH APPROPRIATE DIMENSION IS NEEDED FOR VECTOR SHIFTING TRANSFORM!\n\n");
				exit(-1);
			}else{
				NICE::Vector new_vec(vec);
				new_vec+=params[0];
				return new_vec;
			}
		}

		/** ========= ELEMENTWISE MULTIPLICATION OF VECTORS =========== */

		NICE::Vector VectorScalingTransform::transform( const NICE::Vector& vec)
		{
			fprintf(stderr,"\n\nERROR: ONE PARAMETER VECTOR IS NEEDED FOR VECTOR SCALING TRANSFORM!\n\n");
			exit(-1);
		};

		NICE::Vector VectorScalingTransform::transform( const NICE::Vector& vec, const std::vector<NICE::Vector>& params)
		{
			if(params.size()<1 || params[0].size()!=vec.size() ){
				fprintf(stderr,"\n\nERROR: ONE PARAMETER VECTOR WITH APPROPRIATE DIMENSION IS NEEDED FOR VECTOR SCALING TRANSFORM!\n\n");
				exit(-1);
			}else{
				NICE::Vector new_vec(vec);
				new_vec*=params[0];
				return new_vec;
			}
		}


		/** ========= Running Median OF VECTOR =========== */
		NICE::Vector RunningMedianTransform::transform( const NICE::Vector& vec)
		{
			std::vector<NICE::Vector> param_vec;
			NICE::Vector param(1);
			param[0]=3;
			param_vec.push_back(param);
			return RunningMedianTransform::transform(vec,param_vec);
		};

		NICE::Vector RunningMedianTransform::transform( const NICE::Vector& vec, const std::vector<NICE::Vector>& params)
		{
			if(params.size()<1 || params[0].size()<1 || params[0][0]<1 || params[0][0]>=vec.size()){
				fprintf(stderr,"\n\nERROR: ONE POSITIVE 1 < PARAMETER < SIZE MUST BE PROVIDED AS MEDIAN WIDTH!\n\n");
				exit(-1);
			}else{
				std::list<double> auxlist;
				std::list<double> worklist;
				std::list<double>::iterator it;
				NICE::Vector median(vec.size());

				int vecsize=(int)vec.size();
				int size = (int) params[0][0];
				int klow= (size%2!=0 ? -(size-1)/2 : -size/2);
				int kup= (size%2!=0 ? (size-1)/2 : size/2-1);
				int mid= (size%2!=0 ? (size-1)/2 : size/2-1);
				//set up initial list, only k<0 case should occur
				for(int k=klow;k<kup+1;k++) {
					auxlist.push_back( (k<0 || k>=vecsize)? 0.0 : vec[k] );
				}
				worklist=auxlist;
				worklist.sort();

				if(size%2 != 0){ //real median
					int k=0;
					for (it=worklist.begin(); it!=worklist.end(); ++it){
						if(k >= mid){
							median[0]=*it;
							break;
						}
						k++;
					}
					//for all other inices >0
					for(int i=1;i<vecsize;i++){
						auxlist.pop_front();
						//only i+kup>=vecsize should occur
						auxlist.push_back( (i+kup>=vecsize || i+kup<0)? 0.0 : vec[i+kup] );
						worklist=auxlist;
						worklist.sort();
						k=0;
						for (it=worklist.begin(); it!=worklist.end(); ++it){
							if(k >= mid){
								median[i]=*it;
								break;
							}
							k++;
						}					
					}
				}else{ //midpoint between two centerd points
					int k=0;
					double m1,m2;
					for (it=worklist.begin(); it!=worklist.end(); ++it){
						if(k == mid){
							m1=*it;it++;m2=*it;
							median[0]=0.5*(m1+m2);
							break;
						}
						k++;
					}
					//for all other inices >0
					for(int i=1;i<vecsize;i++){
						auxlist.pop_front();
						//only i+kup>=vecsize should occur
						auxlist.push_back( (i+kup>=vecsize || i+kup<0)? 0.0 : vec[i+kup] );
						worklist=auxlist;
						worklist.sort();
						k=0;
						for (it=worklist.begin(); it!=worklist.end(); ++it){
							if(k == mid){
								m1=*it;++it;m2=*it;
								median[i]=0.5*(m1+m2);
								break;
							}
							k++;
						}					
					}
				}
				return median;
			}
		}


		/** ========= Running Mean OF VECTOR =========== */

		NICE::Vector RunningMeanTransform::transform( const NICE::Vector& vec)
		{
			std::vector<NICE::Vector> param_vec;
			NICE::Vector param(1);
			param[0]=3;
			param_vec.push_back(param);
			return RunningMeanTransform::transform(vec,param_vec);
		};

		//for mirroring
		uint RunningMeanTransform::get_index( int index, int vec_size)
		{
			if(index<0) return (-index-1);
			if(index>=vec_size) return (2*vec_size-index-1);
			return index;
		}

		NICE::Vector RunningMeanTransform::transform( const NICE::Vector& vec, const std::vector<NICE::Vector>& params)
		{
			if(params.size()<1 || params[0].size()<1 || params[0][0]<1 || params[0][0]>=vec.size()){
				fprintf(stderr,"\n\nERROR: ONE POSITIVE 1 < PARAMETER < SIZE MUST BE PROVIDED AS MEAN WIDTH!\n\n");
				exit(-1);
			}else{
				uint vecsize=vec.size();
				int size = (int) params[0][0];
				int klow= (size%2!=0 ? -(size-1)/2 : -size/2);
				int kup= (size%2!=0 ? (size-1)/2 : size/2-1);
				//augment vector with mirroring elements at the boundary
				NICE::Vector mean( vec.size() );
				double mean_i=0;
				for(int k=klow;k<kup+1;k++) {mean_i+=vec[get_index(k,vecsize)];}
				mean[0]=mean_i/size;
				for(int i=1;i<(int)vec.size();i++){
					mean_i+=vec[get_index(i+kup,vecsize)]-vec[get_index(i+klow,vecsize)];
					mean[i]=mean_i/size;
				}
				return mean;
			}
		}

