/*
Copyright (c) 2000                      RIPE NCC


All Rights Reserved

Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation, and that the name of the author not be
used in advertising or publicity pertaining to distribution of the
software without specific, written prior permission.

THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

/*
-------------------------------------------------------------------------------
Module Header
Filename          : DelayPlots.C
Author            : Rene Wilhelm
Date              : 13-OCT-2000
Description       : Implementation of TTM DelayPlots class
Language Version  : C++
OSs Tested        : Solaris 2.6
External Programs : ps2gif (which in turn needs ghostscript and imaging tools)
$Id: DelayPlots.C,v 1.17 2002/08/09 14:32:08 henk Exp $
-------------------------------------------------------------------------------
*/

#define PATH "/ncc/ttpro/data/root"   // path to TTM ROOT data storage
#define TYPE 'H'		      // hierarchical (year/month/day) storage

#include <stdlib.h>
#include <time.h>
#include <iostream.h>
#include <TCanvas.h>
#include <TLine.h>
#include <TPaveLabel.h>
#include <TPostScript.h>
#include <TText.h>
#include "Delay.h"
#include "DelayPlots.h"
#include "utils.h"

static char const rcsid[] = "$Id: DelayPlots.C,v 1.17 2002/08/09 14:32:08 henk Exp $";



void DelayPlots::SetSourceTarget(TestBox src, TestBox **dest) {

	// initialize source and targets 

	int i;
	uint k;

	MaxBoxId=0;
	source = src;
	for (i=0; i<ntargets; i++) {
		target[i] = *dest[i];
		if (target[i].id > MaxBoxId) {
			MaxBoxId = target[i].id;
		}
	}

	// create mapping between testbox id and array index
	// since active testbox id's not necessarily form a continuum in
	// the integer number space, this indirection minimizes
	// overhead in memory allocation

	id2index = new int[MaxBoxId+1];

	// initialize to -1 == no such box in target list
	for (k=0; k<=MaxBoxId; k++) {
		id2index[k] = -1;
	}

	// now fill in the assignments from the list
	for (i=0; i<ntargets; i++) {
		id2index[target[i].id] = i;
	}
}


void DelayPlots::CSV (TestBox src, TestBox **dest, int ndest, time_t period,
		time_t end, char* outdir) {

	// Print data as CSV to corresponding .csv file(s) in output directory

	Int_t nProcessed = 0;
	Int_t nAccepted	= 0;
	int tgt, targetId, index;

	time_t newstart = end - period;

	if ((newstart == starttime) && (end == endtime) && (ndest == ntargets)) {
		// (re)set source & destination
		SetSourceTarget(src, dest);
	} 
	else {
		endtime = end;
		tm *timestruct = gmtime(&endtime);
		strftime(cendtime1, 20, "%Y-%m-%d %H:%M", timestruct);
		strftime(cendtime2, 8, "%b %d", timestruct);
		starttime = newstart;
		timestruct = gmtime(&starttime);
		strftime(cstarttime1, 20, "%Y-%m-%d %H:%M", timestruct);
		strftime(cstarttime2, 8, "%b %d", timestruct);
		ntargets = ndest;
		target = new TestBox[ntargets];
		SetSourceTarget(src, dest);
	}

	// chain data
	if (datachain != NULL) {
		delete datachain;	// dispose of old data chain
	}

	datachain = new TTMDataChain(source, endtime, period, PATH, TYPE);
			
	rootchain = datachain->getRootChain();
	if (rootchain == NULL) {
		fprintf(stderr, "%s: ERROR: cannot create ROOT data chain!?\n", progname);
		exit(1);
	}

        char *pathname = new char[strlen(outdir)+32*ntargets]; 
        FILE **file    = new FILE*[ntargets]; 

	// open all output files, write header line
        for (tgt=0; tgt<ntargets; tgt++) {
		if (target[tgt].id == source.id) {
			continue;       // nothing is send from box to itself :)
		}
		sprintf(pathname, "%s/tt%2.2d.tt%2.2d.csv", outdir,
			source.id, target[tgt].id);

		if ((file[tgt] = fopen(pathname, "w")) == NULL) {

			char* message = new char[strlen(pathname) + 30];
			sprintf(message,"%s: ERROR cannot open %s", progname, pathname);
			perror(message);
			exit(1);
		}

		fprintf(file[tgt], "Src,Dst,Size,Arrival,Delay,SrcClk,TrgClk,Nhops,Route,SrcErr,TrgErr\n");
	}

	Delay *delay = new Delay();
	rootchain->SetBranchAddress("delay", &delay);
	nMeasurements = (Int_t) rootchain->GetEntries();
	if (verbose) {
		cout << "Source Box " << source.id << ": " << nMeasurements
				 << " measurements found" << endl;
	}

	// process all delay measurements, add output to right file

	for (Int_t eventNo=0; eventNo < nMeasurements; eventNo++) {
		if (rootchain->GetEvent(eventNo) < 0) {
			// problem reading this "event"?
			continue;
		}
		if (delay ->GetSourceId() == (Int_t) source.id) {
			nProcessed++;
			if (((targetId = delay->GetTargetId()) > (int)MaxBoxId)
			        || (targetId < 1) ) {
				// Not part of the list of targets, or
				// invalid targetId; silently skip 
				continue;
			}
			if ((index = id2index[targetId]) < 0) {
				// Not part of the list of targets
				// silently skip 
				continue;
			}

		        // packetId by convention equals time packet was send

		        Double_t packettime = delay->GetPacketTime(starttime);
		        if ((packettime < starttime) || (packettime > endtime)) {
		                // outside selected time interval, silently skip
		                continue;
		        }


			fprintf (file[index], "%d,%d,%d,%f,%f,%x,%x,%d,%d,%f,%f\n", 
					 delay->GetSourceId(),	
					 delay->GetTargetId(), 
					 delay->GetPacketSize(), 
					 delay->GetArrivalTime(), 
					 delay->GetPacketDelay(),
					 delay->GetSourceClock(),
					 delay->GetTargetClock(),
					 delay->GetNhops(),
					 delay->GetRouteId(),
					 delay->GetSourceNtp(),
					 delay->GetTargetNtp());

			nAccepted++; 
		}
	}

	// close all output files
       	for (tgt=0; tgt<ntargets; tgt++) {
		if (target[tgt].id == source.id) {
			continue;   
		}
	        fclose(file[tgt]);
	}
	
	if (verbose) {
		cout << "Source Box " << source.id << ": " << nProcessed
					<< " measurements processed" << endl;
		cout << "Source Box " << source.id << ": " << nAccepted
					<< " measurements accepted " << endl;
	}

	// delete dynamically allocated objects (avoid memory leaks)

        delete[] pathname;
        delete[] file;    
}
void DelayPlots::Fill (TestBox src, TestBox **dest, int ndest, time_t period,
		time_t end, double mndelay, double mxdelay, short allpckts) {
	

	// Initialize histograms: clear old ones or create new ones

	mindelay = mndelay;
	maxdelay = mxdelay;
	allpackets = allpckts;

	time_t newstart = end - period;

	if ((newstart == starttime) && (end == endtime) && (ndest == ntargets)) {
		// clear existing histograms
		ClearHistos();

		// (re)set source & destination
		SetSourceTarget(src, dest);
	}
	else {
		// delete old histograms (if any) and book new ones
		DeleteHistos(); 

		endtime = end;
		tm *timestruct = gmtime(&endtime);
		strftime(cendtime1, 20, "%Y-%m-%d %H:%M", timestruct);
		strftime(cendtime2, 8, "%b %d", timestruct);
		starttime = newstart;
		timestruct = gmtime(&starttime);
		strftime(cstarttime1, 20, "%Y-%m-%d %H:%M", timestruct);
		strftime(cstarttime2, 8, "%b %d", timestruct);
		ntargets = ndest;
		target = new TestBox[ntargets];
		SetSourceTarget(src, dest);
		CreateHistos();
	}

	// chain data

	if (datachain != NULL) {
		delete datachain;	// dispose of old data chain
	}

	datachain = new	TTMDataChain(source, endtime, period, PATH, TYPE);
			
	// now loop over data and fill histograms

	rootchain = datachain->getRootChain();
	if (rootchain == NULL) {
		fprintf(stderr, "%s: ERROR: cannot create ROOT data chain!?\n",
			progname);
		exit(1);
	}

	Delay *delay = new Delay();
	rootchain->SetBranchAddress("delay", &delay);
	nMeasurements = (Int_t) rootchain->GetEntries();

	Int_t nProcessed = 0;
	for (Int_t eventNo=0; eventNo < nMeasurements; eventNo++) {
		if (rootchain->GetEvent(eventNo) < 0) {
			// problem reading this "event"?
			continue;
		}
		if (delay->GetSourceId() == (Int_t) source.id) {
			processDelay(delay);
			nProcessed++;
		}
	}
	if (verbose) {
		cout << "Source Box " << source.id << ": " << nProcessed
		     << " measurements processed" << endl;
	}

}

void DelayPlots::processDelay(Delay *delay) {
	// process one delay measurement: fill histograms and percentiles
	
	int nhops, routeid;
	int targetId, index;

	if (((targetId = delay->GetTargetId()) > (int)MaxBoxId) || (targetId < 1) ) {
		// Not part of the list of targets, or invalid targetId;
		// silently skip 
		return;
	}

	if ((index = id2index[targetId]) < 0) {
		// Not part of the list of targets, silently skip 
		return;
	}
	
	// packetId by convention equals time packet was send

	Double_t packettime = delay->GetPacketTime(starttime);
	if ((packettime < starttime) || (packettime > endtime)) {
		// outside selected time interval, silently skip
		return;
	}

	// transform UNIX timestamp into ROOT timestamp
	// (i.e. seconds since 1/1/1995 instead of seconds since 1/1/1970)

	packettime -= ROOT_TIMEZERO;

	// Fill histograms

	PacketsSent[index]->Fill(packettime);

	// number of hops
	if ((routeid=delay->GetRouteId()) > 0) {
 		nhops=delay->GetNhops();
		RoutesNHops[index]->Fill(packettime, (Float_t) 10.0 * nhops);
		RoutesInfo[index]->Update(routeid, nhops) ;
	}


	PacketStatus status = delay->Status();
	
	if (status == ClockValid) {
		Float_t packetdelay = delay->GetPacketDelay();
		Delay2D[index]->Fill(packettime, packetdelay);
		Delay1D[index]->Fill(packetdelay);
		ClocksOK[index]->Fill(packettime);

		// in future perhaps: Add(value, interval);
		// where  interval=(packettime mod delta)

		DelayPercentile[index]->Add(packetdelay);

	}
	else if (status == PacketLost) {
		PacketsLost[index]->Fill(packettime);
	}
	else {
		if (allpackets) {
			Float_t packetdelay = delay->GetPacketDelay();
			Delay2D[index]->Fill(packettime, packetdelay);
			Delay1D[index]->Fill(packetdelay);
		}

		if (status == ClockSrcValid) {
			// source ok, target wrong
			BadTargetClock[index]->Fill(packettime);
		}
		else if (status == ClockTrgValid) {
			// target ok, source wrong
			BadSourceClock[index]->Fill(packettime);
		}
		else if (status == ClockInvalid) {
			// both clocks wrong
			BadClocks[index]->Fill(packettime);
		}
		else {
			cout << "ERROR: unknown packet status " << status
			     << "Source Id "  << delay->GetSourceId()
			     << " Target Id " << delay->GetTargetId() << endl;
		}
	}

}

void DelayPlots::Plot(plotformat fmt, char *outdir, short lgscale) {

	// make plots for all targets
	// files are named <outdir>/tt<src>tt<dst>.<type>
	// <src> = source id, <dst> = target_id, 
	// <type> = ps, eps, gif

	// I'd prefer completely dynamic allocation, but
	// for now 32 bytes should be enough for filename part
	
	logscale = lgscale;
	TPostScript *psout;
	char *pathnames = new char[strlen(outdir)+32*ntargets]; 
	char *cmdbuffer = new char[strlen(outdir)+strlen(PS2GIF)+32*ntargets]; 


	// force thin lines in PS output to avoid cluttering histograms
	gStyle->SetLineScalePS(0.2);


	
	for (int tgt=0; tgt<ntargets; tgt++) {
		if (target[tgt].id == source.id) {
			continue;	// nothing is send from box to itself :)
		}

		TCanvas *canvas = new TCanvas("canvas", "Plots", 0, 0, 1270, 990);
		if (fmt == eps) {
			sprintf(pathnames, "%s/tt%2.2d.tt%2.2d.eps", outdir,
				source.id, target[tgt].id);

			// open eps file - type 113 in ROOT
			psout = new TPostScript(pathnames, 113);
		}
		else {
			sprintf(pathnames, "%s/tt%2.2d.tt%2.2d.ps", outdir,
				source.id, target[tgt].id);
			// open ps file  - type 112 = landscape
			psout = new TPostScript(pathnames, 112);
			psout->Range(25.5, 18.8);   // centre on A4 size paper
		}
		PlotHistos(tgt, target[tgt].id);

  		// now update canvas (needed to write to PS file?)
	  	canvas->Update();

		psout->Close();
		delete psout;
		delete   canvas;
	}

	if (fmt == gif) {
		// now convert them to gif; create ps2gif command line which
		// specificies all .ps files that need converting.
		//
		// BoundingBox parameters are taken from a manual run of the
		// bbfig tool; will be fine as long as ROOT PostScript prolog
		// does not change. 
		//
		// rotate to get proper orientation
		// scale up a bit to make text more legible
		//

		char *newname = pathnames;
		for (int tgt=0; tgt<ntargets; tgt++) {
			sprintf(newname, " tt%2.2d.tt%2.2d.ps", source.id, target[tgt].id);
			newname += strlen(newname);
		}

		sprintf(cmdbuffer, "cd %s;%s %s", outdir, PS2GIF, pathnames);
		if (verbose) {
			cerr << "executing " << cmdbuffer << endl;
		}

		if (system(cmdbuffer) != 0) {
			cerr << "ERROR converting to gif: " << pathnames <<endl;
		}
		else {
			// conversion OK -> cleanup ps (they can be real large)
			sprintf(cmdbuffer, "cd %s;%s %s", outdir, "/bin/rm ", pathnames);
			if (verbose) {
				cerr << "executing " << cmdbuffer << endl;
			}

			if (system(cmdbuffer) != 0) {
				cerr << "ERROR removing ps files? " <<
				pathnames <<endl;
			}
		}
	}

	// delete dynamically allocated objects (avoid memory leaks)

	delete[] pathnames;
	delete[] cmdbuffer;
}
		
void DelayPlots::PlotHistos(int index, int targetId) {
	
	char buf[175];
	Int_t bin;

	// create pads
	TPad *pad1 = new TPad("pad1", "This is pad 1",0.01,0.49,0.43,0.95);
	TPad *pad2 = new TPad("pad2", "This is pad 2",0.44,0.49,0.86,0.95);
	TPad *pad3 = new TPad("pad3", "This is pad 3",0.01,0.02,0.43,0.48);
	TPad *pad4 = new TPad("pad4", "This is pad 4",0.44,0.02,0.86,0.48);
	TPad *pad5 = new TPad("pad5", "This is pad 5",0.87,0.02,0.99,0.95);

	// force them to be visible on the canvas
	pad1->Draw();
	pad2->Draw();
	pad3->Draw();
	pad4->Draw();
	pad5->Draw();

	// Top Centre Label
	sprintf(buf, "               Delays from tt%2.2d to tt%2.2d.  Start: %s  End: %s UTC",
		source.id, targetId, cstarttime1, cendtime1);
	TPaveLabel *pl = new TPaveLabel(0.274,0.956,0.748,0.998,buf,"br");
	pl->SetTextSize(0.45);
	pl->Draw();
	
	// Top left
	pad1->cd();
	pad1->SetGridx();
	pad1->SetGridy();
	if (logscale) {
		pad1->SetLogy();
	}
	RoutesNHops[index]->Draw();
	Delay2D[index]->Draw("SAME");
	
	// Top Right
	pad2->cd();
	pad2->SetGridx();
	pad2->SetGridy();
	if (logscale) {
		pad2->SetLogx();
	}
	Delay1D[index]->Draw();
	
	// Bottom Left
	pad3->cd();
	pad3->SetGridx();
	pad3->SetGridy();

	// capture satistics *before* adding up Histograms!
	int validpackets = (int) ClocksOK[index]->GetEntries();
	int sourceok = (int) BadTargetClock[index]->GetEntries();
	int targetok = (int) BadSourceClock[index]->GetEntries();
	int badclocks = (int) BadClocks[index]->GetEntries();
	int lostpackets = (int) PacketsLost[index]->GetEntries();

	// add histograms to create stacked plots (1 bin = multiple data items)

	BadSourceClock[index]->Add(BadSourceClock[index], ClocksOK[index], 1, 1);
	BadTargetClock[index]->Add(BadTargetClock[index], BadSourceClock[index], 1, 1);
	BadClocks[index]->Add(BadClocks[index], BadTargetClock[index], 1, 1);


	// PacketsSent histo is the background, everything not obscured 
	// by valid/invalid clock histograms indicates packet loss.

	PacketsSent[index]->Draw();
	BadClocks[index]->Draw("SAME");
	BadTargetClock[index]->Draw("SAME");
	BadSourceClock[index]->Draw("SAME");
	ClocksOK[index]->Draw("SAME");

	// Bottom Right
	pad4->cd();
	pad4->SetGridx();
	pad4->SetGridy();

	Arrived->Reset();
	Arrived->Add(PacketsSent[index], PacketsLost[index], 1, -1);
	Arrived->Divide(Arrived, PacketsSent[index], 1, 1);
	Arrived->Draw();

	// Statistics
	pad5->cd();

	// 

	// percentiles for packet loss (using fraction arrived per bin)
	LossPercentile->Reset();
	for(bin=1; bin<=binsX; bin++) {
		LossPercentile->Add(float(1-Arrived->GetBinContent(bin)));
	}

	// write output with TLatex-class in new root-version  
	strcpy(buf, "STATISTICS:");
	Float_t x = 0.5;       // Starting point
	Float_t y = 0.95;
	Int_t align = 22;      // horizontally and vertically centered
	Font_t font = 20;     // Times-Roman-bold-r
	write_stats(pad5, buf, x, y, align, font);

	y -=0.03;
//	write_stats(pad5, "Histogram", x, y, align, font);
//	y -=0.02;
	write_stats(pad5, "Delay @& Hops:", x, y, align, font);

	font = 10;	// Times-Romand-medium-i
	y -=0.025;
	sprintf(buf,     "Entries: %g", Delay1D[index]->GetEntries());
	write_stats(pad5, buf, x, y, align, font);
	y -=0.02;
	sprintf(buf,     "Overflow: %g", Delay1D[index]->GetBinContent(binsY+1) );
	write_stats(pad5, buf, x, y, align, font);
	y -=0.02;
	sprintf(buf,     "Underflow: %g", Delay1D[index]->GetBinContent(0) );
	write_stats(pad5, buf, x, y, align, font);

	align = 32;
	Float_t x1 = 0.52;
	Float_t x2 = 0.95;

	y -=0.025;
	write_stats(pad5, "2.5 Perc:", x1, y, align, font);
	sprintf(buf,     "%8.1fms", DelayPercentile[index]->GetLevel(2.5));
	write_stats(pad5, buf, x2, y, align, font);

	y -=0.02;
	write_stats(pad5, "Median:", x1, y, align, font);
	sprintf(buf,     "%8.1fms", DelayPercentile[index]->GetLevel(50.0));
	write_stats(pad5, buf, x2, y, align, font);

	y -=0.02;
	write_stats(pad5, "97.5 Perc:", x1, y, align, font);
	sprintf(buf,     "%8.1fms", DelayPercentile[index]->GetLevel(97.5));
	write_stats(pad5, buf, x2, y, align, font);

	y -=0.02;
	write_stats(pad5,"Mean:", x1, y, align, font);
	sprintf(buf,     "%8.1fms", Delay1D[index]->GetMean());
	write_stats(pad5, buf, x2, y, align, font);
	y -=0.02;
	write_stats(pad5,"RMS:", x1, y, align, font);
	sprintf(buf,     "%8.1fms", Delay1D[index]->GetRMS());
	write_stats(pad5, buf, x2, y, align, font);

	align=22;
	y -=0.025;
	sprintf(buf,     "Min. hops: %d", RoutesInfo[index]->GetMinHops());
	write_stats(pad5, buf, x, y, align, font);
	y -=0.02;
	sprintf(buf,     "Max. hops: %d", RoutesInfo[index]->GetMaxHops());
	write_stats(pad5, buf, x, y, align, font);
	
	y -=0.03;
	TLine line1(0.0,0.0,0.0,0.0);
	line1.SetLineWidth(2);
	line1.SetLineColor(4);
	line1.DrawLine(0.05, y, 0.95, y); 

	y -=0.04;
	font = 20;     // Times-Roman-bold-r
	write_stats(pad5, "Packets sent/valid:", x, y, align, font);
	y -=0.025;
	
	font = 10;	// Times-Romand-medium-i
	int totalsent = (int) PacketsSent[index]->GetEntries();
	sprintf( buf,     "Total: %i", totalsent);
	write_stats(pad5, buf, x, y, align, font);
	y -=0.02;

	float percent =  validpackets * 100.0 / totalsent;
	sprintf( buf,     "Valid: %i = %.3g %%", validpackets, percent);
	write_stats(pad5, buf, x, y, align, font);

	y -=0.02;
	percent = targetok * 100.0 / totalsent;
	sprintf( buf,     "Send bad: %i = %.2g %%", targetok, percent);
	write_stats(pad5, buf, x, y, align, font);

	y -=0.02;
	percent = sourceok * 100.0 / totalsent;
	sprintf( buf,     "Recv bad: %i = %.2g %%", sourceok, percent);
	write_stats(pad5, buf, x, y, align, font);

	y -=0.02;
	percent = badclocks * 100.0 / totalsent;
	sprintf( buf,     "2 Clocks bad: %i = %.2g %%", badclocks, percent);
	write_stats(pad5, buf, x, y, align, font);

	y -=0.02;
	percent = lostpackets * 100.0 / totalsent;
	sprintf( buf,     "Lost: %i = %.2g %%", lostpackets, percent);
	write_stats(pad5, buf, x, y, align, font);
	
	y -=0.03;
	line1.SetLineColor(2);
	line1.DrawLine(0.05, y, 0.95, y);  
	y -=0.04;
	
	font = 20;     // Times-Roman-bold-r
	write_stats(pad5,"Packets lost:", x, y, align, font);

	font = 10;	// Times-Romand-medium-i
	align = 32;	// right justified
	x1 += 0.03;
	x2 -= 0.03;

	y -=0.025;
	write_stats(pad5, "2.5 Perc:", x1, y, align, font);
	sprintf(buf,     "%5.1f%%", 100 * LossPercentile->GetLevel(2.5));
	write_stats(pad5, buf, x2, y, align, font);
	y -=0.02;
	write_stats(pad5, "Median:", x1, y, align, font);
	sprintf(buf,     "%5.1f%%", 100 * LossPercentile->GetLevel(50.0));
	write_stats(pad5, buf, x2, y, align, font);
	y -=0.02;
	write_stats(pad5, "97.5 Perc:", x1, y, align, font);
  	sprintf(buf,     "%5.1f%%", 100 * LossPercentile->GetLevel(97.5));
	write_stats(pad5, buf, x2, y, align, font);

	
	// estimate of sending process uptime
	int count = 0;

	for (bin = 1; bin <= binsX; bin++) {
		if (PacketsSent[index]->GetBinContent(bin) > 0) {
			count++;
		}
	}
	float uptime = count*100/(float)binsX;

	y -=0.025;
	align = 22;	// centered
	sprintf(buf,     "Uptime: %.3g %%", uptime);
	write_stats(pad5, buf, x, y, align, font);
	
	y -=0.03;
	line1.SetLineColor(3);
	line1.DrawLine(0.05, y, 0.95, y); 
	
	y -=0.04;
	font = 20;     // Times-Roman-bold-r
	write_stats(pad5, "Over-all statistic:", x, y, align, font);

	font = 10;	// Times-Romand-medium-i
	y -=0.01;  
//	y -=0.02;  
//	write_stats(pad5, "Number of measurements", x, y, align, font);
//	y -=0.02;
//	sprintf( buf,     "in period: %i", nMeasurements);
//	write_stats(pad5, buf, x, y, align, font);

	y -=0.02;
	Float_t days = ((endtime-starttime)/86400);
	if (days > 1) {
		sprintf( buf,     "Time period: %.2g days", days);
	}
	else {
		sprintf( buf,     "Time period: %.2g day",  days);
	}
	write_stats(pad5, buf, x, y, align, font);
	y -=0.035;
	write_stats(pad5, "Number of routing", x, y, align, font);
	y -=0.02;
	sprintf( buf,     "vectors: %i", RoutesInfo[index]->GetEntries());
	write_stats(pad5, buf, x, y, align, font);
	y -=0.02;
	sprintf( buf,     "flaps: %i", RoutesInfo[index]->GetNumFlaps());
	write_stats(pad5, buf, x, y, align, font);
	y -=0.035;
	sprintf( buf,     "Number of bins: %d", binsX );
	write_stats(pad5, buf, x, y, align, font);
	y -=0.02;
	sprintf( buf,     "Minutes/bin: %.3g", ((endtime-starttime)/binsX/60.0));
	write_stats(pad5, buf, x, y, align, font);
  
  
  }
  
  

void DelayPlots::CreateHistos() {
	char	buf[100];     // small buffer for character strings
        char    xtitle[100];  // buffer for x-axis title
	char    *timeformat;  // indicates how time on axis is formatted

	Delay2D		= new TH2F*[ntargets];
	Delay1D		= new TH1F*[ntargets];
	PacketsSent	= new TH1F*[ntargets];
	ClocksOK	= new TH1F*[ntargets];
	BadSourceClock	= new TH1F*[ntargets];
	BadTargetClock	= new TH1F*[ntargets];
	BadClocks	= new TH1F*[ntargets];
	RoutesNHops	= new TH2F*[ntargets];
	PacketsLost	= new TH1F*[ntargets];
	
	RoutesInfo 	= new RoutingInfo*[ntargets];
	DelayPercentile = new Percentiles*[ntargets];

	binsY = 250; 

	// Some initialisation based on time period

	int ndiv = 0;
	int ndays = (endtime - starttime) / 3600 / 24;
	if (ndays>3)  {
		timeformat = "%b %d";      // <month> <day> 
		strcpy(xtitle, "Time [month day]");
		if (ndays>7) {
			ndiv = -506;	  // 6 major divisions
		}
	}
	else {
		timeformat = "%H:%M";	 //  <hour>:<minute>
		sprintf(xtitle, "      %s%26cTime [hour:minute]%26c%s", cstarttime2,
			' ', ' ', cendtime2);
	}

	// transform UNIX timestamps into ROOT timestamps
	// (seconds since 1/1/1995 instead of seconds since 1/1/1970)

	time_t endtime_root = endtime - ROOT_TIMEZERO;
	time_t starttime_root = starttime - ROOT_TIMEZERO;

	for (int i=0; i<ntargets; i++) {

		RoutesInfo[i]      = new RoutingInfo;
		DelayPercentile[i] = new Percentiles;

		int id = target[i].id;

		// h1
		sprintf(buf, "Delay2D[%d]", id); 
		Delay2D[i] = new TH2F(buf, "delay vs time", binsX*2,
			starttime_root, endtime_root,
			binsY*2, mindelay, maxdelay);

		// Histogram of delay values
		// Note the size binsY is also used in Plot() method!
		sprintf(buf, "Delay1D[%d]", id);
		Delay1D[i] = new TH1F(buf, "PacketDelay", binsY,
			mindelay, maxdelay);

		// h3
		sprintf(buf, "PacketsSent[%d]", id);
	 	PacketsSent[i] = new TH1F(buf, "Packets sent/valid", binsX, 
			starttime_root, endtime_root);

		// h4
		sprintf(buf, "ClocksOK[%d]", id);
	 	ClocksOK[i] = new TH1F(buf, "Clocks OK", binsX, 
			starttime_root, endtime_root);

		// h7
		sprintf(buf, "BadSourceClock[%d]", id);
		BadSourceClock[i] = new TH1F(buf, "Bad Source Clock", binsX,
			starttime_root, endtime_root);

		// h6
		sprintf(buf, "BadTargetClock[%d]", id);
		BadTargetClock[i] = new TH1F(buf, "Bad Target Clock", binsX,
			starttime_root, endtime_root);

		// h5
		sprintf(buf, "BadClocks[%d]", id);
		BadClocks[i] = new TH1F(buf, "Bad Clocks", binsX,
			starttime_root, endtime_root);

		// hops
		sprintf(buf, "RoutesNHops[%d]", id);
  		RoutesNHops[i] = new TH2F(buf, "PacketDelay, Number of hops*10",
			binsX*2, starttime_root, endtime_root,
			binsY*2, mindelay, maxdelay);

		// lost
		sprintf(buf, "PacketsLost[%d]", id);
  		PacketsLost[i] = new TH1F(buf, "Packets arrived/lost", binsX,
			starttime_root, endtime_root);   


		// Now Set various histograms attributes (persistent over reset)

		Delay1D[i]->SetFillColor(5); // yellow
		Delay1D[i]->SetXTitle("Delay [msec]");
		Delay1D[i]->SetYTitle("# packets [Entries/bin]");

		// Force division of "delay" scale in 1D and 2D histograms
		// ROOT 3.0 default behaviour is not good

		Delay1D[i]->SetNdivisions(-505, "x");
		RoutesNHops[i]->SetNdivisions(-505, "y");

		RoutesNHops[i]->SetMarkerSize(2);
		RoutesNHops[i]->SetMarkerColor(2);
		RoutesNHops[i]->SetXTitle(xtitle);
		RoutesNHops[i]->SetYTitle("Delay [msec]");
		if (ndiv != 0) {
			RoutesNHops[i]->SetNdivisions(ndiv,"x"); 
		}
		RoutesNHops[i]->GetXaxis()->SetTimeDisplay(1);
		RoutesNHops[i]->GetXaxis()->SetTimeFormat(timeformat);

		PacketsSent[i]->SetFillColor(7); // cyan
		PacketsSent[i]->SetLineColor(1); // black
		PacketsSent[i]->SetXTitle(xtitle);
		PacketsSent[i]->SetYTitle("# packets [Entries/bin]");
		if (ndiv != 0) {
			PacketsSent[i]->SetNdivisions(ndiv,"x"); 
		}
		PacketsSent[i]->GetXaxis()->SetTimeDisplay(1);
		PacketsSent[i]->GetXaxis()->SetTimeFormat(timeformat);

		BadClocks[i]->SetFillColor(2); // red
		BadClocks[i]->SetLineColor(1); // black

		BadTargetClock[i]->SetFillColor(5); // yellow
		BadTargetClock[i]->SetLineColor(1); // black

		BadSourceClock[i]->SetFillColor(6);  // magenta
		BadSourceClock[i]->SetLineColor(1);  // black

		ClocksOK[i]->SetFillColor(3); // green
		ClocksOK[i]->SetLineColor(1); // black

	}
	// finally one temporary histogram, reused for every source-target plot
	Arrived = new TH1F("arrrived", "Packets arrived/lost", binsX,
			starttime_root, endtime_root);   

	Arrived->SetXTitle(xtitle);
	Arrived->SetYTitle("fraction arrived");
	Arrived->SetFillColor(3);      // green
	if (ndiv != 0) {
		Arrived->SetNdivisions(ndiv,"x"); 
	}
	Arrived->GetXaxis()->SetTimeDisplay(1);
	Arrived->GetXaxis()->SetTimeFormat(timeformat);
}

void DelayPlots::ClearHistos () {
	// Reset all histograms and percentiles

	for (int i=0; i<ntargets; i++) {

		Delay2D[i]->Reset();
		Delay1D[i]->Reset();
		PacketsSent[i]->Reset();
		ClocksOK[i]->Reset();
		BadSourceClock[i]->Reset();
		BadTargetClock[i]->Reset();
		BadClocks[i]->Reset();
		RoutesNHops[i]->Reset();
		PacketsLost[i]->Reset();

		RoutesInfo[i]->Reset();
		DelayPercentile[i]->Reset();	
	}
}

void DelayPlots::DeleteHistos () {
	// delete allocated histograms, reset pointers to NULL, scalars to 0

	if (ntargets != 0) {
		// if ntargets==0, no histograms exist
		for (int i=0; i<ntargets; i++) {
			delete Delay2D[i];
			delete Delay1D[i];
			delete PacketsSent[i];
			delete ClocksOK[i];
			delete BadSourceClock[i];
			delete BadTargetClock[i];
			delete BadClocks[i];
			delete RoutesNHops[i];
			delete PacketsLost[i];
	
			delete RoutesInfo[i];
			delete DelayPercentile[i];	
		}

		delete[] target;
		delete[] Delay2D;
		delete[] Delay1D;
		delete[] PacketsSent;
		delete[] ClocksOK;
		delete[] BadSourceClock;
		delete[] BadTargetClock;
		delete[] BadClocks;
		delete[] RoutesInfo;
		delete[] RoutesNHops;
		delete[] DelayPercentile;	
		
		delete   Arrived;
		
		ntargets = 0;
	}
}


DelayPlots::DelayPlots() {

	// default construtor
	// initialize everything to zero or NULL

	source.id=0;
	source.name="";

        target=NULL;
        ntargets=0;

	Delay2D		= NULL;
	Delay1D		= NULL;
	PacketsSent	= NULL;
	ClocksOK	= NULL;
	BadSourceClock	= NULL;
	BadTargetClock	= NULL;
	BadClocks	= NULL;
	RoutesNHops	= NULL;
	PacketsLost	= NULL;

	DelayPercentile=NULL;

        Arrived=NULL;

        endtime=0;
	starttime=0;

	cstarttime1=new char[20];
	cstarttime2=new char[8];
	cendtime1=new char[20];
	cendtime2=new char[8];

        rootchain=NULL;
        datachain=NULL;

	// defaults values for histogram parameters
	maxdelay = 250;
	binsX = 168; 	 // week plot: 1 bin per 60 minutes

	LossPercentile = new Percentiles;
	gStyle->SetOptStat(0);      // turn off statistics box
}

//********************************************************/
//  function name: write_stats
//  arguments:     *pad        -> pointer to current pad
//                 *ptr_text   -> pointer to string
//                 x           -> x-coordinate NTC
//                 y           -> y-coordinate NTC
//                 align       -> alignment (see ROOT TattText class)
//		   font	       -> font id   (see ROOT TattText class)
//  purpose:       writes 'ptr_text' on 'pad' as TText on
//                 coordinates 'x' and 'y' with alignment
//                 'align'. Use TLatex-class for a better
//                 correspondence between screen and print-out
//                 in newer versions of ROOT (above 2.21/08)
//  Author:	   Johann Gutauer
/**************************************************************/

void DelayPlots::write_stats(TPad* pad, const char* ptr_text, const Float_t x,
		const Float_t y, const Short_t align, Font_t font)
{
  pad->cd();
  TText *xlabel = new TText(x, y, ptr_text);
  xlabel -> SetTextFont(font);
  xlabel -> SetTextColor(1);
  xlabel -> SetTextSize(0.11);
  xlabel -> SetTextAlign(align);
  xlabel->Draw();
  return;
}

