// $Id$
//
//    File: MyProcessor.cc
// Created: Wed Nov 30 07:12:58 EST 2011
// Creator: davidl (on Linux ifarm1101 2.6.18-128.7.1.el5 x86_64)
//

#include <iostream>
#include <vector>
#include <string>
using namespace std;

#include <TGClient.h>
#include <TLatex.h>

#include <FCALTEST2011/D250WindowRawData.h>
#include <FCALTEST2011/D250EventTime.h>
#include <FCALTEST2011/DMiniFCALHit.h>
#include <FCALTEST2011/DPaddleHit.h>
#include <FCALTEST2011/DRemoteScintillatorHit.h>

#include "MyProcessor.h"
using namespace jana;

#include "fcal11view.h"
#include "fcal11_mainframe.h"
#include "fcal11_fADCviewer.h"
#include "fcal11_SignalViewer.h"

MyProcessor *gMYPROC=NULL;

//------------------
// MyProcessor (Constructor)
//------------------
MyProcessor::MyProcessor()
{
	fcal11mf = NULL;

	hsamples = new TH1D("hsamples", "", 10, -0.5, 9.5);

	for(int i=0; i<5; i++){
		for(int j=0; j<5; j++){
			char hname[256];
			sprintf(hname,"c%dr%d", i, j);
			TH1D *h = new TH1D(hname, "", 50, -0.5, 49.5);
			h->SetStats(0);
			hsignals[i][j] = h;
		}
	}

	gMYPROC = this;
}

//------------------
// ~MyProcessor (Destructor)
//------------------
MyProcessor::~MyProcessor()
{

}

//------------------
// init
//------------------
jerror_t MyProcessor::init(void)
{
	if(!fcal11mf) fcal11mf = new fcal11_mainframe(gClient->GetRoot(), 1000, 600);


	return NOERROR;
}

//------------------
// brun
//------------------
jerror_t MyProcessor::brun(JEventLoop *eventLoop, int runnumber)
{
	return NOERROR;
}

//------------------
// evnt
//------------------
jerror_t MyProcessor::evnt(JEventLoop *loop, int eventnumber)
{
	if(!loop)return NOERROR;
	this->loop = loop;
	last_jevent.FreeEvent();
	last_jevent = loop->GetJEvent();

	string source = "<no source>";
	if(last_jevent.GetJEventSource())source = last_jevent.GetJEventSource()->GetSourceName();

	// Check user-defined filter settings are satisfied before
	// displaying the event. If not, set up to immediately check
	// the next event.
	if(SoftwareTrigger()){
	
		cout<<"----------- New Event "<<eventnumber<<" -------------"<<endl;
		fcal11mf->SetEvent(eventnumber);
		fcal11mf->SetSource(source.c_str());

		UpdateBlocks();
		UpdatePaddles();

		if(!gfADCVIEWER->IsHidden())UpdateHisto();
		
		if(!gSIGNALVIEWER->IsHidden())UpdateSignals();
		
	}else{
		fcal11mf->SetEvent(-1);
		fcal11mf->events_to_process++;
	}

	return NOERROR;
}

//------------------
// erun
//------------------
jerror_t MyProcessor::erun(void)
{
	return NOERROR;
}

//------------------
// fini
//------------------
jerror_t MyProcessor::fini(void)
{
	// Called before program exit after event processing is finished.
	return NOERROR;
}

//------------------
// SoftwareTrigger
//------------------
bool MyProcessor::SoftwareTrigger(void)
{
	// Get horizontal and vertical paddle trigger filter
	UInt_t htrig_mask = 0;
	UInt_t vtrig_mask = 0;
	bool require_both;
	fcal11mf->GetTrigRequirement(htrig_mask, vtrig_mask, require_both);
	for(int i=1; i<=5; i++){
		char name[256];
		sprintf(name, "htrig%d", i);
		if(fcal11mf->GetCheckButton(name))htrig_mask |= 0x1<<(i-1);
		sprintf(name, "vtrig%d", i);
		if(fcal11mf->GetCheckButton(name))vtrig_mask |= 0x1<<(i-1);
	}
	
	// Find which paddles fired
	vector<const DPaddleHit*> paddles;
	loop->Get(paddles);
	UInt_t htrig = 0;
	UInt_t vtrig = 0;
	for(unsigned int i=0; i<paddles.size(); i++){
		const DPaddleHit *paddle = paddles[i];
		if(paddle->sum>1000.0){
			if(paddle->orientation ==0){
				htrig |= 0x1<<(paddle->chid);
			}else{
				vtrig |= 0x1<<(paddle->chid);
			}
		}
	}
	
	// Find if remote scintillator fired
	bool remote_trig = false;
	if(fcal11mf->GetCheckButton("require_remote")){
		const DRemoteScintillatorHit* remote=NULL;
		try{
			loop->GetSingle(remote);
			if(remote){
				if(remote->sum>1000.0)remote_trig = true;
			}
		}catch(...){}
	}
	
	// Determine if the event should be displayed
	bool display_event = true;
	bool htrig_fired = htrig & htrig_mask;
	bool vtrig_fired = vtrig & vtrig_mask;

	if(htrig_mask | vtrig_mask){
		if(require_both){
			display_event = htrig_fired && vtrig_fired;
		}else{
			display_event = htrig_fired || vtrig_fired;
		}
	}else{
		if(remote_trig)display_event = true;
	}
	
	return display_event;
}

//------------------
// UpdateHisto
//------------------
void MyProcessor::UpdateHisto(void)
{
	// Get the all samples objects for all channels
	vector<const D250WindowRawData*> windowRawData;
	loop->Get(windowRawData);
	
	// Search for object for the currently selected slot and channel
	for(unsigned int i=0; i<windowRawData.size(); i++){
		const D250WindowRawData *wrd = windowRawData[i];
		if(wrd->chan != gfADCVIEWER->channel)continue;

		const D250EventTime *eventTime = NULL;
		wrd->GetSingle(eventTime);
		if(eventTime){
			if(eventTime->slot != gfADCVIEWER->slot)continue;

			// Resize histo if needed since one module may return more samples
			if((int)wrd->samples.size() != hsamples->GetNbinsX()){
				hsamples->SetBins(wrd->samples.size(), -0.5, (double)wrd->samples.size()-0.5);
			}
			
			// Optionally determine a pedestal to subtract
			double ped = 0.0;
			if(gfADCVIEWER->GetCheckButton("sub_peds")){
				unsigned int Nped_samples=0;
				for(; Nped_samples<10; Nped_samples++){
					if(Nped_samples>=wrd->samples.size())break;
					ped += (double)wrd->samples[Nped_samples];
				}
				ped /= (double)Nped_samples;
			}
			
			// Copy samples into histo bin contents
			for(unsigned int j=0; j<wrd->samples.size(); j++){
				hsamples->SetBinContent(j, (double)wrd->samples[j] - ped);
			}
			
			// Optionally have histogram auto-scale
			if(gfADCVIEWER->GetCheckButton("autoscale")){
				hsamples->GetYaxis()->UnZoom();
			}else{
				hsamples->GetYaxis()->SetRangeUser(-10.0, 1000.0);
			}
			
			// Set histo title and display options
			char title[256];
			sprintf(title, "Slot %d Channel %d", eventTime->slot, wrd->chan);
			hsamples->SetTitle(title);
			hsamples->SetStats(0);

			gfADCVIEWER->canvas->GetCanvas()->cd();
			gPad->SetTicks();
			gPad->SetGrid();

			hsamples->Draw();
			gfADCVIEWER->canvas->GetCanvas()->Update();
			break;
		}

	}
}

//------------------
// UpdateSignals
//------------------
void MyProcessor::UpdateSignals(void)
{
	// Get the all samples objects for all channels
	vector<const DMiniFCALHit*> hits;
	loop->Get(hits);
	
	// Search for object for the currently selected slot and channel
	for(unsigned int i=0; i<hits.size(); i++){
		const DMiniFCALHit *hit = hits[i];
		
		if(hit->row<0 || hit->row>4 || hit->col<0 || hit->row>4)continue;

		TH1D *h = hsignals[hit->col][hit->row];

		const D250WindowRawData *wrd = NULL;
		hit->GetSingle(wrd);
		if(wrd){
			// Resize histo if needed since one module may return more samples
			if((int)wrd->samples.size() != h->GetNbinsX()){
				h->SetBins(wrd->samples.size(), -0.5, (double)wrd->samples.size()-0.5);
			}
			
			// Optionally determine a pedestal to subtract
			double ped = 0.0;
			if(gSIGNALVIEWER->GetCheckButton("sub_peds")){
				unsigned int Nped_samples=0;
				for(; Nped_samples<10; Nped_samples++){
					if(Nped_samples>=wrd->samples.size())break;
					ped += (double)wrd->samples[Nped_samples];
				}
				ped /= (double)Nped_samples;
			}
			
			// Copy samples into histo bin contents
			double sum = 0.0;
			for(unsigned int j=0; j<wrd->samples.size(); j++){
				h->SetBinContent(j, (double)wrd->samples[j] - ped);
				sum += (double)wrd->samples[j] - ped;
			}

			// Optionally have histogram auto-scale
			double y;
			if(gSIGNALVIEWER->GetCheckButton("autoscale")){
				h->GetYaxis()->UnZoom();
				y = h->GetMaximum()*0.85;
			}else{
				h->GetYaxis()->SetRangeUser(-10.0, 1000.0);
				y =1000.0*0.85;
			}

			// Set histo title and display options
			char title[256];
			sprintf(title, "row %d , col %d", hit->col, hit->row);
			h->SetTitle(title);
			h->SetStats(0);

			gSIGNALVIEWER->SetPad(hit->col, hit->row);
			h->Draw();
			
			if(gSIGNALVIEWER->GetCheckButton("sum_labels")){
				char str[256];
				sprintf(str, "sum=%4.0f", sum);
				double x = 48;
				TLatex *lab = new TLatex(x, y, str);
				lab->SetTextAlign(32);
				lab->SetTextSize(0.1);
				lab->Draw();
			}
		}
	}
	
	gSIGNALVIEWER->canvas->GetCanvas()->Update();
}

//------------------
// UpdateBlocks
//------------------
void MyProcessor::UpdateBlocks(void)
{
	vector<const DMiniFCALHit*> hits;
	loop->Get(hits);
	
	fcal11mf->canvas->GetCanvas()->cd();
	
	double x = 0.0;
	double y = 0.0;
	double Etot = 0.0;
	for(unsigned int i=0; i<hits.size(); i++){
		const DMiniFCALHit *hit = hits[i];
		
		double scale = 1.0;
		float val = log10(hit->sum/scale)/log10(2000.0/scale);
		if(hit->sum<=0.0)val=0.0;
		fcal11mf->SetBlockColor(hit->row, hit->col, val);
		
		if(hit->sum > 1.0){
			double weight = hit->sum;
			if(fcal11mf->GetCheckButton("log_weighting"))weight = log(weight);
			x += ((double)hit->col-2.0)*weight;
			y += ((double)(4-hit->row)-2.0)*weight;
			Etot += weight;
		}
	}
	
	if(Etot>0.0){
		x /= Etot;
		y /= Etot;
		
		x *= 4.0; // 4 cm block size
		y *= 4.0;
		
		fcal11mf->DrawDot(x, y);
	}

	fcal11mf->canvas->GetCanvas()->Update();
}


//------------------
// UpdatePaddles
//------------------
void MyProcessor::UpdatePaddles(void)
{
	vector<const DPaddleHit*> hits;
	loop->Get(hits);
	
	fcal11mf->canvas->GetCanvas()->cd();
	
	for(unsigned int i=0; i<hits.size(); i++){
		const DPaddleHit *hit = hits[i];
		
		fcal11mf->SetPaddleColor(hit->orientation, hit->chid, hit->sum>1000);
	}

	fcal11mf->canvas->GetCanvas()->Update();
}