#ifndef LYCCA_PROCESS_DSSSD_HPP_
#define LYCCA_PROCESS_DSSSD_HPP_

#include <prespec/process/Processor.hpp>

//! @brief Evaluates information from the DSSD (double-sided silicon stip detector) 
//!        that are used in the Lycca wall detector and the target detector for 
//!        particle tracking and identification. This processor supports variable strip
//!        numbers.
class DSSSD : public prespec::process::Processor
{
	public:
		DSSSD(const std::string &config_dir,
				const std::string &name,
				bool no_files);
		~DSSSD();			
		virtual void process(prespec::viscon::Interface &viscon_interface, int trigger);
		
	    // the inputs to this processor
		enum InputArray
		{
			amplitude_p  ,   // adc amplitudes of p-side channels.
			                 // The number of indices in this array is 
			                 // given by the parameter "strip_num_p"
			amplitude_n  ,   // adc amplitudes of n-side channels.
			                 // The number of indices in this array is 
			                 // given by the parameter "strip_num_n"
			                 
			time_p,          // time signal, usually taken from the p-side
		};

		enum OutputArray
		{
			cal_amplitude_p  ,   // adc amplitudes of p-side channels.
			                     // The number of indices in this array is 
			                     // given by the parameter "strip_num_p"
			cal_amplitude_n  ,   // adc amplitudes of n-side channels.
			                     // The number of indices in this array is 
			                     // given by the parameter "strip_num_n"
			cal_time_p       ,   // aligned time signals                 
		};

		// the outputs of this processor
		enum Output
		{
			x    ,                  // x-position 
			y    ,                  // y-position
			x_sub,                  // x-position sub strip resolution
			y_sub,                  // y-position sub strip resolution
			dE_p ,                  // energy loss p-side
			dE_n ,                  // energy loss n-side
			dE,		                // energy loss p/n average
			multiplicity_p,         // how many strips on the p-side were hit
			multiplicity_n,         // how many strips on the n-side were hit
			cluster_multiplicity_p, // how many separated groups of p-side strips were hit
			cluster_multiplicity_n, // how many separated groups of n-side strips were hit
			dE_max_n,		// maximum n dE value in an event. Added by LS
			dE_max_p,		// maximum p dE value in an event. Added by LS
			dE_max,			// calibrated maximum average dE value in an event. Added by LS

		};
		
		enum Parameter
		{
			strip_num_p ,    // number of p-side strips
			thresholds_p,    // upper and lower threshold of p-side energy
			strip_num_n ,    // number of n-side strips
			thresholds_n,    // upper and lower threshold of n-side energy
			x_center,        // x-position of DSSD center
			y_center,        // y-position of DSSD center
			strip_width_p,   // width of one p-side strip;
			strip_width_n,   // width of one n-side strip;
			left_right,      // if this is 1: orientation from left to right 
			                 // if this is -1: orientation from right to left
			top_bottom,      // if this is 1: orientation from top to bottom 
			                 // if this is -1: orientation from bottom to top
			rotate,          // rotate by 0 ->   0 deg
							 //           1 ->  90 deg
							 //           2 -> 180 deg
							 //           3 -> 270 deg
			selfcalibration, // 4 values: number points, minimum slope, maximum slope, width of cauchy distribution
			                 // selfcalibration is deactivated if number points = 0
			selfcalibration2,// 4 values: number points, minimum slope, maximum slope, width of cauchy distribution
			                 // selfcalibration2 is deactivated if number points = 0
		};
		
		enum Calibration
		{
			cal_p ,          // energy calibration for p-side
			cal_n ,          // energy calibration for n-side
			cal_t ,          // time alignment for the p-side time signals
			
			cal_dE,          // after gain matching the amplitudes of single strips with cal_p and cal_n,
			                 // this coefficient is supposed to calibrate to fixed units (for example to MeV)
		};
	
	private:
		void rotate_xy(double x, double y, double &x_rot, double &y_rot, int i); // i = 0,1,2,3 rotates by phi=0,90,180,270 degrees

		// data for selfcalibration
		std::vector<std::vector<std::vector<double> > > prob_logs_np; // a 2d array of likelihood functions
		std::vector<std::vector<double> > slopes;		              // the Snp matrix entries
		std::vector<std::vector<double> > slope_errors;		          // the errors of the Snp matrix entries

		// data for selfcalibration2
		std::vector<std::vector<double> > prob_log_n_slopes;
		std::vector<std::vector<double> > prob_log_p_slopes;
		
		void do_selfcalibration();
		void do_selfcalibration2();
		void fit_slopes();
		double cauchy(double x, double width);
};

class FitFunction
{
	public:
		FitFunction(int num_n, int num_p)
			: num_n_(num_n), num_p_(num_p)
		{}
		
		double operator()(int np, const double *pbegin, const double *pend);
	private:
		int num_n_;
		int num_p_;	
};

class FitResidues
{
	public:
		double operator()(double f, double v, double s);
};

#endif
