/*
Copyright (c) 2001                      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          : ipdv.C
Author            : Reinhard Sojka
Date              : 05-NOV-2001
Description       : Program for creating TTM IPDV ("jitter") plots
Language Version  : C++
OSs Tested        : Solaris 2.6
Command line options:

    --help  get an overview of the options 
    -6      IP version 6 data
    -s 	    source box no.
    -p	    start time and end time, format is YYYYMMDD/YYYYMMDD, 
      	    any ISO 8601 format is ok, like YYYYMMDDThh:mm:ss or similar 

    -t	    target box no., -1 for all connected boxes, optional, default = -1

    -d	    date in file name, 'on' or 'off', optional, 
            default = 'off' (ipdv.tt_A.tt_B.start.end.*)
    -e	    enable error plot, 'on' or 'off', optional
 	    (default = 'off')
    -f	    output file format, 'gif',  'ps', 'pdf' or 'eps', optional
            (default = 'gif')
    -m      maximum delay to be displayed on histograms, optional
	    (default = 800 ms)
    -z      minimum delay to be displayed on histograms, optional
	    (default = 0 ms)
    -u      maximum IPDV to be displayed on histograms, optional
	    (default = 400 ms)
    -o	    output directory for plots, optional
            (default is current directory)
    -h	    create a HTML file for the webpage, 'on' or 'off', optional
            (default = 'off')
    -r	    write percentiles to a file, 'on' or 'off', optional
            (default = 'off')
    -v      verbose, 'on' or 'off', optional, default = 'off' 
  
Notes/Description:

	program to read the data of a time interval betweeen some minutes and
	several days in different lists, filtered by testboxnumber

	sort the data by arrival time and calculata IPDV for the (existing)
	testboxes in file; calculates also the experimental error and draws
	an error plot>

	plots will be stored as *.eps file in current folder 
	normalized delay histograms, added statistic pad 
	autozoom for delay distribution and IPDV distribution
	added percentiles for statistics pad 
	output as gif or eps, options/switches made conform with delay
	calculation 

	changed time input, changed handling of time formats and creation
	of file names now with the correct time in the histograms
        (Root has its own time stamp, starting 1995)
	percentiles can be writtten to a file 
	added the possibility to customize the plots 

external programs:   ps2gif to convert eps into gif format


$Id: ipdv.C,v 1.8 2003/06/02 10:42:19 ruben Exp $
-------------------------------------------------------------------------------
*/

static char const rcsid[] = "$Id: ipdv.C,v 1.8 2003/06/02 10:42:19 ruben Exp $";
static char const *rcsid_p = rcsid;   // Prevent g++ from optimizing out rcsid

#define TTCONFIG_COMMAND  "/ncc/ttpro/bin/ttconfig"
#define TTCONFIG_COMMAND_ARGS "-v active"
//#define DEFAULT_OUTPUTDIR "/ncc/ttpro/spool/plots"

        #include "iostream.h"
        #include "fstream.h"
        #include "stdio.h"
        #include "string.h"
        #include "time.h"
        #include "stdlib.h"
        #include "unistd.h"
	#include <sys/stat.h>


#ifndef __CINT__  
    #include "TROOT.h"
    #include "TApplication.h"
    #include "TCanvas.h"
    #include "TChain.h"
    #include "TTree.h"
    #include "TFile.h"
    #include "TH1.h"
    #include "TH2.h"
    #include "TF1.h" 
    #include "TGraphErrors.h" 
    #include "TPaveLabel.h" 
    #include "TText.h" 
    #include "TLine.h" 
    #include "TStyle.h"
    #include "TPostScript.h" 
#endif

    	#include "Delay.h" 
    	#include "Percentiles.h"	//Rene's class to calculate the percentiles 
	#include "iso8601.h"		//to convert input string into "readable" time  

//create structure to store one set of data in a list 
//maybe a bit oldfashioned, but works fine. creating an own class would make sense
struct ttmData{
	UInt_t     PacketId;        // Identifier of the packet 
        Int_t      SourceId;        // ID# of the sending testbox
//        Int_t      SourcePort;      // Port from which the packet was sent
        Int_t      TargetId;        // ID# of the receiving testbox 
//        Int_t      TargetPort;      // Port to which the packet was sent 
//        Int_t      PacketSize;      // Packet size in bytes 
        Double_t   ArrivalTime;     // -1.0 if undefined 
        Double_t   PacketDelay;     // Delay in ms, -1.0 if undefined 
//        UInt_t     SourceClock;     // Sending clock NTP status    
//        UInt_t     TargetClock;     // Receiving clock NTP status 
        Int_t      Nhops;           // Number of hops, -1 if unknown
//        Int_t      RouteId;         // Routing vector number, -1 if unknown 
        Float_t    SourceNtp;       // Sending clock NTP estimated error
        Float_t    TargetNtp;       // Receiving clock NTP estimated error
	
	ttmData *next; };


// * * *  declaration of subroutines  * * *
void CreateAxisStyle(char* xTitle, char* xTimeFormat, Double_t minXax, Double_t maxXax, Int_t outputFormat);
void CreatePlotTitle(char* titleOfWholePlot, Int_t boxNo, char* SourceBox, Double_t minXax, Double_t maxXax); 
void CreateChar(char* SourceBox, char* startYYYYMMDD, char* endYYYYMMDD, Int_t start_Box, Double_t minXax, Double_t maxXax); 
time_t CreateRootFileName(char* sourceFile, time_t act_TimeStamp, Int_t start_Box);	 
void CreatePlotFileNameDate(char* plotFileName, Int_t boxNo, char* SourceBox, char* startYYYYMMDD, char* endYYYYMMDD, Int_t outputFormat);
void CreatePlotFileNameStd(char* plotFileName, Int_t boxNo, char* SourceBox, Int_t outputFormat);
void WriteStatisticsPad(TPad* pad, Int_t allPackets, Int_t validPackets, Int_t noOfFlaps, Percentiles* ipdvPercentile, Float_t rmsIPDV, Float_t meanDelay, Float_t rmsDelay);
void write_stats(TPad* pad, const char* ptr_text, const Float_t x, const Float_t y, const Short_t align, Font_t font);
void PutDataInElement(Delay* delay, ttmData* element);
void SortList(ttmData* access);
void TerminationMessage();
void CalculateAndFillHistograms(ttmData* access, TH1F* h1, TH2F* h2, TH2F* h3, TH2F* h4, TH1F* h5, TGraphErrors* ipdvErr, Percentiles* ipdvPercentile, Int_t enableErrorPlot);
void SetHistogramProperties(TH1F* h1, TH2F* h2, TH2F* h3, TH2F* h4, TH1F* h5, TH2F* dummy, char* xTitle, char* xTimeFormat);
void DrawHistograms(TH1F* h1, TH2F* h2, TH2F* h3, TH2F* h4, TH1F* h5, TPad* pad1, TPad* pad2, TPad* pad3, TPad* pad4, Int_t customIPDVPlot, Int_t customDelayPlot);
void DrawErrorPlot(TH2F* dummy, TGraphErrors* ipdvErr, TPad* errorPad);
void ExtractInput(int argc, char** argv, Int_t* start_Box, Int_t* end_Box, time_t& endtime, time_t& duration, Int_t* enableErrorPlot, Int_t* outputFormat, Int_t* enableDateInFileName, Int_t* savePercentiles, Int_t* createHTML, Int_t* printVerbose, char* outputDir, Int_t* maxIPDV, Int_t* customIPDVPlot, Int_t* maxCustomDelay, Int_t* minCustomDelay, Int_t*  customDelayPlot);
void SavePercentiles(Percentiles* ipdvPercentile, char* SourceBox, Int_t boxNo, ofstream &IntoFile, Int_t firstLine);
void CreateHTMLFile(Percentiles* ipdvPercentile, char* anyString, Int_t boxNo, ofstream &IntoFile, Int_t paragraphID);
Int_t CheckPlotParameter(Int_t* maxIPDV, Int_t* maxCustomDelay, Int_t* minCustomDelay);


// * * * G L O B A L  V A R I A B L E S * * *
const Int_t MAX = 125;				//number of testboxes
char fn_Path[30] = "/ncc/ttpro/data/root/";	//path to root files, dont't forget "/" at the end  
const time_t convToRootTime = 788918400;	//Root has its own time, starting 1995 - needed for histograms   

char *progname;
TStyle *rootstyle;				//to get access to gStyle

int ipVersion = 4;				// default IPv4 data

// * * * M A I N * * * 
int main(int argc, char **argv)
{

progname = argv[0];			//assigns the name, which is used to start the program, to argv[0]  

// ROOT initialisation
TROOT simple ("TTM", "Plots");
gROOT->SetBatch();		// no graphics windows
TApplication* app = new TApplication("delayPlots", NULL, NULL, NULL, 0);

// ensure correct timezone for date<->time_t conversions
putenv("TZ=GMT+0");

//set default values and get the input data  
Int_t start_Box = 1; Int_t end_Box = -1;	//source and target box

// XXX duration is wrong interpreted here
time_t endtime; 
time_t duration;  

// FIXME Should be a plottype
Int_t outputFormat = 0; 			//standard output format is gif - gif=0, ps=1, eps=2, pdf=3    

// XXX Are they all used
Int_t enableErrorPlot = 0;			//error plot is disabled by default  
Int_t savePercentiles = 0;			//write percentiles to file, disabled by default  
Int_t enableDateInFileName = 0;			//output file name, default = ipdv.tt_A.tt_B.*  
Int_t createHTML = 0;				//create a HTML fiel to link the plots from the web site  
Int_t printVerbose  = 0;			//verbose  
Int_t maxIPDV = 400; 				//by default is IPDV vs time histograms from -400 to 400 ms
Int_t customIPDVPlot = 0;			//make the IPDV plots with specified parameters, default autozoom
Int_t maxCustomDelay = 800;			//by default the maximum for the delays histograms is 800 ms
Int_t minCustomDelay = 0;			//by default the delays histogram start at 0 ms 
Int_t customDelayPlot = 0;			//make the delay plots with specified parameters, default is autozoom  
char outputDir[50]; strcpy(outputDir, "");	//directory to write output files, default is current directory  

//call subroutine
// XXX Should use class TTMOptions!!!
ExtractInput(argc, argv, &start_Box, &end_Box, endtime, duration, &enableErrorPlot, &outputFormat, &enableDateInFileName, &savePercentiles, &createHTML, &printVerbose, outputDir, &maxIPDV, &customIPDVPlot, &maxCustomDelay, &minCustomDelay, &customDelayPlot);	//call subroutine 

//make sure that the parameter for custom plots make sense
CheckPlotParameter(&maxIPDV, &maxCustomDelay, &minCustomDelay);	//call subroutine

//create pointers - lists are organized in an array 
ttmData arrayOfLists[MAX];	//the only known element of a list 
ttmData *access = arrayOfLists;	//use pointers to access the lists
ttmData *last = NULL;		//last element of list 

//create arrays
Int_t allPackets[MAX];		//for statistics
Int_t validPackets[MAX];
Int_t boxNo;			//contains the actual testbox

Double_t routingVectorId[MAX];	//will store the actual routing vector
//Int_t noOfVectors[MAX];		//how many vectors per List
Int_t noOfFlaps[MAX];		//how often changed the routing vector
Percentiles thePercentiles[MAX];	//will contain (and calculate) the percentiles of the IPDV distribution
Percentiles *ipdvPercentile = thePercentiles;	//use pointers to access the percentiles 

//initialize pointers and arrays 
for(boxNo = 0; boxNo < MAX; boxNo++)
{ 
	(access + boxNo)->next = last;	//set access to end of list 
	allPackets[boxNo] = 0;
	validPackets[boxNo] = 0;

	routingVectorId[boxNo] = 0.0;	
	noOfFlaps[boxNo] = -1;		//will be incremented to 0 when first routing vector is assigned(min. 1 vector) 
	(ipdvPercentile + boxNo)->Reset();	//initialize content
}

//create a chain of files and read first file 
if(printVerbose > 0)						//info - if you want to see it
	cout << endl << "loading file(s)..." << endl;		//just for info
char sourceFile[250];				//file name, will be added to chain
TChain chain("tree");	

//add one or more files to chain, if necessary
time_t  act_TimeStamp = endtime - duration;  
do
{
	act_TimeStamp =  CreateRootFileName(sourceFile, act_TimeStamp, start_Box);    

        struct stat statbuf;
        if (stat(sourceFile, &statbuf) < 0) {
                perror(sourceFile);
        }
        else {
                chain.Add(sourceFile);
        }

	if(printVerbose > 0)			//info - if you want to see it
		cout << sourceFile << endl;	//debug - just for info
}while(act_TimeStamp <= (endtime));	

// XXX should round endtime to end of day

//create a delay object to store data per event (a whole set of data) 
Delay *delay = new Delay(); 
chain.SetBranchAddress("delay", &delay);

//get number of lines(=events) in source file
Int_t nTotalEvents = chain.GetEntries() - 1;

//to get range of x axis for histograms - calculate it from start parameters in a subroutine 
Double_t minXax = (Double_t) endtime - duration;
Double_t maxXax = (Double_t) endtime; 

Int_t readPackets = 0;				//debug 

//find selected packets and store them in the list
//read in starts from the end to get a list which starts with the first line 

if(printVerbose > 0)						//info - if you want to see it
	cout << endl << "putting data in list(s)..." << endl;	//just for info
for (Int_t eventNo = nTotalEvents; eventNo > (-1); eventNo--)
{
        chain.GetEvent(eventNo);		//read a set of data 

	//check if there is enough space in the arrays
        if(delay->GetTargetId() >= MAX) //too much testboxes
        {                               //maybe importaint in case of further use 
		TerminationMessage();		// call subroutine to display message
                return(0);			//end program   
        }
	
	//select packets
        if( (delay->GetArrivalTime() < maxXax) && (delay->GetArrivalTime() > minXax) )
        {

	        allPackets[delay->GetTargetId()]++;             //increment counter
	       
		if(delay->Status() == ClockValid )          //only valid packets 
        	{       
	                //select packets, create element and put it in (correct) list   
        	        if(end_Box > 0) //only one box selected
                	{
				if(end_Box == delay->GetTargetId() )
	                        {
				        ttmData *element = new ttmData;
					PutDataInElement(delay, element);	//call subroutine to fill element
			
					//put element in list
					element->next = (access + (element->TargetId))->next;
                                	(access + (element->TargetId))->next = element; //new starting point
	                                readPackets++;                  //counter - debug
        	                        validPackets[element->TargetId]++;     //increase counter for valid packets 
                	        }
	                }
			else            //extract data from all target boxes in chain 
                	{
			        ttmData *element = new ttmData;
				PutDataInElement(delay, element);	//call subroutine to copy data in element
                           
				//put element in list
				element->next = (access + (element->TargetId))->next;
                        	(access + (element->TargetId))->next = element; //new starting point
	                        readPackets++;                  //counter - debug
        	                validPackets[element->TargetId]++;     //increase counter for valid packets 
			}

                        //find out how often the route was switched - maybe a bit uneffective here
                        if(routingVectorId[delay->GetTargetId()] != delay->GetRouteId() )
                        {
                                routingVectorId[delay->GetTargetId()] = delay->GetRouteId();
                                noOfFlaps[delay->GetTargetId()]++;
                        }

                }		//endif ClocksOK()
        }
}               //end for-loop

if( printVerbose > 0)			//info - if you want to see it
	printf("sorting ...\n");	//just for info

//sorting all lists by PacketId  
for(boxNo = 0; boxNo < MAX; boxNo++)
{
	//sorting only lists containing data 
	if( ((access + boxNo)->next) && ((access + boxNo)->next->next) && ((access + boxNo)->next->next->next) ) 
	{
		SortList((access + boxNo));		//call subroutine to sort the list
	}
}	

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

//create canvas 
TCanvas *c1 = new TCanvas("c1", "IPDV plots", 0, 0, 1270, 990);

//create pads to display histograms and statistics
TPad *pad1 = new TPad("pad1", "This is pad 1",0.01,0.49,0.43,0.95);	//1-4 will display histograms
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);	//will display statistics

//draw grid 
pad1->SetGridx(); pad1->SetGridy();
pad2->SetGridx(); pad2->SetGridy();
pad3->SetGridx(); pad3->SetGridy();
pad4->SetGridx(); pad4->SetGridy();

//force them to be visible on the canvas - histograms don't appear without this procedure 
pad1->Draw();
pad2->Draw();
pad3->Draw();
pad4->Draw();
pad5->Draw();

//prepare title of the canvas c1
TPaveLabel *pl1 = new TPaveLabel(0.175, 0.956, 0.825, 0.998, "foo", "br");	//prepare, text will be filled later
pl1->SetTextSize(0.45);
pl1->Draw();

        if (ipVersion == 6) {
                TPaveLabel *ip = new TPaveLabel(0.01,0.956,0.1,0.998,"IPv6","br");
                ip->SetTextColor(2);
                ip->SetTextSize(0.8);
                ip->Draw();
        }


// and once again for the next canvas with a pad to display the error plot  
TCanvas *c2 = new TCanvas("c2", "more IPDV plots", 0, 0, 1270, 990);
TPad *errorPad = new TPad("errorPad", "to display errors",0.02,0.04,0.98,0.92);  //will display error graph 
errorPad->SetGridx(); errorPad->SetGridy();
errorPad->Draw();

//prepare title of the canvas c2
TPaveLabel *pl2 = new TPaveLabel(0.175, 0.956, 0.825, 0.998, "foo", "br");       //prepare, text will be filled later
pl2->SetTextSize(0.45);
pl2->Draw();

//create a 1dim-histogram
//TH1F *h1 = new TH1F("h1", "IPDV", 800, -400, 400);
TH1F *h1 = new TH1F("h1", "IPDV", 800, ((-1) * maxIPDV), maxIPDV);
//TH1F *h5 = new TH1F("h5", "PacketDelay", 800, 0, 800);
TH1F *h5 = new TH1F("h5", "PacketDelay", 800, minCustomDelay, maxCustomDelay);

//create 2dim histograms with dynamic borders according to selected time frame
//TH2F *h2 = new TH2F("h2", "IPDV vs time", 500,(minXax-convToRootTime), (maxXax-convToRootTime), 400, -400, 400); 
TH2F *h2 = new TH2F("h2", "IPDV vs time", 500,(minXax-convToRootTime), (maxXax-convToRootTime), 400, ((-1) * maxIPDV), maxIPDV); 
//TH2F *h3 = new TH2F("h3", "delay (black) and hops*10 (red)", 500, (minXax-convToRootTime), (maxXax-convToRootTime), 400, 0, 800);	//delay 
TH2F *h3 = new TH2F("h3", "delay (black) and hops*10 (red)", 500, (minXax-convToRootTime), (maxXax-convToRootTime), 400, minCustomDelay, maxCustomDelay);	//delay 
//TH2F *h4 = new TH2F("h4", "delay (black) and hops*10 (red)", 500, (minXax-convToRootTime), (maxXax-convToRootTime), 400, 0, 800);	//nhops 
TH2F *h4 = new TH2F("h4", "delay (black) and hops*10 (red)", 500, (minXax-convToRootTime), (maxXax-convToRootTime), 400, minCustomDelay, maxCustomDelay);	//nhops 
TH2F *dummy = new TH2F("dummy", "IPDV error", 500, (minXax-convToRootTime), (maxXax-convToRootTime), 400, -400, 400);	//just a placeholder to get the axes for the error diagram

//create title  and style of X axis
char xTitle[100];
char xTimeFormat[6]; 
CreateAxisStyle(xTitle, xTimeFormat, minXax, maxXax, outputFormat);	//call subroutine for calculation 

//set properties of the histograms
SetHistogramProperties(h1, h2, h3, h4, h5, dummy, xTitle, xTimeFormat);	//call subroutine

//convert variables(numbers) in strings, so they can be used in file names
char SourceBox[4];           //number of source box
char startYYYYMMDD[9];           
char endYYYYMMDD[9];             
CreateChar(SourceBox, startYYYYMMDD, endYYYYMMDD, start_Box, minXax, maxXax); 

//write percentiles to a file, if selceted at data input
ofstream IntoFile;              //file handler - declaration here, otherwise compiler complains  
if(savePercentiles > 0)
{
        char percFileName[250];              //selected target Box

        strcpy(percFileName, outputDir);
        strcat(percFileName, "ipdv_percentiles.tt");
        strcat(percFileName, SourceBox);
        strcat(percFileName, ".txt");

        //open file and write data
        IntoFile.open(percFileName);             //open (or create) file

	SavePercentiles((ipdvPercentile + 0), SourceBox, 0, IntoFile, 1); //call subroutine to write the first lines  
}

//write percentiles to a file, if selceted at data input
ofstream theHTML_File;              //file handler - declaration here, otherwise compiler complains
if(createHTML > 0)
{
        char htmlFileName[250];              //selected target Box

        strcpy(htmlFileName, outputDir);
        strcat(htmlFileName, "ipdv_LinkPage.tt");
        strcat(htmlFileName, SourceBox);
        strcat(htmlFileName, ".html");

        //open file and write data
        theHTML_File.open(htmlFileName);             //open (or create) file

        CreateHTMLFile((ipdvPercentile + 0), SourceBox, 0, theHTML_File, 0); //call subroutine to write the first lines
}

//format key for plot file  -  see class TPostScript 
Int_t formatKey;
if(outputFormat == 2)  			//standard output format is gif - gif=0, ps=1, eps=2     
	formatKey = 113;	//113=eps
else
	formatKey = 112;	//112=ps - gif will be created from a *.ps file (by an external Program)


//other strings for filenames and titles
char plotFileName[60];                  //file name of plot
char errorPlotFileName[70];                  //file name of plot
char titleOfWholePlot[250];		//title of the plot, displayed on top of the canvas c1 
char buffer[250];

//calculate IPDVs and draw+save plots - loop over all data  

if(printVerbose > 0)                    //info - if you want to see it
	cout << "calculating and drawing ..." << endl;		//just for info
for(boxNo = 0; boxNo < MAX; boxNo++)
{
	//calcualting only lists containing data
	if( ((access + boxNo)->next) && ((access + boxNo)->next->next) && ((access + boxNo)->next->next->next)  )
	{
		//clear histograms
		//h1->Reset("ICE");
		h1->Reset();	//option "ICE" doesn't reset the number of entries, maybe fixed in a new version of ROOT
		h2->Reset("ICE");
		h3->Reset("ICE");
	        h4->Reset("ICE");
	        //h5->Reset("ICE");
	        h5->Reset();	//option "ICE" doesn't reset the number of entries, maybe fixed in a new version of ROOT

		//create a TGraphErrors - has to be done inside loop
		TGraphErrors *ipdvErr = new TGraphErrors(validPackets[boxNo]);  //constructor will need the number  
										//of entries 

		//do the calculation - call subroutine 
		CalculateAndFillHistograms( (access + boxNo), h1, h2, h3, h4, h5, ipdvErr, (ipdvPercentile + boxNo), enableErrorPlot );

		//create the file name and prepare to save the image, plus adding of the output directory
                if(enableDateInFileName > 0 )
                        CreatePlotFileNameDate(plotFileName, boxNo, SourceBox, startYYYYMMDD, endYYYYMMDD, outputFormat);
                else
                        CreatePlotFileNameStd(plotFileName, boxNo, SourceBox, outputFormat);

                strcpy(buffer, outputDir);              //create whole path and file name
                strcat(buffer, plotFileName);           //plotFileName may be used later for the name of the error plot

		//prepare to write to file
		TPostScript *epsHandle = new TPostScript(buffer, formatKey);	 
		if(formatKey != 113)				//not for *.eps because of unexpected results 
			epsHandle->Range(25.5, 18.8);		//center of A4 page (according to delays program)

		//draw the histograms on canvas c1
		c1->cd();
		DrawHistograms(h1, h2, h3, h4, h5, pad1, pad2, pad3, pad4, customIPDVPlot, customDelayPlot);//call subroutine

		//create title of the plot to be displayed on top of the canvas 
		CreatePlotTitle(titleOfWholePlot, boxNo, SourceBox, minXax, maxXax);
		c1->cd();
		pl1->SetLabel( titleOfWholePlot);
		pl1->Draw();

		//write data to statistics pad
		pad5->cd();
		WriteStatisticsPad(pad5, allPackets[boxNo], validPackets[boxNo], noOfFlaps[boxNo], (ipdvPercentile + boxNo), (h1->GetRMS()), (h5->GetMean()), (h5->GetRMS()) );	//call subroutine

		//write canvas to disk and delete handler
		c1->cd();
		c1->Update();
		epsHandle->Close();	
		delete epsHandle;

		//convert *.ps to *.gif - if it has been selected at data input
		if( outputFormat == 0) 
		{
			sprintf(buffer, "ps2gif -b 41 28 574 750.75 -s 1.2 -r %s%s", outputDir, plotFileName);  
			system(buffer);			//call system function  
			//and now delete the eps file
			sprintf(buffer, "%s%s", outputDir, plotFileName);  
			unlink(buffer);
		}
		else if( outputFormat == 3)
		{
			// convert to pdf
			sprintf(buffer, "ROOTps2pdf %s%s", outputDir, plotFileName);  
			system(buffer);			//call system function  
			//and now delete the eps file
			sprintf(buffer, "%s%s", outputDir, plotFileName);  
			unlink(buffer);
		}

		//draw error plot - if it has been enabled at data input  
		if(enableErrorPlot > 0)
		{
			strcpy(errorPlotFileName, outputDir);		//create file name for error graph, start with path 
			strcat(errorPlotFileName, "error_");		//create file name for error graph
			strcat(errorPlotFileName, plotFileName);

			//draw plot 
			TPostScript *epsErrorHandle = new TPostScript(errorPlotFileName, formatKey);	 
			c2->cd();
			DrawErrorPlot(dummy, ipdvErr, errorPad);		//call subroutine
			c2->cd();
			pl2->SetLabel( titleOfWholePlot);
			pl2->Draw();

			//save to disk
			c2->cd();
			c2->Update();
			epsErrorHandle->Close();
			delete epsErrorHandle;

			//and convert *.ps to *.gif - if it has been selected at data input
	                if( outputFormat == 0)
       		        {
                        	sprintf(buffer, "ps2gif -b 41 28 574 750.75 -s 1.2 -r %s", errorPlotFileName);
                        	system(buffer);			//call the system function    
				//and now delete the eps file
                        	sprintf(buffer, "rm -f %s", errorPlotFileName);
                        	system(buffer);			//call the system function    
                	}

		}

		//write percentiles to a file or write HTML file, if selceted at data input
		if(savePercentiles > 0)
			SavePercentiles((ipdvPercentile + boxNo), "foo", boxNo, IntoFile, 0); //subroutine to write data 

		if(createHTML > 0)
			CreateHTMLFile((ipdvPercentile + boxNo), plotFileName, boxNo, theHTML_File, 1); //call subroutine 

		//delete error plot 
		delete ipdvErr;

	}		//endif
}		//end for loop

//write the end of the file - if file creation has been selected as data input
if(createHTML > 0)
	CreateHTMLFile((ipdvPercentile + boxNo), "foo", boxNo, theHTML_File, 2); //subroutine to write data

//clean up and delete list
Int_t delPackets = 0;
ttmData *del_element = new ttmData;	//to delete the list

for(boxNo = 0; boxNo < MAX; boxNo++)
{
	if((access + boxNo)->next) 	//delete only lists containing data
	{ 
		do
		{
                        del_element = (access + boxNo)->next;                        //first element
                        (access + boxNo)->next = (access + boxNo)->next->next;  //get next element 
                        delete del_element;                          //delete element 
                        delPackets++;                   //counter - debug

		}while((access + boxNo)->next);
	}			//endif
}				//end for loop  

delete delay; 
delete h1;					//histograms
delete h2;
delete h3;
delete h4;
delete h5;
delete dummy;
//delete f_gauss;
delete pad1; delete pad2; delete pad3; delete pad4; delete pad5; delete errorPad;
delete pl1; delete pl2;				//labels
delete c1; 
delete c2;
delete app;

//close files 
IntoFile.close();               //close file
theHTML_File.close();


if(printVerbose > 0)                    //info - if you want to see it
{
	cout << readPackets << " packets filtered" << endl;
	cout << delPackets << " packets deleted" << endl;
}
return(1);
}		// * * * end main * * *


// * * * S U B R O U T I N E S * * *


//generates the axis title and the (time) format string for the x axis 
void CreateAxisStyle(char* xTitle, char* xTimeFormat, Double_t minXax, Double_t maxXax, Int_t outputFormat)
{
	//generates the axis title, depending on the time frame 
	//converts unix timeformat into "readable" strings
	char minText[20];
	char maxText[8];
	long t_minXax = (long) minXax;			//type long is needed for conversion	
	long t_maxXax = (long) maxXax;	
	tm *timestruct = gmtime(&t_minXax);		//convert the first parameter into a time structure 
       
	Int_t ndays = (Int_t) (maxXax - minXax) / 3600 / 24;	//from Unix time format to days
	if (ndays > 3) 
	{
		strftime(minText, 20, "%Y", timestruct);				//extract only year	
                sprintf(xTitle, "      %s%26cTime [month day]%35c", minText,' ', ' ');
                strcpy(xTimeFormat, "%b %d");           //<month> <day> - axis style 
	}
	else
	{
		strftime(minText, 20, "%Y %b %d", timestruct);	
		timestruct = gmtime(&t_maxXax); 
		strftime(maxText, 8, "%b %d", timestruct);	
		sprintf(xTitle, "      %s%20cTime [hour:minute]%21c%s", minText,' ', ' ', maxText);
		if(outputFormat == 2)		//different look for eps (=2) output - don't know why 
			sprintf(xTitle, "      %s%13cTime [hour:minute]%14c%s", minText,' ', ' ', maxText);
        	strcpy(xTimeFormat, "%H:%M");		//<hour> <minute> - axis style 
	}	
	return;
}


//generates the title for the canvas c1 
void CreatePlotTitle(char* titleOfWholePlot, Int_t boxNo, char* SourceBox, Double_t minXax, Double_t maxXax)
{
	char TargetBox[4];              //selected target Box

        if(boxNo  < 10)
                sprintf(TargetBox, "0%d", boxNo);     //convert Int_t into string  
        else
                sprintf(TargetBox, "%d", boxNo);     //convert Int_t into string 

	char timeToText[20];
	long t_minXax = (long) minXax;                  //type long is needed for conversion    
        long t_maxXax = (long) maxXax;
        tm *timestruct = gmtime(&t_minXax);             //convert the first parameter into a time structure 


        strcpy(titleOfWholePlot, "Delay Variations from tt");
        strcat(titleOfWholePlot, SourceBox);
        strcat(titleOfWholePlot, " to tt");
        strcat(titleOfWholePlot, TargetBox);
        strcat(titleOfWholePlot, "  Start: ");

	strftime(timeToText, 20, "%Y-%b-%d,  %H:%M", timestruct);		//extract date and time	

        strcat(titleOfWholePlot, timeToText);
        strcat(titleOfWholePlot, "  End: ");
	
	timestruct = gmtime(&t_maxXax); 
	strftime(timeToText, 20, "%Y-%b-%d, %H:%M", timestruct);		//extract date and time	
        strcat(titleOfWholePlot, timeToText);
        strcat(titleOfWholePlot, " UTC");

	return;
}

//converts numbers into strings, needed for filenames
void CreateChar(char* SourceBox, char* startYYYYMMDD, char* endYYYYMMDD, Int_t start_Box, Double_t minXax, Double_t maxXax) 
{
        //generates the strings needed for generating the strings for plot file name (version with date in name) 
        //converts unix timeformat into "readable" strings
        long t_minXax = (long) minXax;                  //type long is needed for conversion
        long t_maxXax = (long) maxXax;
        tm *timestruct = gmtime(&t_minXax);             //convert the first parameter into a time structure

        //create strings 
	strftime(startYYYYMMDD, 9, "%Y%m%d", timestruct);	

        timestruct = gmtime(&t_maxXax);             //convert the first parameter into a time structure
	strftime(endYYYYMMDD, 9, "%Y%m%d", timestruct);	

	//convert the source box into string 
        if(start_Box < 10)
        {       sprintf( SourceBox, "0%d", start_Box); }
        else
        {       sprintf( SourceBox, "%d", start_Box); }

        return;
}

//create the root file, to add this filt to the tree 
time_t CreateRootFileName(char* sourceFile, time_t act_TimeStamp, Int_t start_Box)	 
{
	char srcBox[10];              //source box

        //need 2 letters - maybe a problem when more than 100 boxes are in the field 
        if(start_Box < 10)
        {       sprintf( srcBox, "tt0%d", start_Box); }
        else
        {       sprintf( srcBox, "tt%d", start_Box); }

	//special handling of tt02 ("osdorp") and tt04 ("jordaan")
	if(start_Box == 2)
		strcpy(srcBox, "osdorp");
	if(start_Box ==4)
		strcpy(srcBox, "jordaan");


	char buffer[50];
	time_t calcTime = act_TimeStamp - 15;		//to be sure to get all data, maybe not really necesary 
        tm *timestruct = gmtime(&calcTime);		//convert parameter into a time structure

	//create file name (incl. path)
	strcpy(sourceFile, fn_Path);                    //fn_Path is global
        strftime(buffer, 45, "%Y/%m/%d/", timestruct);	//make the file name  
	strcat(sourceFile, buffer);			

	if (ipVersion == 6) {
		strcat(sourceFile, "IPv6.");
	}

	strcat(sourceFile, srcBox); 
        strftime(buffer, 45, ".ripe.net.%Y%m%d.root", timestruct);	
	strcat(sourceFile, buffer);   

	act_TimeStamp = act_TimeStamp + 86400;	//86400 = 60*60*24 = seconds per day 
	return act_TimeStamp;  
}

//create the file name of the plot - default
void CreatePlotFileNameStd(char* plotFileName, Int_t boxNo, char* SourceBox, Int_t outputFormat)
{
        char TargetBox[4];              //selected target Box

        if(boxNo  < 10)
                sprintf(TargetBox, "0%d", boxNo);     //convert Int_t into string
        else
                sprintf(TargetBox, "%d", boxNo);     //convert Int_t into string

	if (ipVersion == 6) {
        	strcpy(plotFileName, "IPv6.ipdv.tt");
	}
	else {
        	strcpy(plotFileName, "ipdv.tt");
	}
        strcat(plotFileName, SourceBox);
        strcat(plotFileName, ".tt");
        strcat(plotFileName, TargetBox);
	
	if(outputFormat == 2)			//specify output Format -  gif=0, ps=1, eps=2 
        	strcat(plotFileName, ".eps");
	else
        	strcat(plotFileName, ".ps");	//gif will be generated from an external program
        return;
}

//create the file name of the plot file  -  with date, coded in file name   
void CreatePlotFileNameDate(char* plotFileName, Int_t boxNo, char* SourceBox, char* startYYYYMMDD, char* endYYYYMMDD, Int_t outputFormat)
{
	char TargetBox[4];              //selected target Box
	
	if(boxNo  < 10)
		sprintf(TargetBox, "0%d", boxNo);     //convert Int_t into string  
	else
		sprintf(TargetBox, "%d", boxNo);     //convert Int_t into string  

	if (ipVersion == 6) {
        	strcpy(plotFileName, "IPv6.ipdv.plot.");
	}
	else {
        	strcpy(plotFileName, "ipdv.plot.");
	}
	strcat(plotFileName, SourceBox);
	strcat(plotFileName, ".");
	strcat(plotFileName, TargetBox);
	strcat(plotFileName, ".");
	strcat(plotFileName, startYYYYMMDD);  
	strcat(plotFileName, ".");
	strcat(plotFileName, endYYYYMMDD);    

        if(outputFormat == 2)                   //specify output Format -  gif=0, ps=1, eps=2
                strcat(plotFileName, ".eps");
        else
                strcat(plotFileName, ".ps");    //gif will be generated from an external program
        return;
}

//write (and calculate) data in the statistics pad
void WriteStatisticsPad(TPad* pad, Int_t allPackets, Int_t validPackets, Int_t noOfFlaps, Percentiles* ipdvPercentile, Float_t rmsIPDV, Float_t meanDelay, Float_t rmsDelay)
{
	Double_t x, y;	//coordinates for printing
	char buf[30];
	Int_t align;    	//align of the text 
	Font_t font;		//font of the text  	  

	//clear pad
	pad->Clear();

	//write the title	
	x = 0.5; y = 0.95;	//starting point
	align = 22;					//horizontally and vertically centered
	font = 60;					// helvetiva-bold-r   -   see TAttText class
	write_stats(pad, "STATISTICS", x, y, align, font);	//call subroutine

	//write valid and invalid packets
	y = y - 0.03;					//draw a line to seperate data on pad
	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;
	write_stats(pad, "Packets sent/valid", x, y, align, font);	//call subroutine

	y = y - 0.025;
	sprintf(buf, "Total: %d=100.00%%", allPackets);
	font = 40;					//helvetica-medium-r   -   see TAttText class
	write_stats(pad, buf, x, y, align, font);	//call subroutine

	y = y - 0.025;
	Double_t percent = validPackets * 100.0 / allPackets;
	sprintf(buf, "Valid: %d=%.2f%%", validPackets, percent);
	write_stats(pad, buf, x, y, align, font);	//call subroutine

        y = y - 0.05;
        strcpy(buf, "Number of Routing"); 
        write_stats(pad, buf, x, y, align, font);       //call subroutine
	
        y = y - 0.025;
        sprintf(buf, "flaps: %d", noOfFlaps);
        write_stats(pad, buf, x, y, align, font);       //call subroutine


	//percentiles
        y = y - 0.03;                                   //draw a line to seperate data on pad
        //line1.SetLineWidth(2);
        //line1.SetLineColor(4);
        line1.DrawLine(0.05, y, 0.95, y);

        y -=0.04;
	font = 60;					// helvetiva-bold-r   -   see TAttText class
        write_stats(pad, "IPDV Percentiles", x, y, align, font);     //call subroutine

        y = y - 0.03;
	font = 40;					//helvetica-medium-r   -   see TAttText class
        sprintf(buf, "Mean: %7.2fms", ipdvPercentile->GetLevel(50.0) );
        write_stats(pad, buf, x, y, align, font);       //call subroutine

        y = y - 0.025;
        sprintf(buf, "RMS:  %7.2fms", rmsIPDV );
        write_stats(pad, buf, x, y, align, font);       //call subroutine

        y = y - 0.025;
        sprintf(buf, "50%% > %5.1fms", ipdvPercentile->GetLevel(25.0) );
        write_stats(pad, buf, x, y, align, font);       //call subroutine

        y = y - 0.025;
        sprintf(buf, "50%% < %5.1fms", ipdvPercentile->GetLevel(75.0) );
        write_stats(pad, buf, x, y, align, font);       //call subroutine

        y = y - 0.025;
        sprintf(buf, "85.0%% > %5.1fms", ipdvPercentile->GetLevel(7.5) );
        write_stats(pad, buf, x, y, align, font);       //call subroutine

        y = y - 0.025;
        sprintf(buf, "85.0%% < %5.1fms", ipdvPercentile->GetLevel(92.5) );
        write_stats(pad, buf, x, y, align, font);       //call subroutine

        y = y - 0.025;
        sprintf(buf, "97.5%% > %5.1fms", ipdvPercentile->GetLevel(1.25) );
        write_stats(pad, buf, x, y, align, font);       //call subroutine

        y = y - 0.025;
        sprintf(buf, "97.5%% < %5.1fms", ipdvPercentile->GetLevel(98.75) );
        write_stats(pad, buf, x, y, align, font);       //call subroutine

	
	//mean and RMS of delay distribution 
        y = y - 0.03;                                   //draw a line to seperate data on pad
        //line1.SetLineWidth(2);
        //line1.SetLineColor(4);
        line1.DrawLine(0.05, y, 0.95, y);

        y -=0.04;
	font = 60;					// helvetiva-bold-r   -   see TAttText class
        write_stats(pad, "Delay Distribution", x, y, align, font);     //call subroutine

        y = y - 0.025;
        sprintf(buf, "Mean: %6.2fms", meanDelay);
	font = 40;					//helvetica-medium-r   -   see TAttText class
        write_stats(pad, buf, x, y, align, font);       //call subroutine

        y = y - 0.025;
        sprintf(buf, "RMS: %6.2fms", rmsDelay);
        write_stats(pad, buf, x, y, align, font);       //call subroutine

}


//********************************************************/
//  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 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;
}


//put a set of data from a root file into an a ttmData element
void PutDataInElement(Delay* delay, ttmData* element)
{

        element->PacketId = delay->GetPacketId();
        element->SourceId = delay->GetSourceId();
//      element->SourcePort = delay->GetSourcePort();      
        element->TargetId = delay->GetTargetId();
//      element->TargetPort = delay->GetTargetPort();     
//      element->PacketSize = delay->GetPacketSize();  
        element->ArrivalTime = delay->GetArrivalTime();
        element->PacketDelay = delay->GetPacketDelay();
//        element->SourceClock = delay->GetSourceClock();
//        element->TargetClock = delay->GetTargetClock();
        element->Nhops = delay->GetNhops();
//      element->RouteId = delay->GetRouteId();        
        element->SourceNtp = delay->GetSourceNtp();
	element->TargetNtp = delay->GetTargetNtp();

	return;
}


//sort one list - Bubble Sort
void SortList(ttmData* access)
{
	ttmData *worker;		//will do most of the work

	ttmData store;			//needed for swapping content of elements
	ttmData *store_NN = 0;		//need for swapping elements
	ttmData *store_NNN = 0;		//need for swapping elements

        worker = access->next;                //begin of list 
        do
        {
                //compare first and second element of a list
                if( (access->next->ArrivalTime) > (access->next->next->ArrivalTime) )
                {
	        	//exchange the contents of the first and second element
			//store data of first element for later use
			store.PacketId = access->next->PacketId;
	       		store.SourceId = access->next->SourceId;
			//store.SourcePort = access->next->SourcePort;      
        		store.TargetId = access->next->TargetId;
			//store.TargetPort = access->next->TargetPort;     
			//store.PacketSize = access->next->PacketSize;  
        		store.ArrivalTime = access->next->ArrivalTime;
	        	store.PacketDelay = access->next->PacketDelay;
        		//store.SourceClock = access->next->SourceClock;
        		//store.TargetClock = access->next->TargetClock;
	        	store.Nhops = access->next->Nhops;
			//store.RouteId = access->next->RouteId;        
        		store.SourceNtp = access->next->SourceNtp;
	        	store.TargetNtp = access->next->TargetNtp;

			//overwrite data of first element
			access->next->PacketId = access->next->next->PacketId;
                        access->next->SourceId = access->next->next->SourceId;
                        //access->next->SourcePort = access->next->next->SourcePort;      
                        access->next->TargetId = access->next->next->TargetId;
                        //access->next->TargetPort = access->next->next->TargetPort;     
                        //access->next->PacketSize = access->next->next->PacketSize;  
                        access->next->ArrivalTime = access->next->next->ArrivalTime;
                        access->next->PacketDelay = access->next->next->PacketDelay;
                        //access->next->SourceClock = access->next->next->SourceClock;
                        //access->next->TargetClock = access->next->next->TargetClock;
                        access->next->Nhops = access->next->next->Nhops;
                        //access->next->RouteId = access->next->next->RouteId;        
                        access->next->SourceNtp = access->next->next->SourceNtp;
                        access->next->TargetNtp = access->next->next->TargetNtp;

			//overwrite data in second element 	
                        access->next->next->SourceId = store.SourceId;
                        //access->next->next->SourcePort = store.SourcePort;      
                        access->next->next->TargetId = store.TargetId;
                        //access->next->next->TargetPort = store.TargetPort;     
                        //access->next->next->PacketSize = store.PacketSize;  
                        access->next->next->ArrivalTime = store.ArrivalTime;
                        access->next->next->PacketDelay = store.PacketDelay;
                        //access->next->next->SourceClock = store.SourceClock;
                        //access->next->next->TargetClock = store.TargetClock;
                        access->next->next->Nhops = store.Nhops;
                        //access->next->next->RouteId = store.RouteId;        
                        access->next->next->SourceNtp = store.SourceNtp;
                        access->next->next->TargetNtp = store.TargetNtp;
                }
                //compare the rest of the list 
                if( (worker->next->ArrivalTime) > (worker->next->next->ArrivalTime) )
                {
			store_NN = worker->next->next;
			store_NNN = worker->next->next->next;
                       
			worker->next->next->next = worker->next;
			worker->next->next = store_NNN;
			worker->next = store_NN;
			 //and start again
                        worker = access->next;
                }
                else
                {
                       //next element
                        worker = worker->next;
                }
        }while(worker->next->next);

        return;
}

//if the global variable "MAX" is not sufficient to the number of boxes, this message will be displayed
void TerminationMessage()
{
	cout << endl << "insufficent array size  in program!!!!" << endl;
        cout << "there are more testboxes than at ";
        cout << "the time this program was written" << endl;
        cout << "please set MAX to a higher value and recompile... " << endl << endl;
        
	return;   
}

//calculates delay variation and errors, put it in histograms, fill percentiles 
void CalculateAndFillHistograms(ttmData* access, TH1F* h1, TH2F* h2, TH2F* h3, TH2F* h4, TH1F* h5, TGraphErrors* ipdvErr, Percentiles* ipdvPercentile, Int_t enableErrorPlot)
{
	//Double_t actError, nextError;           //calculate experimantal error of clocks
	Double_t actError_2, nextError_2;           //calculate experimantal error of clocks - no square root is calcualted
	Double_t ipdvError;                     //error of whole IPDV calculation (per pair)
	Double_t ipdVariation;                  //the IPDV
	Int_t packetNo = 0;                         //needed to fill error plot (TGraphErrors will be created in loop)

	ttmData *helper;		//need help
	helper = access->next;    //start with the first element of the list

	do
	{
        	//IPDV(i) = D(i+1) - D(i)             - according to <draft-ietf-ippm-ipdv-07.txt> page 5, 
        	//				        see page 8, where it is defined the other way around-informed the authors 
        	ipdVariation = (helper->next->PacketDelay) - (helper->PacketDelay);

		//fill the histograms
        	h1->Fill(ipdVariation);
	        h2->Fill(((helper->ArrivalTime)-convToRootTime), ipdVariation);
        	h3->Fill(((helper->ArrivalTime)-convToRootTime), helper->PacketDelay);
	        h4->Fill(((helper->ArrivalTime)-convToRootTime), (helper->Nhops) * 10);
        	h5->Fill(helper->PacketDelay);

		//add (or fill) percentiles
		ipdvPercentile->Add(ipdVariation);

	        //calculate and fill error diagram - if it has been enabled at data input  
		if(enableErrorPlot > 0)
		{

        	        //calculate errors
	                //expError(packet) = sqrt(SourceNtp^2 + TargetNtp^2)
/*     		        actError = sqrt(((helper->SourceNtp)*(helper->SourceNtp))  + ((helper->TargetNtp)*(helper->TargetNtp)));
            	        nextError = sqrt(((helper->next->SourceNtp)*(helper->next->SourceNtp)) + ((helper->next->TargetNtp)*(helper->next->TargetNtp)));
              		//ipdvError = sqrt(actError^2 + nextError^2)
                	ipdvError = sqrt((actError * actError) + (nextError * nextError));
*/
	                //calcualte errors - faster version
       		        //expError(packet) = sqrt(SourceNtp^2 + TargetNtp^2); ipdvError = sqrt(actError^2 + nextError^2)
                	//no square root on expError, so no "^2" in the ipdvError
                	actError_2 = (((helper->SourceNtp)*(helper->SourceNtp)) + ((helper->TargetNtp)*(helper->TargetNtp)));
                	nextError_2 = (((helper->next->SourceNtp)*(helper->next->SourceNtp)) + ((helper->next->TargetNtp)*(helper->next->TargetNtp)));
                	//ipdvError = sqrt(actError^2 + nextError^2)
                	ipdvError = sqrt(actError_2 + nextError_2);

			//fill diagram
        		ipdvErr->SetPoint(packetNo, ((helper->ArrivalTime)-convToRootTime), ipdVariation);   //point
		        ipdvErr->SetPointError(packetNo, 0.0, ipdvError);                       //error for this point
		}

	        helper = helper->next;      //get next one
        	packetNo++;

	}while(helper->next);

	return;
}



//set the histogram title, time format, axis title , ....
void SetHistogramProperties(TH1F* h1, TH2F* h2, TH2F* h3, TH2F* h4, TH1F* h5, TH2F* dummy, char* xTitle, char* xTimeFormat)
{
	//set properties of the histograms
//histogram for IPDV distribution
h1->SetMarkerColor(1);
//h1->SetFillColor(7);
h1->SetFillColor(4);
h1->SetStats(kFALSE);                   //no statisics displayed on histogram
h1->GetXaxis()->SetTitle("[ms]");
h1->GetYaxis()->SetTitle("[%]    ");

//histogram for IPDV vs time
h2->SetMarkerColor(4);
h2->GetXaxis()->SetTimeDisplay(1);
h2->GetXaxis()->SetTimeFormat(xTimeFormat);
h2->GetXaxis()->SetTitle(xTitle);
h2->GetYaxis()->SetTitle("[ms]   ");
h2->SetStats(kFALSE);

//histograms for hops and delay vs time
h3->SetMarkerColor(1);			//delay
h3->GetXaxis()->SetTimeDisplay(1);
h3->GetXaxis()->SetTimeFormat(xTimeFormat);
h3->GetXaxis()->SetTitle(xTitle);
h3->GetYaxis()->SetTitle("[ms]   ");
h3->SetStats(kFALSE);
h4->SetMarkerColor(2);			//nhops
h4->SetStats(kFALSE);

//histogram for delay ditribution
h5->SetMarkerColor(2);
h5->SetFillColor(5);
h5->SetStats(kFALSE);
h5->GetXaxis()->SetTitle("[ms]");
h5->GetYaxis()->SetTitle("[%]    ");


//set properties of the error diagram axis ("dummy" is a place holder -  TGraphErrors will be created in loop)
dummy->GetXaxis()->SetTimeDisplay(1);
dummy->GetXaxis()->SetTimeFormat(xTimeFormat);
dummy->GetXaxis()->SetTitle(xTitle);
dummy->GetYaxis()->SetTitle("[ms]   ");
dummy->SetStats(kFALSE);

	return;
}

//draw the histograms on canvas c1
void DrawHistograms(TH1F* h1, TH2F* h2, TH2F* h3, TH2F* h4, TH1F* h5, TPad* pad1, TPad* pad2, TPad* pad3, TPad* pad4, Int_t customIPDVPlot, Int_t customDelayPlot)
{
	//first, declaration of some variables
	Int_t binNo = 0;		//for loops, will contain the number of the bon for zoom operation
	Double_t limit = 0.0;		//exit condition for loop
	Double_t sumBins = 0.0;		//to add up the bins in a loop
	Double_t scale = 0.0;		//factor to normalize the histograms
	
if(customIPDVPlot == 0)	//by default - with auto zooming
{
	//dymanic zoom for the IPDV distribution - displays approx. 99% of the contents
	h1->SetAxisRange(-400, 400);              //needs initalisation, but don't know why 
        limit = 0.005 * h1->GetEntries();	// only right half will be analyzed, distribution is symetric
        sumBins = 0.0;

        for(binNo=801; binNo > 419 ; binNo--)	//801 to get the overflow bin - histogram range is 
        {                                               //from -400 ms to 400 ms  
                sumBins = sumBins + h1->GetBinContent(binNo);
                if( (sumBins > limit) ) break;
        }                                       //end finding the dynamic zoom range 

	//normalize the histogram with the IPDV distribution
        pad1->cd();
        scale = 100 / h1->Integral();                  //normalize the histogram to percent
        h1->Scale(scale);
        h1->SetAxisRange((400-binNo), (binNo-400));              //zoom in - dynamic 
        h1->Draw();

        //draw the histogram with IPDVs vs time 
        pad2->cd();
	h2->GetYaxis()->SetRange(0, 400);		//initialisation - in bin numbers 
	if(binNo < 540)
		h2->GetYaxis()->SetRange(125, 275);	//zoom in on y-axis - numbers are bin numbers 
        h2->Draw();
}
else			//draw customzed IPDV plots
{
        //normalize the histogram with the IPDV distribution
        pad1->cd();
        scale = 100 / h1->Integral();                  //normalize the histogram to percent
        h1->Scale(scale);
        h1->Draw();

        //draw the histogram with IPDVs vs time
        pad2->cd();
        h2->Draw();

}

if(customDelayPlot == 0)	//by default - with auto zooming
{
        //normalize and draw the delay distribution 
        pad3->cd();
	
	//dynamic zoom, more than 99% of the values (in the bins) will be displayed - don't know if it is really usefull
	h5->SetAxisRange(0, 800);              //needs initalisation, but don't know why 
	limit = 0.01 * h5->GetEntries();
	sumBins = 0.0;
 
	for(binNo=801; binNo > 29; binNo--)	//801 to get the overflow bin
	{
		sumBins = sumBins + h5->GetBinContent(binNo);		
		if( (sumBins > limit) ) break;		
	}    					//end finding the dynamic zoom range 

        scale = 100 / h5->Integral();		//normalize the histogram to percents
	h5->Scale(scale);
        if(binNo < 750) h5->SetAxisRange(0, (binNo + 10) );              //zoom in - dynamic 
	h5->Draw();

        //draw the histograms with the delays(h3) and hops(h4) 
        pad4->cd();
	h3->GetYaxis()->SetRange(0, 400);	//initialisation - numbers are bin numbers 
	if(binNo < 250)				//zoom in - dynamic
		h3->GetYaxis()->SetRange(0, 125);	//zoom in on y-axis - bin numbers 
	else
		h3->GetYaxis()->SetRange(0, 200);	//zoom in on y-axis - bin numbers  
	if(binNo > 390)				//just in case 
		h3->GetYaxis()->SetRange(0, 400);	//numbers are bin numbers	 
		
        h3->Draw();
        h4->Draw("sameP");
}
else 		//customized delay plots
{

        //normalize and draw the delay distribution
        pad3->cd();

        scale = 100 / h5->Integral();           //normalize the histogram to percents
        h5->Scale(scale);
        h5->Draw();

        //draw the histograms with the delays(h3) and hops(h4)
        pad4->cd();

        h3->Draw();
        h4->Draw("sameP");

}
	return;
}


//draw the error plot on canvas c2
void DrawErrorPlot(TH2F* dummy, TGraphErrors* ipdvErr, TPad* errorPad)
{
	errorPad->cd();

        dummy->Draw();                  //just to get the axes 
        ipdvErr->SetMarkerStyle(25);
        ipdvErr->SetMarkerSize(0.25);
        ipdvErr->SetMarkerColor(3);
        ipdvErr->Draw("P");             //drawn without axes 

	return;
}

//extract the data from the command line so the calculation can be done
//inspired by Rene's program getargs.C 
void ExtractInput(int argc, char** argv, Int_t* start_Box, Int_t* end_Box, time_t& endtime, time_t& duration, Int_t* enableErrorPlot, Int_t* outputFormat, Int_t* enableDateInFileName, Int_t* savePercentiles, Int_t* createHTML, Int_t* printVerbose, char* outputDir, Int_t* maxIPDV, Int_t* customIPDVPlot, Int_t* maxCustomDelay, Int_t* minCustomDelay, Int_t*  customDelayPlot)
{
	Int_t noOfEntries = 0;	//to make sure all mandatory switches  are present  
        char ch;
        char * pEnd;            //needed for conversion, see documentation of "strtod" and "strtol"
	Int_t helpme = 0;
        extern char *optarg;

        //check if any (or too litle) arguments are there
        if(argc <= 1)
        {
                cout << "program to calculate delay variations (aka.: IPDV, Jitter)" << endl;
                cout << "use the option --help to get help" << endl;                              //friendly advise 
                cout << "usage: " << progname << " -s startbox -p starttime/endtime   [-t target] [-o outputdir] [-f format] [-m max. delay] [-z min.delay] [-u max. IPDV] [-v] [-6] [-r] [-h] [-e] [-d]" << endl;                              //friendly advise 
                exit(0);
        }

        while ((ch = getopt(argc, argv, "46rdehv?s:t:f:m:o:p:u:z:-:")) != -1)

        switch(ch)
        {
	case '4':
		ipVersion = 4;
		break;
	case '6':
		ipVersion = 6;
		break;
        case 's':
                //*start_Box = (Int_t) strtol (optarg, &pEnd, 0); //convert string into number (long)
		sscanf(optarg, "%d", start_Box);
		noOfEntries++; 
                break;
        case 't':
                //*end_Box = (Int_t) strtol (optarg, &pEnd, 0);   //convert string into number
		sscanf(optarg, "%d", end_Box);
                break;
        case 'd':
                *enableDateInFileName = 1;
                break;
        case 'e':
                *enableErrorPlot = 1;
                break;
        case 'f':
		*outputFormat = 0;		//default (=gif)  -   gif=0, ps=1, eps=2, pdf=3
                if(strncmp(optarg,  "ps", 2) == 0)
                        *outputFormat = 1;
                if(strncmp(optarg,  "eps", 3) == 0)
                        *outputFormat = 2;
                if(strncmp(optarg,  "pdf", 3) == 0)
                        *outputFormat = 3;
                break;
	case 'm':							//max. delay in histogram
                *maxCustomDelay = (Int_t) strtod (optarg, &pEnd);	//convert string into number
		*customDelayPlot = 1;	
		break;
        case 'z':							//min. delay in histogram
                *minCustomDelay = (Int_t) strtod (optarg, &pEnd);   //convert string into number
                *customDelayPlot = 1;                                                
                break;
	case 'u':						//max. IPDV in histogram
                *maxIPDV = (Int_t) strtod (optarg, &pEnd);   //convert string into number
		*customIPDVPlot = 1;
		break;
        case 'o':
                strcpy(outputDir, optarg);
                //make sure that string ends with '/' , otherwise add it to the string
                if(((strrchr(outputDir, '/'))-outputDir+1 ) != (strlen(outputDir)) )
                        strcat(outputDir, "/");                         //add "/" if it is not already there
                break;
        case 'p':
                decodePeriod(optarg, endtime, duration);        //call function in "iso8601.h" to decode time
		noOfEntries++; 
                break;
        case 'r':
                        *savePercentiles = 1;
                break;
        case 'h':
                        *createHTML = 1;
                break;
        case 'v':
                        *printVerbose = 1;
                break;
        case '?':       //this part is a bit dirty, but should be sufficent for the beginning
        case '-':       //to get --help
        default:
                cout << "program to calculate delay variations (aka.: IPDV, Jitter)" << endl;
                cout << "options: " << "-s source box no." << endl;
                cout << "         " << "-p start time and end time, format is YYYYMMDD/YYYYMMDD," << endl;
                cout << "         " << "   any ISO 8601 format is ok, like YYYYMMDDThh:mm:ss or similar" << endl;
                cout << endl;
                cout << "         " << "-t target box no., -1 for all connected boxes, optional, default = -1" << endl;
                cout << endl;
                cout << "         " << "-d date in file name (ipdv.tt_A.tt_B.start.end.*), optional, default = 'off' " << endl;
                cout << "         " << "-f output file format, 'gif', 'ps', 'pdf' or 'eps', optional, default = 'gif' " << endl;
		cout << "         " << "-m maximum delay in ms to be displayed on histograms, optional" << endl;
		cout << "         " << "-z minimum delay in ms to be displayed on histograms, optional" << endl;
		cout << "         " << "-u maximum IPDV in ms to be displayed on histograms, optional" << endl;
                cout << "         " << "-o output directory for plots, optional, default is current directory " << endl;
                cout << "         " << "-r write percentiles to a file, optional, default = 'off' " << endl;
                cout << "         " << "-h create a HTML file for the webpage, optional, default = 'off' " << endl;
                cout << "         " << "-e enable error plot, optional, default = 'off' " << endl;
                cout << "         " << "-v verbose, 'on' or 'off', optional, default = 'off' " << endl;
                exit(0);
        }

	if(noOfEntries < 2)
	{
		cout << "insuffucent input or misstyped a switch" << endl;	
                cout << "use the option --help to get help" << endl;		//friendly advise 
                exit(0);
	}
        return;
}

//create a HTML file to link the plots from the web site - if it has been selected at data input
void CreateHTMLFile(Percentiles* ipdvPercentile, char* anyString,  Int_t boxNo, ofstream &IntoFile, Int_t paragraphID)
{
        //paragraphID = 0  -  write the first line of the file
        if(paragraphID == 0)
        {
                IntoFile << "<html>" << endl << "<head>" << endl;
                IntoFile << "<title>Delay Variations - Testbox " << anyString << "</title>" << endl;
                IntoFile << "</head>" << endl << "<body>" << endl;
		IntoFile << "<br><table width=795 border=0><tr align=center><td><h2>from Testbox " << anyString << " to </h2></td></tr></table>" << endl;
		//IntoFile << "<table width=795 border=2 cellpadding=5>" << endl;  //Opera doesn't like this style 
		IntoFile << "<table border=2 cellpadding=5>" << endl;
		IntoFile << "<colgroup> <col width=55> <col width=105> <col width=105> <col width=105> <col width=105> <col width=105> <col width=105> <col width=105> </colgroup>" << endl;

		//write table headline 
                IntoFile << "<tr align=center>";
                IntoFile << "<td>tt</td>";      //box number
                IntoFile << "<td>mean</td>";         //mean
                IntoFile << "<td>50%<br>lower edge</td>";         //50% lower edge
                IntoFile << "<td>50%<br>upper edge</td>";         //50% upper edge
                IntoFile << "<td>85%<br>lower edge</td>";          //85% lower edge
                IntoFile << "<td>85%<br>upper edge</td>";         //85% upper edge
                IntoFile << "<td>97.5%<br>lower edge</td>";         //97.5% lower edge
                IntoFile << "<td>97.5%<br> upper edge</td>";        //97.5% upper edge
                IntoFile << "</tr>" << endl;

        }

        //paragraphID = 1  -  add a line of data to the file
        if(paragraphID == 1)
        {
		//manipulate file name from *.eps to *.gif
		char buffer[250] = "";
		strncat(buffer, anyString, (strlen(anyString)-2) );	//cut "ps" from filename
		strcat(buffer, "gif");					//and add "gif" 

		//create a line for the table on the website
		IntoFile << "<tr align=center>";
                IntoFile << "<td><a href=" << buffer << ">" << boxNo << "</a></td>";      //box number
                IntoFile << "<td>" << ipdvPercentile->GetLevel(50.0) << " ms</td>";         //mean
                IntoFile << "<td>" << ipdvPercentile->GetLevel(25.0) << " ms</td>";         //50% lower edge
                IntoFile << "<td>" << ipdvPercentile->GetLevel(75.0) << " ms</td>";         //50% upper edge
                IntoFile << "<td>" << ipdvPercentile->GetLevel(7.5) << " ms</td>";          //85% lower edge
                IntoFile << "<td>" << ipdvPercentile->GetLevel(92.5) << " ms</td>";         //85% upper edge
                IntoFile << "<td>" << ipdvPercentile->GetLevel(1.25) << " ms</td>";         //97.5% lower edge
                IntoFile << "<td>" << ipdvPercentile->GetLevel(98.75) << " ms</td>";        //97.5% upper edge
                IntoFile << "</tr>" << endl;

        }

        //paragraphID = 2  -  add last line to the file
        if(paragraphID == 2)
	{
		IntoFile << "</table>" << endl << "</body>" << endl << "</html>" << endl;
	}
        return;
}


//write percentiles to file - if it has been selected at data input
void SavePercentiles(Percentiles* ipdvPercentile, char* SourceBox,  Int_t boxNo, ofstream &IntoFile, Int_t firstLine)
{
	//firstLine = 1  -  write the first line of the file
	if(firstLine > 0)
	{
		IntoFile << "# percentiles of all boxes connected to testbox tt " << SourceBox << endl << endl; 
		IntoFile << "#testbox    mean  50%_lower 50%_upper 85%_lower 85%_upper 97.5%_lower 97.5%_upper" << endl <<endl;
		return;
	}
	//firstLine = 0  -  add a line of data to the file 
	else
	{
		IntoFile.width(6); IntoFile << boxNo;					//box number  
                IntoFile.width(10); IntoFile << ipdvPercentile->GetLevel(50.0);		//mean  
		IntoFile.width(10); IntoFile << ipdvPercentile->GetLevel(25.0);		//50% lower edge 
		IntoFile.width(10); IntoFile << ipdvPercentile->GetLevel(75.0);		//50% upper edge  
		IntoFile.width(10); IntoFile << ipdvPercentile->GetLevel(7.5);		//85% lower edge  
		IntoFile.width(10); IntoFile << ipdvPercentile->GetLevel(92.5);		//85% upper edge  
		IntoFile.width(10); IntoFile << ipdvPercentile->GetLevel(1.25);		//97.5% lower edge  
		IntoFile.width(10); IntoFile << ipdvPercentile->GetLevel(98.75);	//97.5% upper edge 
                IntoFile << endl;   

	}
        return;
}

//check if the parameters for the custom plots make sense (and the program doesn't crash)
Int_t CheckPlotParameter(Int_t* maxIPDV, Int_t* maxCustomDelay, Int_t* minCustomDelay)
{
/*	*maxIPDV > 0
	*minCustomDelay >= 0
	*minCustomDelay < *maxCustomDelay
*/
	if( (*maxIPDV > 0) && (*minCustomDelay >= 0) && (*minCustomDelay < *maxCustomDelay) )
		return(1);
	else			//program should run with these values (hope so)
	{
		if(*maxIPDV < 0)
			*maxIPDV = 2;
		if(*minCustomDelay < 0)
			*minCustomDelay = 0;
		if(*maxCustomDelay < *minCustomDelay)
			*maxCustomDelay = *minCustomDelay + 10;		//no better ideas
	}
	
	return(1);
}




 

