#include "hseed.h"
#include "TSystem.h"
#include "TMath.h"
#include "TInetAddress.h"
#include "TDatime.h"
#include <iostream>
#include <iomanip>
#include <unistd.h>
using namespace std;
ClassImp(HSeed)
HSeed::HSeed(Int_t method,Int_t fallback,Int_t fixedSeed, Bool_t noBlock)
{
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    fNoBlock = noBlock;
    fHostname = gSystem->HostName();
    fAddress  = getIP();
    fPid      = getPid();
    frandom = 0;
    if(method < 0 || method > 5)
    {
	Error("HSeed()","method %i :  method has to be 1 ... 5. Use 0 instead.",method);
	fMethod = 0;
    } else {
	fMethod = method;
    }
    if(fMethod != 0 ) fallback = fMethod;
    if(fallback < 1 || fallback > 4)
    {
	Error("HSeed()","fallback %i :  method has to be 1 ... 4. Use 2 instead.",fallback);
	fFallBack = 2;
    } else {
	fFallBack = fallback;
    }
    fFirstSeed   =-1;
    fInitialSeed =-1;
    fFixedSeed   =-1;
    fFileHandle  =-1;
    if(fNoBlock) fFileHandle = open("/dev/urandom", O_RDONLY);
    else         fFileHandle = open("/dev/random" , O_RDONLY);
    if(fFileHandle < 0){
	if(fNoBlock) Error("HSeed()","Could not open /dev/urandom for reading : will take numbers from TRandom3 ( method 2)");
	else         Error("HSeed()","Could not open /dev/random for reading : will take numbers from TRandom3 ( method 2)");
	fMethod   = fallback;
	fFallBack = fallback; 
	fFileHandle =-1;
    }
    TDatime datime;
    UInt_t t      = datime.Get();
    Int_t  iplast = getIPPart(0);
    if        (fMethod == 0) {
	if     (fFallBack == 1) {fGenerator.SetSeed(t);                       frandom = &fGenerator; fInitialSeed = t;}
	else if(fFallBack == 2) {fGenerator.SetSeed(t+fPid*1000+iplast*1000); frandom = &fGenerator; fInitialSeed = t+fPid*1000+iplast*1000;}
	else if(fFallBack == 3) {gRandom  ->SetSeed(t);                       frandom = gRandom;     fInitialSeed = t;}
	else if(fFallBack == 4) {gRandom  ->SetSeed(t+fPid*1000+iplast*1000); frandom = gRandom;     fInitialSeed = t+fPid*1000+iplast*1000;}
    } else {
	if     (fMethod == 1) {fGenerator.SetSeed(t);                       frandom = &fGenerator;fInitialSeed = t;}
	else if(fMethod == 2) {fGenerator.SetSeed(t+fPid*1000+iplast*1000); frandom = &fGenerator;fInitialSeed = t+fPid*1000+iplast*1000;}
	else if(fMethod == 3) {gRandom  ->SetSeed(t);                       frandom = gRandom;    fInitialSeed = t;}
	else if(fMethod == 4) {gRandom  ->SetSeed(t+fPid*1000+iplast*1000); frandom = gRandom;    fInitialSeed = t+fPid*1000+iplast*1000;}
	if(fMethod == 5){
	    if(fixedSeed < 0 ) { Error("HSeed()","Option fixedSeed used, but seed is negative! will use Abs()"); }
	    fFixedSeed   = TMath::Abs(fixedSeed);
	    fInitialSeed = fFixedSeed;
	    fFirstSeed   = fFixedSeed;
	}
    }
}
HSeed::~HSeed()
{
    if(fFileHandle!=-1){
	close(fFileHandle);
    }
    fFileHandle=-1;
}
UInt_t HSeed::getPid()
{
    
    return gSystem->GetPid();
}
UInt_t HSeed::getIP()
{
    
    TString hostname     = gSystem->HostName();
    TInetAddress address = gSystem->GetHostByName(hostname.Data());
    return address.GetAddress();
}
UInt_t HSeed::getIPPart(UInt_t field)
{
    
    if(field > 3) {
	Error("getIPPart()","address field has to be 0,1,2 or 3 . Take lowest bytes instead.");
	field=0;
    }
    return (0xFF)&(fAddress>>field*8);
}
Int_t HSeed::getSeed()
{
    
    Int_t rndNum =-1;
    if        (fMethod == 0)
    {
	Int_t ret = read(fFileHandle, &rndNum, sizeof(rndNum));
	if(ret < 0){
	    if(fNoBlock) Error("getSeed()"," Could not read from /dev/urandom : will take numbers from fallback");
	    else         Error("getSeed()"," Could not read from /dev/random : will take numbers from fallback");
	    close(fFileHandle);
	    fFileHandle = -1;
	    fMethod     = fFallBack;
	} else {
	    while(rndNum<0){   
		ret = read(fFileHandle, &rndNum, sizeof(rndNum));
	    }
	    if(fFirstSeed == -1) fFirstSeed = rndNum;
	    return rndNum;
	}
    } else if (fMethod > 0 && fMethod < 5) {
	while(rndNum<0) {
	    rndNum = frandom->Rndm()*(kMaxInt-1);
	}
	if(fFirstSeed == -1) fFirstSeed = rndNum;
	return rndNum;
    }
    else if(fMethod == 5 ){
	return fFixedSeed;
    }
  return -1;
}
void  HSeed::print()
{
    
    cout<<"HSeed : "<<fHostname<<", ip : "<<Form("%i.%i.%i.%i",getIPPart(3),getIPPart(2),getIPPart(1),getIPPart(0))<<", pid :" <<fPid
	<<", method : "  <<setw(4) <<fMethod
	<<", fallback : "<<setw(4) <<fFallBack
	<<", first : "   <<setw(10)<<fFirstSeed
	<<", init : "    <<setw(10)<<fInitialSeed
	<<", fixed : "   <<setw(10)<<fFixedSeed
	<<endl;
}