/*
 * Copyright 2011 EFDA | European Fusion Development Agreement
 *
 * Licensed under the EUPL, Version 1.1 or - as soon they 
   will be approved by the European Commission - subsequent  
   versions of the EUPL (the "Licence"); 
 * You may not use this work except in compliance with the 
   Licence. 
 * You may obtain a copy of the Licence at: 
 *  
 * http://ec.europa.eu/idabc/eupl
 *
 * Unless required by applicable law or agreed to in 
   writing, software distributed under the Licence is 
   distributed on an "AS IS" basis, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 
   express or implied. 
 * See the Licence for the specific language governing 
   permissions and limitations under the Licence. 
 *
 * $Id$
 *
**/
#include "EPICSHandler.h"
#include "Atomic.h"
#include "CDBExtended.h"

// EPICS version
#include "nciu.h"
#include "caProto.h"
#include "epicsVersion.h"
#include "epicsRelease.h"

// EPICS FileDescriptor Manager
#include <fdmgr.h>
#include <fdManager.h>

// EPICS pCAS GDD stuff
#include "gddAppFuncTable.h"
#include "smartGDDPointer.h"
#include "gddApps.h"
#include "aitTypes.h"

#include "FString.h"
#include "BasicTypes.h" // BasicTypeDescriptor
#include "Sleep.h"

#define DEFAULT_EPICS_DBG 0
#define DEFAULT_EPICS_HOPR 10.0
#define DEFAULT_EPICS_LOPR -10.0
#define DEFAULT_EPICS_LEN 1
#define DEFAULT_EPICS_PREC 4
//#define DEFAULT_EPICS_SCAN 1.0
#define DEFAULT_EPICS_SCAN 0
#define DEFAULT_EPICS_EGU ""
#define DEFAULT_EPICS_SYNC "excasIoSync"
#define DEFAULT_EPICS_ASYNC "excasIoAsync"
#define DEFAULT_EPICS_SCANON true
#define DEFAULT_EPICS_ASYNCSCAN true
#define DEFAULT_EPICS_ASYNCDELAY 0.1
#define DEFAULT_EPICS_MAXSIMULTASYNCIO 1000u
#define DEFAULT_EPICS_HYST 0.0
#define DEFAULT_EPICS_ADEL 0.0
#define DEFAULT_EPICS_MDEL 0.0

#define DEFAULT_EPICS_CPUMASK 0xFFFF
#define DEFAULT_EVENT_CPUMASK 0xFFFF

#define DEFAULT_BUFFER_SIZE 0xFFFF
#define DEFAULT_BUFFER_ALIGN 4

const char * EPICSHandler::tf_strings[] = { "false", "true" };
const int32  EPICSHandler::tf_values[] = { false, true };

/*
typedef enum {
	aitEnumInvalid=0,
	aitEnumInt8,
	aitEnumUint8,
	aitEnumInt16,
	aitEnumUint16,
	aitEnumEnum16,
	aitEnumInt32,
	aitEnumUint32,
	aitEnumFloat32,
	aitEnumFloat64,
	aitEnumFixedString,
	aitEnumString,
	aitEnumContainer
} aitEnum;
*/

// TODO
//see base-3-14-11/src/gdd/aitTypes.c:41 aitName
//see base-3-14-11/src/gdd/aitTypes.h:131 ... 
//aitSize aitName aitPrintf aitScanf
const char * EPICSHandler::aitEnum_strings[] = {
	"aitEnumInvalid",
	"aitEnumInt8",
	"aitEnumUint8",
	"aitEnumInt16",
	"aitEnumUint16",
	"aitEnumEnum16",
	"aitEnumInt32",
	"aitEnumUint32",
	"aitEnumFloat32",
	"aitEnumFloat64",
	"aitEnumFixedString",
	"aitEnumString",
	"aitEnumContainer",
	0 };

// the following must match epics/base/include/menuAlarmSevr.h
#include "menuAlarmSevr.h"
const char * EPICSHandler::menuAlarmSevr_strings[] = {
		"NO_ALARM",
		"MINOR",
		"MAJOR",
		"INVALID",
		0 };

// the following must match epics/base/include/menuAlarmStat.h
#include "menuAlarmStat.h"
const char * EPICSHandler::menuAlarmStat_strings[] = {
		"NO_ALARM",
		"READ",
		"WRITE",
		"HIHI",
		"HIGH",
		"LOLO",
		"LOW",
		"STATE",
		"COS",
		"COMM",
		"TIMEOUT",
		"HWLIMIT",
		"CALC",
		"SCAN",
		"LINK",
		"SOFT",
		"BAD_SUB",
		"UDF",
		"DISABLE",
		"SIMM",
		"READ_ACCESS",
		"WRITE_ACCESS",
		0 };

// the following must match epics/base/include/menuScan.h
#include "menuScan.h"
const char * EPICSHandler::menuScan_strings[] = {
		"Passive",
		"Event",
		"I/O Intr",
		"10 second",
		"5 second",
		"2 second",
		"1 second",
		".5 second",
		".2 second",
		".1 second",
		0 };

const float EPICSHandler::menuScan_values[] = {
		-1.0, -1.0, -1.0,
		10.0, 5.0, 2.0, 1.0,
		0.5, 0.2, 0.1,
		0 };

// the following must match epics/base/include/menuYesNo.h
#include "menuYesNo.h"
const char * EPICSHandler::menuYesNo_strings[] = {
	"NO",
	"YES",
	0 };

/*
enum excasIoType { excasIoSync, excasIoAsync };
*/ 
const char * EPICSHandler::excasIoType_strings[] = {
	DEFAULT_EPICS_SYNC,
	DEFAULT_EPICS_ASYNC
	};

const int EPICSHandler::menu_values[] = {
		0, 1, 2, 3, 4, 5, 6, 7,
		8, 9, 10, 11, 12, 13, 14, 15,
		16, 17, 18, 19, 20, 21, 22, 23
};


bool EPICSHandler::ObjectLoadSetup( ConfigurationDataBase &info,StreamInterface *err) {
	// we use an Extended CDB in order to read double, strings and etc..
    CDBExtended cdb(info);
    int32 _i32;
    
    if ( !GCNamedObject::ObjectLoadSetup(info,err) ) {
        AssertErrorCondition(InitialisationError,
        		"EPICSHandler::ObjectLoadSetup: ?!?!: GCNamedObject::ObjectLoadSetup Failed");
        return False;
    }
    AssertErrorCondition(Information,
    		"EPICSHandler::ObjectLoadSetup: %s: Loading Process Variables",
    		Name());

    // read the common prefix of all Process Variables (is not mandatory
    if ( !cdb.ReadFString(pvPrefix, "PREFIX", "") ) {
        AssertErrorCondition(Information,
        		"EPICSHandler::ObjectLoadSetup: %s: PREFIX not specified. Assuming no prefix.",
        		Name());
    }
    
    if ( !cdb.ReadOptions(_i32, "scanOn", tf_strings, tf_values, DEFAULT_EPICS_SCANON) ) {
        AssertErrorCondition(Information,
        		"EPICSHandler::ObjectLoadSetup: %s: scanOn not specified. Assuming %d.",
        		Name(), DEFAULT_EPICS_SCANON);    	
    }
    scanOn = _i32;
    if ( !cdb.ReadOptions(_i32, "asyncScan", tf_strings, tf_values, DEFAULT_EPICS_ASYNCSCAN) ) {
    	AssertErrorCondition(Information,
    			"EPICSHandler::ObjectLoadSetup: %s: asyncScan not specified. Assuming %d.",
    			Name(), DEFAULT_EPICS_ASYNCSCAN);    	
    }    
    asyncScan = _i32;
    if ( !cdb.ReadDouble(asyncDelay, "asyncDelay", DEFAULT_EPICS_ASYNCDELAY) ) {
        AssertErrorCondition(Information,
        		"EPICSHandler::ObjectLoadSetup: %s: asyncDelay not specified. Assuming %f.",
        		Name(), DEFAULT_EPICS_ASYNCDELAY);
    }
    if ( !cdb.ReadInt32( _i32, "maxSimultAsyncIO", (int32)DEFAULT_EPICS_MAXSIMULTASYNCIO) ) {
        AssertErrorCondition(Information,
        		"EPICSHandler::ObjectLoadSetup: %s: maxSimultAsyncIO not specified. Assuming %d.",
        		Name(), DEFAULT_EPICS_MAXSIMULTASYNCIO);
    }
    maxSimultAsyncIO = _i32;
    if ( !cdb.ReadInt32( _i32, "debugLevel", (int32)DEFAULT_EPICS_DBG) ) {
        AssertErrorCondition(Information,
        		"EPICSHandler::ObjectLoadSetup: %s: debugLevel not specified. Assuming %d.",
        		Name(), DEFAULT_EPICS_DBG);
    }
    debugLevel = _i32;
  
    //PublishSubscriber Interface(TODO) configuration options

    if ( !cdb.ReadInt32(cpuMask,"RunOnCPU", DEFAULT_EPICS_CPUMASK) ) {
    	AssertErrorCondition(Information,
    			"EPICShandler ::ObjectLoadSetup: %s: RunOnCPU not specified. Assuming %d.",
    			Name(), cpuMask);
    }
    if ( !cdb.ReadInt32(cpuMask_event,"RunOnCPU_event", DEFAULT_EVENT_CPUMASK) ) {
    	AssertErrorCondition(Information,
    			"EPICShandler ::ObjectLoadSetup: %s: RunOnCPU_event not specified. Assuming %d.",
    			Name(), cpuMask_event);
    }
    
    //buffers number, buffer must be allocated before event's thread allocation

    if ( !cdb.ReadInt32(buffer_size,"BufferSize", DEFAULT_BUFFER_SIZE) ) {
    	AssertErrorCondition(Information,
    			"EPICShandler ::ObjectLoadSetup: %s: BufferSize not specified. Assuming %08x.",
    			Name(), buffer_size);
    }
    if ( !cdb.ReadInt32(buffer_align,"BufferAlign", DEFAULT_BUFFER_ALIGN) ) {
    	AssertErrorCondition(Information,
    			"EPICShandler ::ObjectLoadSetup: %s: BufferAlign not specified. Assuming %08x.",
    			Name(), buffer_align);
    }
        
    //create the array of Process Variable descriptors (pvInfo)
    if ( !cdb->Move("ProcessVariable") ) {
        AssertErrorCondition(InitialisationError,
        		"EPICSHandler::ObjectLoadSetup: %s: No Process Variable specified. Fatal error!",
        		Name());
        return False;
    }
    numberOfPVs = cdb->NumberOfChildren();
    if ( numberOfPVs == 0 ) {
        AssertErrorCondition(InitialisationError,
        		"EPICSHandler::ObjectLoadSetup: %s: Number of signals is zero. Fatal error!",
        		Name());
        return False;
    }
    pvList = new pvInfo* [numberOfPVs];
    if ( pvList == NULL ) {
        AssertErrorCondition(InitialisationError,
        		"EPICSHandler::ObjectLoadSetup: %s: Failed to allocate space for %d ProcessVariables. Fatal error!",
        		Name(), numberOfPVs);
        return False;
    }
    
    //create the pCAS here and then attach the PV's
    pCAS = new exServer( scanOn != 0, asyncScan== 0,
    		asyncDelay, maxSimultAsyncIO );
    if ( !pCAS ) {
        AssertErrorCondition(InitialisationError,
        		"EPICSHandler::ObjectLoadSetup: %s: Failed to allocate space for pCAS. Fatal error!",
        		Name() );
        return False;
    }
    pCAS->setDebugLevel(debugLevel);
    
    //read process variables properties loop
    //TODO every PV must have it's own custom ObjectLoadSetup
    //TODO at least do that for the DBR/DBF types
    int i=0;
    for(i=0; i<numberOfPVs; i++) {
        cdb->MoveToChildren(i);
        
        FString pvName;
        if ( !cdb.ReadFString(pvName, "NAME") ) {
            AssertErrorCondition(InitialisationError,
            		"EPICSHandler::ObjectLoadSetup: %s: NAME not specified. Fatal error!",
            		Name());
            return False;
        }
//        ((char *)(pvName.Buffer()))[pvName.Size()] = 0;
        FString pvType;
        aitEnum aitType;
        if ( !cdb.ReadFString(pvType, "TYPE") ) {
            AssertErrorCondition(InitialisationError,
            		"EPICSHandler::ObjectLoadSetup: %s: TYPE not specified. Fatal error!",
            		Name());
            return False;
        }
        // convert to ait type
        aitType = ConvertToaitEnum( pvType.Buffer() );
        
        FString pvSync;
        excasIoType excasSync;
        if ( !cdb.ReadFString(pvSync, "SYNC", DEFAULT_EPICS_SYNC) ) {
            AssertErrorCondition(InitialisationError,
            		"EPICSHandler::ObjectLoadSetup: %s: TYPE not specified. Assuming %s",
            		Name(), DEFAULT_EPICS_SYNC);
        }
        // convert to excasIoType
        excasSync = ConvertToexcasIoType( pvSync.Buffer() );

        unsigned pvElements;
        if ( !cdb.ReadInt32(_i32, "LEN", (int32)DEFAULT_EPICS_LEN) ) {
           	AssertErrorCondition(Information,
           			"EPICSHandler::ObjectLoadSetup: %s: LEN not specidfied. Assuming %d",
           			Name(), pvElements);
        }
        pvElements = _i32;
/* previous implementation        
        double pvScanPeriod;
        if ( !cdb.ReadDouble(pvScanPeriod, "SCAN", DEFAULT_EPICS_SCAN)) {
        	AssertErrorCondition(Information,
        			"EPICSHandler::ObjectLoadSetup: %s: SCAN not specified. Assuming %f", 
        			Name(), DEFAULT_EPICS_SCAN);
        }
*/        
        if( !cdb.ReadOptions(_i32, "SCAN", menuScan_strings, menu_values, DEFAULT_EPICS_SCAN) ) {
        	AssertErrorCondition(Information,
        			"EPICSHandler::ObjectLoadSetup: %s: SCAN not specified. Assuming \"%s\"", 
        			Name(), menuScan_strings[DEFAULT_EPICS_SCAN]);        	
        }
        float pvScanPeriod = menuScan_values[_i32];
        
        FString pvUnits;
        if ( !cdb.ReadFString(pvUnits, "EGU", DEFAULT_EPICS_EGU) ) {
            AssertErrorCondition(InitialisationError,
            		"EPICSHandler::ObjectLoadSetup: %s: EGU not specified. Assuming \"%s\"",
            		Name(), DEFAULT_EPICS_EGU);
        }
//        ((char *)(pvUnits.Buffer()))[pvUnits.Size()] = 0;
        
        // graphical limits "High/Low OPerational Range"
        double pvHopr;
        if(!cdb.ReadDouble(pvHopr, "HOPR", DEFAULT_EPICS_HOPR)) {
        	AssertErrorCondition(Information, 
        			"EPICSHandler::ObjectLoadSetup: %s: HOPR not specified. Assuming %f",
        			Name(), DEFAULT_EPICS_HOPR);
        }     
        double pvLopr;
        if ( !cdb.ReadDouble(pvLopr, "LOPR", DEFAULT_EPICS_LOPR)) {
        	AssertErrorCondition(Information,
        			"EPICSHandler::ObjectLoadSetup: %s: LOPR not specified. Assuming %f", 
        			Name(), DEFAULT_EPICS_LOPR);
        }

        // alarm limits
        // the policy is to set alarms to OPerational Ranges if not specified
        double pvHihi; //Hihi Alarm Limit
        if(!cdb.ReadDouble(pvHihi, "HIHI", pvHopr)) {
        	AssertErrorCondition(Information, 
        			"EPICSHandler::ObjectLoadSetup: %s: HIHI not specified. Assuming %f",
        			Name(), pvHopr);
        }
        double pvHigh; //High Alarm Limit 
        if(!cdb.ReadDouble(pvHigh, "HIGH", pvHopr)) {
        	AssertErrorCondition(Information, 
        			"EPICSHandler::ObjectLoadSetup: %s: HIGH not specified. Assuming %f",
        			Name(), pvHopr);
        }     
        double pvLow; //Low Alarm Limit
        if ( !cdb.ReadDouble(pvLow, "LOW", pvLopr)) {
        	AssertErrorCondition(Information,
        			"EPICSHandler::ObjectLoadSetup: %s: LOW not specified. Assuming %f", 
        			Name(), pvLopr);
        }
        double pvLolo; //Lolo Alarm Limit
        if ( !cdb.ReadDouble(pvLolo, "LOLO", pvLopr)) {
        	AssertErrorCondition(Information,
        			"EPICSHandler::ObjectLoadSetup: %s: LOLO not specified. Assuming %f", 
        			Name(), pvLopr);
        }
         
        // menu alarm severity
        int32 pvHhsv; //Hihi Alarm Severity 
        if( !cdb.ReadOptions(pvHhsv, "HHSV", menuAlarmSevr_strings, menu_values, 0) ) {
        	AssertErrorCondition(Information, 
        			"EPICSHandler::ObjectLoadSetup: %s: HHSV not specified. Assuming %s",
        			Name(), menuAlarmSevr_strings[pvHhsv] );
        }
        int32 pvHsv; //High Alarm Severity 
        if( !cdb.ReadOptions(pvHsv, "HSV", menuAlarmSevr_strings, menu_values, 0) ) {
        	AssertErrorCondition(Information, 
        			"EPICSHandler::ObjectLoadSetup: %s: HSV not specified. Assuming %s",
        			Name(), menuAlarmSevr_strings[pvHsv] );
        }     
        int32 pvLsv; //Low Alarm Severity
        if( !cdb.ReadOptions(pvLsv, "LSV", menuAlarmSevr_strings, menu_values, 0) ) {
        	AssertErrorCondition(Information,
        			"EPICSHandler::ObjectLoadSetup: %s: LSV not specified. Assuming %s", 
        			Name(), menuAlarmSevr_strings[pvLsv] );
        }
        int32 pvLlsv; //Lolo Alarm Severity
        if( !cdb.ReadOptions(pvLlsv, "LLSV", menuAlarmSevr_strings, menu_values, 0) ) {
        	AssertErrorCondition(Information,
        			"EPICSHandler::ObjectLoadSetup: %s: LLSV not specified. Assuming %s", 
        			Name(), menuAlarmSevr_strings[pvLlsv] );
        }      
        
        // hysteresis/deadband values
        double pvHyst; // Hysteresis Alarm deadband
        if ( !cdb.ReadDouble(pvHyst, "HYST", DEFAULT_EPICS_HYST)) {
        	AssertErrorCondition(Information,
        			"EPICSHandler::ObjectLoadSetup: %s: HYST not specified. Assuming %f", 
        			Name(), pvHyst);
        } 
        double pvAdel; // Archive deadband
        if ( !cdb.ReadDouble(pvAdel, "ADEL", DEFAULT_EPICS_ADEL)) {
        	AssertErrorCondition(Information,
        			"EPICSHandler::ObjectLoadSetup: %s: ADEL not specified. Assuming %f", 
        			Name(), pvAdel);
        }
        double pvMdel; // Monitor deadband
        if ( !cdb.ReadDouble(pvMdel, "MDEL", DEFAULT_EPICS_MDEL)) {
        	AssertErrorCondition(Information,
        			"EPICSHandler::ObjectLoadSetup: %s: MDEL not specified. Assuming %f", 
        			Name(), pvMdel);
        }         
        
        // read precision (realted only to ai/ao records)
        int32 pvPrec;
        if ( !cdb.ReadInt32(pvPrec, "PREC", DEFAULT_EPICS_PREC) ) {
           	AssertErrorCondition(Information,
           			"EPICSHandler::ObjectLoadSetup: %s: PREC not specidfied. Assuming %d",
           			Name(), pvPrec);
        }
        
        //--------------------------------------------------------------------- end of EPICS stuff
        
        
        // we have to allocate the memory for the pv's name (TODO modify pvInfo or dealloc)
        //FString * pvNameObj = new FString(pvName); //TODO deallocate this space -> DONE
        //FString * pvUnitsObj = new FString(pvUnits); //TODO deallocate this space -> DONE
        // create the pv's descriptor pvInfo
        pvInfo *pPVI = new pvInfo(pvScanPeriod, // scan period
        		pvName.Buffer(), // name of the pv (will be copied)
        		pvUnits.Buffer(), // units of the pv (will be copied)
        		(aitFloat32) pvHopr, (aitFloat32) pvLopr, // HOPR and LOPR, gr=graphical
        		pvHihi, pvHigh, pvLow, pvLolo,
        		aitType, excasSync, pvElements);
        if ( !pPVI ) {
        	AssertErrorCondition(InitialisationError,
            		"EPICSHandler::ObjectLoadSetup: %s: cannot create pvInfo structure for %s. Fatal error!",
            		Name(), pvName.Buffer() );
            return False;
        }
        // create the real process variable
        exPV * pPV = pPVI->createPV(*pCAS, true, scanOn, asyncDelay);
        if (!pPV) {
          	AssertErrorCondition(InitialisationError,
                		"EPICSHandler::ObjectLoadSetup: %s: cannot create exPV structure for %s. Fatal error!",
                		Name(), pPVI->getName() );
          	return False;
        }
        
        // Install canonical (root) name
        char pvAlias[256];
        const char * const pNameFmtStr = "%.100s%.20s";
        sprintf(pvAlias, pNameFmtStr, pvPrefix.Buffer(), pPVI->getName());
        pCAS->installAliasName(*pPVI, pvAlias);
        
        // add the process variable to the list by copy and delete the current object
        pvList[i] = pPVI; // copy the process variable to the list (by pointer)
        //--------------------------------------------------------------------- end of EPICS stuff
        
             
        cdb->MoveToFather();
    } // PVs loop

    cdb->MoveToFather();
    
    // Publish Subscribe Interface(TODO) stuff
    if ( subList )
    	delete subList;
    subList = new subscriber [numberOfPVs];
    if ( !subList ) {
       	AssertErrorCondition(InitialisationError,
                		"EPICSHandler::ObjectLoadSetup: %s: cannot create subList. Fatal error!",
                		Name() );
    		return False;    	
    }
    // TODO is this the correct way to initialize the array?!
    memset ( subList, 0, (numberOfPVs * sizeof(subscriber)) );

    // preparing the buffering infrastructure - this part will be moved in the abstract class
    // in the Process Variable loop we can calculate the minimum size required to work
    unsigned _size = buffer_size % buffer_align;
    if ( _size ) {
    	buffer_size += buffer_align - _size;
       	AssertErrorCondition(Information,
       			"EPICSHandler::ObjectLoadSetup: %s: circular buffer resized to %x (aligned)",
       			Name(), buffer_size);
    }
    // check if buffer was previously allocated
    if ( buffer_ptr )
    	delete [] buffer_ptr;
    buffer_ptr = new char [buffer_size];
    if ( !buffer_ptr ) {
      	AssertErrorCondition(InitialisationError,
            	"EPICSHandler::ObjectLoadSetup: %s: cannot allocate space for the circular buffer. Fatal error!",
            	Name() );
		return False;
    }
    // set head and tail and free bytes    
    buffer_head	= (cbHeader * ) buffer_ptr;
    buffer_tail = (cbHeader * ) buffer_ptr;
    buffer_free = buffer_size; 

// ***** circular buffer rules *****
    // head \E8 sempre in una posizione libera (quando e full?)
    // tail \E8 sempre nel primo elemento da mangiare tranne quando il ciruclar buffer e vuoto
    
    
    // check if the threads are already running: kill them and then reload them
    // TODO
    // in a single MARTe instance must exists only one instance of the "callback" thread
    // but you must register one instance of "callback_event" for every instance of this class
    
    
    // create the asynchronous EPICS thread --> from EPICS to MARTe
    callback_finalize++;
	threadID = Threads::BeginThread( (ThreadFunctionType) callback,
												pCAS, THREADS_DEFAULT_STACKSIZE,
												"EPICSHandler", XH_NotHandled, cpuMask);
	if ( !threadID ) {
      	AssertErrorCondition(InitialisationError,
            		"EPICSHandler::ObjectLoadSetup: %s: cannot create EpicsThreadCallback. Fatal error!",
            		Name() );
		return False;
	}
/*    AssertErrorCondition(Information,
    		"EPICSHandler::ObjectLoadSetup: %s: Service started with tid 0x%08x.",
    		Name(), threadID);
*/
    // create the asynchronous MARTe thread --> from MARTe to EPICS
	callback_event_finalize++;
	threadID_event = Threads::BeginThread( (ThreadFunctionType) callback_event,
												this, THREADS_DEFAULT_STACKSIZE,
												"EPICSHandler_events", XH_NotHandled, cpuMask_event);
	if ( !threadID_event ) {
      	AssertErrorCondition(InitialisationError,
            		"EPICSHandler::ObjectLoadSetup: %s: cannot create eventBufferCallback. Fatal error!",
            		Name() );
		return False;
	}
	
/*    AssertErrorCondition(Information,
    		"EPICSHandler::ObjectLoadSetup: %s: Service started with tid 0x%08x.",
    		Name(), threadID_event);
*/
	setup_complete = true;
    AssertErrorCondition(Information,
    		"EPICSHandler::ObjectLoadSetup: %s: Loaded %d Process Variables.",
    		Name(), numberOfPVs);
    return True;
}
//----------------------------------------------------------------------------- end ObjectLoadSetup

// TODO use CDBExtended::ReadOptions instead
aitEnum EPICSHandler::ConvertToaitEnum(const char * s) {
	int i;
	for (i=0; i<(sizeof(aitEnum_strings)/sizeof(char*)); i++ ) {
		if ( strcmp(aitEnum_strings[i], s)==0 )
		//if ( strcasecmp(aitEnum_string[i], s)==0 )
			return (aitEnum)i;
	}
	return aitEnumInvalid; //on error returns 0 (aitEnumInvalid)
}
//----------------------------------------------------------------------------- end ConvertToaitEnum

// TODO use CDBExtended::ReadOptions instead
excasIoType EPICSHandler::ConvertToexcasIoType(const char * s) {
	int i;
	for (i=0; i<(sizeof(excasIoType_strings)/sizeof(char*)); i++ ) {
		if ( strcmp(excasIoType_strings[i], s)==0 )
		//if ( strcasecmp(aitEnum_string[i], s)==0 )
			return (excasIoType)i;
	}
	return excasIoSync; //on error returns 0 (excasIoSync)
}
//----------------------------------------------------------------------------- end ConvertToexcasIoType

int EPICSHandler::callback_finalize = 0;
void EPICSHandler::callback (void * args) {	
	
	//osiTime delay(1000u, 0u);
	// TODO ?
	
	while( callback_finalize )
		fileDescriptorManager.process(0.1); // double seconds (timeout)
}
//----------------------------------------------------------------------------- end EPICSCallback


#define SYNCHRONIZING
int EPICSHandler::callback_event_finalize= 0;
void EPICSHandler::callback_event (void * args) {	
	unsigned id;
	unsigned timestamp;
	int _ret;
	EPICSHandler * THIS = static_cast<EPICSHandler *>(args);
	
	epicsTimeStamp current = epicsTime::getCurrent();
	
	while( callback_event_finalize ) {
		// if there are no subscribers wait a bit and continue loop
		if ( !(THIS->subListSize) ) {
			SleepMsec(100);
			continue;
		}

		// collect data from the producer
		_ret = THIS->get ( id, THIS->subBuffer, THIS->subSize, timestamp);
		if ( _ret == 0 ) // no data, no problem
			continue;
		if ( _ret == -1 ) { // error
			CStaticAssertErrorCondition(FatalError,
					"EPICSHandler::callback_event: thread: get returns %d",
					_ret );
			// TODO read buffer_err to write which kind of error happens
			break; }
		// check id
		if ( !(id < THIS->numberOfPVs) ) { // error again
			CStaticAssertErrorCondition(Warning,
					"EPICSHandler::callback_event: thread: id %d not in PVs range (0..%d)",
					id, THIS->numberOfPVs);
			continue; }
		
		// conversion is done by EPICS side (i.e. GDD)
		smartGDDPointer pDD;
		
		switch ( (THIS->subList[id].type).Type() ) {
		case BTDTInteger:
			//TODO switch on flag
			switch ( (THIS->subList[id].type).BitSize() ) {
/* not supported by the DDB
 * 			case 8:
				if ( (THIS->subList[id].count) == 1 ) // scalar
        			pDD = new gddScalar (gddAppType_value, aitEnumInt8);
        		else // --------------------------- vector
        			pDD = new gddAtomic (gddAppType_value, aitEnumInt8, 1u, THIS->subList[id].count);	        		
        		break;
			case 16:
	        	if ( (THIS->subList[id].count) == 1 ) // scalar
	        		pDD = new gddScalar (gddAppType_value, aitEnumInt16);
	        	else // --------------------------- vector
	        		pDD = new gddAtomic (gddAppType_value, aitEnumInt16, 1u, THIS->subList[id].count);	        		
				break;
*/
			case 32:
				switch ( (THIS->subList[id].type).Flags() ) {
				case BTDSTUnsigned:
		        	if ( (THIS->subList[id].count) == 1 ) // scalar
		        		pDD = new gddScalar (gddAppType_value, aitEnumUint32);
		        	else // --------------------------- vector
		        		pDD = new gddAtomic (gddAppType_value, aitEnumUint32, 1u, THIS->subList[id].count);
		        	break;
				case BTDSTNone:
					if ( (THIS->subList[id].count) == 1 ) // scalar
						pDD = new gddScalar (gddAppType_value, aitEnumInt32);
					else // --------------------------- vector
						pDD = new gddAtomic (gddAppType_value, aitEnumInt32, 1u, THIS->subList[id].count);
					break;
				default:
				   	BString bsbuf;
				   	CStaticAssertErrorCondition(Warning,
							"EPICSHandler::callback_event: thread: BTDTInteger, %s BasicTypeDescriptor not supported for id %d (%s) (32bit)",
							(THIS->subList[id].type).ConvertToString(bsbuf), id,
							(THIS->subList[id].count > 1) ? "array" : "scalar" );
				}
				break;
			default:
				// in this case it is EPICS that does not support Int64 (MARTe supports it already)
			   	BString bsbuf;
			   	CStaticAssertErrorCondition(Warning,
						"EPICSHandler::callback_event: thread: BTDTInteger, %s BasicTypeDescriptor not supported for id %d (%s)",
						(THIS->subList[id].type).ConvertToString(bsbuf), id,
						(THIS->subList[id].count > 1) ? "array" : "scalar" );
			}
			break;
		case BTDTFloat:
			switch ( (THIS->subList[id].type).BitSize() ) {
			case 32:
				if ( (THIS->subList[id].count) == 1 ) // scalar
					pDD = new gddScalar (gddAppType_value, aitEnumFloat32);
	        	else // --------------------------- vector
	        		pDD = new gddAtomic (gddAppType_value, aitEnumFloat32, 1u, THIS->subList[id].count);
				break;
			case 64:
				if ( (THIS->subList[id].count) == 1 ) // scalar
					pDD = new gddScalar (gddAppType_value, aitEnumFloat64);
	        	else // --------------------------- vector
	        		pDD = new gddAtomic (gddAppType_value, aitEnumFloat64, 1u, THIS->subList[id].count);
				break;
			default:
	        	BString bsbuf;
				CStaticAssertErrorCondition(Warning,
		    			"EPICSHandler::callback_event: thread: BTDTFloat, %s BasicTypeDescriptor not supported for id %d (%s)",
						(THIS->subList[id].type).ConvertToString(bsbuf), id,
						(THIS->subList[id].count > 1) ? "array" : "scalar" );
			}
			break;
		default:
			// error not supported by the driver
        	BString bsbuf;
			CStaticAssertErrorCondition(Warning,
	    			"EPICSHandler::callback_event: thread: %s BasicTypeDescriptor not supported for id %d (%s)",
					(THIS->subList[id].type).ConvertToString(bsbuf), id,
					(THIS->subList[id].count > 1) ? "array" : "scalar" );
			continue;
		} //------------------------------------------------------------------- end switch ( (THIS->subList[id].type).Type() )

		if ( !pDD.valid() ) {
			CStaticAssertErrorCondition(Warning,
					"EPICSHandler::callback_event: thread: pDD not valid cannot continue (id is %d, ->count is %d)",
					id, THIS->subList[id].count);
			continue;
		}
		
	    gddStatus gdds= pDD->unreference ();
	    if ( gdds )  {// assert ( ! gddStatus );
	    	CStaticAssertErrorCondition(Warning,
	    			"EPICSHandler::callback_event: thread: assert (!gddStatus) cannot continue"
	    			);
	    	continue;
		}

	    // copy data scalar or array 
		switch ( (THIS->subList[id].type).Type() ) {
		case BTDTInteger:
			switch ( (THIS->subList[id].type).BitSize() ) {
/* THE FOLLOWINGS ARE NOT SUPPORTED BY the DDB :-(
 * 			case 8:
        		if ( (THIS->subList[id].count) == 1 ) // scalar
        			pDD->put( (aitInt8) * (aitInt8 *)(THIS->subBuffer) );
        		else { //-------------------------- vector
	        		aitInt8 * pI8 = new aitInt8 [THIS->subList[id].count];
	        		if ( !pI8 )
	        			goto assert_allocation_error;
	        		// create destructor
	        		int8Destructor * pBTDFd = new int8Destructor;
	        		if ( !pBTDFd ) {
	        			delete [] pI8;
	        			goto assert_destructor_error;
	        		}
	        		// set the data
	        		pDD->putRef (pI8, pBTDFd);
	        		// copy array data
	        		memcpy(pI8, THIS->subBuffer, THIS->subList[id].size);	        			        			
	        	}  		
        		break;
			case 16:
        		if ( (THIS->subList[id].count) == 1 ) // scalar
        			pDD->put( (aitInt16) * (aitInt16 *)(THIS->subBuffer) );
        		else { //-------------------------- vector
	        		aitInt16 * pI16 = new aitInt16 [THIS->subList[id].count];
	        		if ( !pI16 )
	        			goto assert_allocation_error;
	        		// create destructor
	        		int16Destructor * pBTDFd = new int16Destructor;
	        		if ( !pBTDFd ) {
	        			delete [] pI16;
	        			goto assert_destructor_error;
	        		}
	        		// set the data
	        		pDD->putRef (pI16, pBTDFd);
	        		// copy array data
	        		memcpy(pI16, THIS->subBuffer, THIS->subList[id].size);	        			        			
	        	}  		
        		break;
        		*/        		
			case 32:
				switch ( (THIS->subList[id].type).Flags() ) {
				case BTDSTUnsigned:
	        		if ( (THIS->subList[id].count) == 1 ) // scalar
	        			pDD->put( (aitUint32) * (aitUint32 *)(THIS->subBuffer) );
	        		else { //-------------------------- vector
		        		aitUint32 * pI32 = new aitUint32 [THIS->subList[id].count];
		        		if ( !pI32 )
		        			goto assert_allocation_error;
		        		// create destructor
		        		int32Destructor * pBTDFd = new int32Destructor;
		        		if ( !pBTDFd ) {
		        			delete [] pI32;
		        			goto assert_destructor_error;
		        		}
		        		// set the data
		        		pDD->putRef (pI32, pBTDFd);
		        		// copy array data
		        		memcpy(pI32, THIS->subBuffer, THIS->subList[id].size);	        			        			
		        	}  							
					break;
				case BTDSTNone:
	        		if ( (THIS->subList[id].count) == 1 ) // scalar
	        			pDD->put( (aitInt32) * (aitInt32 *)(THIS->subBuffer) );
	        		else { //-------------------------- vector
		        		aitInt32 * pI32 = new aitInt32 [THIS->subList[id].count];
		        		if ( !pI32 )
		        			goto assert_allocation_error;
		        		// create destructor
		        		int32Destructor * pBTDFd = new int32Destructor;
		        		if ( !pBTDFd ) {
		        			delete [] pI32;
		        			goto assert_destructor_error;
		        		}
		        		// set the data
		        		pDD->putRef (pI32, pBTDFd);
		        		// copy array data
		        		memcpy(pI32, THIS->subBuffer, THIS->subList[id].size);	        			        			
		        	}  		
					break;
				//default:
				}	
        		break;      
			//default:
			}
			break;
		case BTDTFloat:
			switch ( (THIS->subList[id].type).BitSize() ) {
			case 32:
        		if ( (THIS->subList[id].count) == 1 ) // scalar
        			pDD->put( (aitFloat32) * (aitFloat32 *)(THIS->subBuffer) );
        		else { //-------------------------- vector
	        		aitFloat32 * pF32 = new aitFloat32 [THIS->subList[id].count];
	        		if ( !pF32 )
	        			goto assert_allocation_error;
	        		// create destructor
	        		float32Destructor * pBTDFd = new float32Destructor;
	        		if ( !pBTDFd ) {
	        			delete [] pF32;
	        			goto assert_destructor_error;
	        		}
	        		// set the data
	        		pDD->putRef (pF32, pBTDFd);
	        		// copy array data
	        		memcpy(pF32, THIS->subBuffer, THIS->subList[id].size);	        			        			
	        	}  		
        		break;
			case 64:
        		if ( (THIS->subList[id].count) == 1 ) // scalar
        			pDD->put( (aitFloat64) * (aitFloat64 *)(THIS->subBuffer) );
        		else { //-------------------------- vector
	        		aitFloat64 * pF64 = new aitFloat64 [THIS->subList[id].count];
	        		if ( !pF64 )
	        			goto assert_allocation_error;
	        		// create destructor
	        		float64Destructor * pBTDFd = new float64Destructor;
	        		if ( !pBTDFd ) {
	        			delete [] pF64;
	        			goto assert_destructor_error;
	        		}
	        		// set the data
	        		pDD->putRef (pF64, pBTDFd);
	        		// copy array data
	        		memcpy(pF64, THIS->subBuffer, THIS->subList[id].size);	        			        			
	        	}  		
        		break;
			}
			break;
		default:
	        CStaticAssertErrorCondition(FatalError,
	        		"EPICSHandler::callback_event: static method: %d This point cannot be reached",
	        		id );
	        continue;
		}	
		goto update;
		
assert_allocation_error: {
		BString bsbuf;
		CStaticAssertErrorCondition(Warning,
				"EPICSHandler::callback_event: static method: assert array allocation error %s for id %d",
				(THIS->subList[id].type).ConvertToString(bsbuf), id );
		continue;
}
	
assert_destructor_error: {
		BString bsbuf;
		CStaticAssertErrorCondition(Warning,
				"EPICSHandler::callback_event: static method: assert array destructor error %s for id %d",
				(THIS->subList[id].type).ConvertToString(bsbuf), id );
		continue;
}
	
update:
		//convert to EPICS timestamp (DDB time is usec)
		long nsec = current.nsec + ((timestamp % 1000000) *1000);
		long sec = nsec / 1000000000;
		if (current.nsec < nsec)
			sec++;
		sec += current.secPastEpoch;
		//add timestamp
	 	aitTimeStamp gddts ( sec , nsec);
	 	pDD->setTimeStamp ( & gddts );

		// update value!
		//status = (pvList[id]->getPV())->update ( *pDD );
	 	//if ( !(THIS->pvList[id]->getScanPeriod() > 00) )
	 	(THIS->pvList[id]->getPV())->update ( *pDD,
	 			(THIS->pvList[id]->getScanPeriod() > 00) ? false : true,
	 			true );
	} //----------------------------------------------------------------------- end while ( callback_event_finalize )
}//---------------------------------------------------------------------------- end MARTeCallback


bool EPICSHandler::subscribe ( const char * nameIn, BasicTypeDescriptor typeIn, int countIn, unsigned &idOut) {
	if ( !setup_complete )
		return False;
	
	// qui e gia stato aggiunto sia sulla lista che sull'hash table..
	// ottimo per cerarlo by name e tornare l'id della lista.. (come ottenere l'id?)
    char pvAlias[256];
    const char * const pNameFmtStr = "%.100s%.20s";
    
    pvExistReturn _exist = (this->pCAS)->pvExistTest(nameIn);
    if ( _exist.getStatus() == pverDoesNotExistHere ) {
     	AssertErrorCondition(FatalError,
            		"EPICSHandler::subscribe: %s: Process Variable %s does NOT exist in the lookup table",
            		Name(), nameIn );
		return False;
    }
    
	idOut = -1;	
	for (int i=0; i<numberOfPVs; i++) {
		sprintf(pvAlias, pNameFmtStr, pvPrefix.Buffer(), pvList[i]->getName() );	
		if ( strcmp(nameIn, pvAlias) == 0) {//string match
			idOut = i;
			break;
		}
	}
	if (idOut == -1) // variable not found -> return false
		return False;

	// Process Variable already subscribed? -> error
	if ( subList[idOut].count != 0 ) {
     	AssertErrorCondition(ParametersError,
            		"EPICSHandler::subscribe: %s: Process Variable %s already subscribed",
            		 Name(), nameIn);
		return False;
	}
		
	// if typeIn has no type (i.e. BTDTNone) has no sense to subscribe on it or also to write it
	if ( typeIn == BTDTNone ) {
     	AssertErrorCondition(ParametersError,
            		"EPICSHandler::subscribe: %s: Process Variable %s type BTDTNone not valid!",
            		Name(), nameIn);
		return False;
	}
	
	// TODO the PV creation must be done here or in another place? avrebbe senso crearla
	// solo se qualcuno ci si iscrive..
	// altrimenti la registri cmoe eusbscirber e ne registri il tipo e quindi il numeo di byte da copiare
	//ricordiamoci che countin e il numero di elementi dell'array.
	// se facciamo che la PV viene creata solo se sottoscritta allora non serve avere una variabile 
	// per sapere se e sottoscritta (una sottoscrizione per variabile) se \E8 creata allora ok
	
	// check if the buffer has enough space to hold the data container (+ header)
	int _size = (typeIn.ByteSize() * countIn);
	if ( buffer_size < (sizeof(cbHeader) + _size) ) {
     	AssertErrorCondition(ParametersError,
            		"EPICSHandler::subscribe: %s: Process Variable %s circular buffer is too small!",
            		 Name(), nameIn);
		return False;
	}
	
	// check if the process variable has at least the number of elements of the DDB array
	// otherwise signal an error (EPICS code checks for that, so we maintain the same)
	if ( countIn > pvList[idOut]->getElementCount()  ) {
		AssertErrorCondition(ParametersError,
			   "EPICSHandler::subscribe: %s: Process Variable %s holds %d elements, less then %d required",
			   Name(), nameIn, pvList[idOut]->getElementCount(), countIn);
		return False;
	}
	
	subList[idOut].size = _size;
	subList[idOut].type = typeIn;
	subList[idOut].count = countIn;
	subList[idOut].statisticGet = 0;
	subList[idOut].statisticPut = 0;
	
	subListSize++;
	
	// if the buffer was allocated but is too small delete and allocate
	if (subSize < _size) {
		if ( subBuffer ) // consider when subBuffer is 0 (never allocated)
			delete subBuffer;
		subSize =_size;
		subBuffer = (char *) malloc (subSize);		
	}

	return True;
}
//----------------------------------------------------------------------------- end subscribe

bool EPICSHandler::unsubscribe (unsigned idIn) {
	if ( !(idIn < numberOfPVs) )
		return False; // index out of bounds
	
	// TODO vedi sopra se decidiamo di aggiungerla al server solo quando facciamo subscribe allora
	// qui in unsubscribe lo dobbiamo togliere dal server (che sarebbe la migliore idea)
	// per il momento il thread \E8 always running!
	if ( subList[idIn].count <= 0 ) {
     	AssertErrorCondition(ParametersError,
            		"EPICSHandler::subscribe: %s: Process Variable id %d is not subscribed!",
            		idIn, Name() );
		return False; // no one is subscribed
	}

	subList[idIn].type = BTDTNone;
	subList[idIn].count = 0;
	subListSize--;
	
	return True;
}
//----------------------------------------------------------------------------- end unsubscribe

// REALTIME - put -> head
int EPICSHandler::put (unsigned idIn, void * bufferIn, unsigned timestamp) {
	if ( !(idIn < numberOfPVs) )
		return -1; // index out of bounds
	if ( !bufferIn )
		return -1; // buffer is not a valid pointer
	
	int _size = subList[idIn].size + sizeof(cbHeader);
	if ( buffer_free < _size )
		return 0; // check if there is room for data
	                  // if there is no room the user can retry many times

	// copy data to circular buffer handling cross bounding
	cbHeader _tmpHeader = { CB_HEADER_MAGIC, idIn, timestamp};	
	long offset = (char *) buffer_head - buffer_ptr;
	int len = (buffer_size - offset);
	
	// write the header and data in the circular buffer
	if ( len < sizeof(cbHeader) ) {
		memcpy ((char *)buffer_head, (char *)&_tmpHeader, len ); // fill last bytes of the buffer
		buffer_head = (cbHeader *) buffer_ptr; // rewind the buffer
		memcpy ((char *)buffer_head, (((char *)&_tmpHeader) + len), sizeof(cbHeader) - len);
		buffer_head = (cbHeader *) (buffer_ptr + (sizeof(cbHeader) - len));
		
		// now I can copy the data , for sure not sliced
		memcpy((char*)buffer_head, bufferIn, subList[idIn].size);
		buffer_head = (cbHeader *)((char *)buffer_head + subList[idIn].size); // always subList .size
	}
	else {		
		memcpy ((char *)buffer_head, (char *)&_tmpHeader, sizeof(cbHeader) ); // fill last bytes of the buffer
		buffer_head = (cbHeader *) (((char *)buffer_head) + sizeof(cbHeader));
		if ( (len - sizeof(cbHeader)) == 0) {
			buffer_head = (cbHeader *)buffer_ptr; // rewind the buffer
			len = subList[idIn].size; 
		}
		else
			len -= sizeof(cbHeader);
		
		//check if buffer must be sliced
		if ( len < subList[idIn].size ) {
			memcpy ((char *)buffer_head, bufferIn, len ); // fill last bytes of the buffer
			buffer_head = (cbHeader *)buffer_ptr; // rewind the buffer
			memcpy ((char *)buffer_head, ((char *)bufferIn + len), (subList[idIn].size - len) );
			buffer_head = (cbHeader *)(buffer_ptr + (subList[idIn].size - len) );			
		}
		else {
			memcpy((char*)buffer_head, bufferIn, subList[idIn].size);
			buffer_head = (cbHeader *)((char *) buffer_head + subList[idIn].size);
		}
	}

	//buffer_free -= _size;
	Atomic::Sub(&buffer_free, _size);
	subList[idIn].statisticPut++;
	
	//synchronizza EPICS !! semaphore del get! (next version)
#ifdef SYNCHRONIZING
		sem.Post();
#endif
	
	return subList[idIn].size;
}
//----------------------------------------------------------------------------- end put

/*
 * you can use get with sizeIn 0 to query about the status of the first buffer
 * in the sense you want know the id to provide the correct buffer size
 * 
 * temporaneamente facciamo che:
 * idOut viene scritto dalla routine "get" bufferIn dev'essere valido
 * sizeIn != 0
 * l'idea e che esiste un'ulteriore routine che ti dice la size e l'id per il momento
 * e quindi passi il giusto buffer (con la giusta size) altrimenti
 * allochi un max buffer in subscribe.. or something similar.. cmq relato agli iscritti..
 * 
 * return the amount of bytes written - TODO make the same in method put
 * 
 * if size is less then the buffer just fill the buffer and move to the next buffer
 */
// REALTIME - get <- get
int EPICSHandler::get (unsigned &idOut, void * bufferIn, unsigned sizeIn, unsigned &timestamp) {
	if ( !bufferIn ) // buffer cannot be zero
		return -1;
	if ( !sizeIn ) // size cannot be zero
		return -1;

	// if there is no data in the buffer return 0
	if ( buffer_free == buffer_size )
#ifdef SYNCHRONIZING
		sem.Wait(); //ok, but we can also check for timeout here..
	if ( buffer_free == buffer_size )
		return 0;
#else
	{
		idOut = -1;
		return 0;
	}
#endif
	
	cbHeader _tmpHeader = {0, 0, 0};	
	long offset = (char *) buffer_tail - buffer_ptr;
	int len = (buffer_size - offset);
	int dataLen = 0;

	// fragmented header or payload around the circular buffer
	if (len < sizeof(cbHeader)) {
		memcpy ( (char *)&_tmpHeader, (char *)buffer_tail, len ); // fill last bytes of the buffer
		buffer_tail = (cbHeader *)buffer_ptr; // rewind the buffer
		memcpy ( (((char *)&_tmpHeader) + len), (char *)buffer_tail , (sizeof(cbHeader) - len) );
		buffer_tail = (cbHeader *)((char*)buffer_tail + (sizeof(cbHeader) - len) );
		
		//check data validity.. i.e. check MAGIC and id validity
		if ( (_tmpHeader.magic != CB_HEADER_MAGIC) )
			return -1; // corrupted buffer do nothing (nor realign or any thing else)
		if ( !(_tmpHeader.id < numberOfPVs) )  
			return -1; // corrupted id number cannot do nothing because cannot infere size
		idOut = _tmpHeader.id;
		timestamp = _tmpHeader.timestamp;
		
		//check how much data can be copied
		dataLen = (sizeIn < subList[idOut].size) ? sizeIn : subList[idOut].size;
		
		// now I can copy the data , for sure not sliced
		memcpy((char*)bufferIn, buffer_tail, dataLen);
		buffer_tail = (cbHeader *) ((char *)buffer_tail + subList[idOut].size);
	}
	else { // here at least the header is all together
		memcpy ((char *)&_tmpHeader, (char *)buffer_tail, sizeof(cbHeader) ); // fill last bytes of the buffer
		
		// check data validity.. MAGIC and id validity
		if ( (_tmpHeader.magic != CB_HEADER_MAGIC) )
			return -1; // corrupted buffer do nothing (nor realign or any thing else)
		if ( !(_tmpHeader.id < numberOfPVs) ) 
			return -1; // corrupted id number cannot do nothing because cannot infere size

		buffer_tail = (cbHeader *) ((char *)buffer_tail + sizeof(cbHeader));
		idOut = _tmpHeader.id;
		timestamp = _tmpHeader.timestamp;
		
		//check how much data can be copied
		dataLen = (sizeIn < subList[idOut].size) ? sizeIn : subList[idOut].size;		
		
		len -= sizeof(cbHeader);
		
		if ( len == 0) { // data is at the beginning of the data buffer
			memcpy(bufferIn, (char*)buffer_ptr, dataLen);
			buffer_tail = (cbHeader *)(buffer_ptr + subList[idOut].size);
		}
		else if ( len >= subList[idOut].size ) { // the data is contiguous
			memcpy(bufferIn, (char*)buffer_tail, dataLen);
			buffer_tail = (cbHeader *)((char *)buffer_tail + subList[idOut].size);
		}
		else { // segmented data
			memcpy (bufferIn, (char *)buffer_tail, (dataLen > len) ? len : dataLen ); // fill last bytes of the buffer
			buffer_tail = (cbHeader *)buffer_ptr; // rewind the buffer
			memcpy (((char*)bufferIn + len), (char *)buffer_tail, (dataLen > len) ? (dataLen - len) : 0);
			buffer_tail = (cbHeader *)(buffer_ptr + (subList[idOut].size -len));			
		}
	}

	//buffer_free += (sizeof(cbHeader) + subList[_tmpHeader.id].size); // one instruction beacause we hope to be atomic
	int _size = (sizeof(cbHeader) + subList[_tmpHeader.id].size); // one instruction beacause we hope to be atomic

/*	
	int32 * atomic_access = &buffer_free;
	int32 c, d;
	
	c = *atomic_access;
	d = Atomic::Exchange(atomic_access, (c+_size));
	if (c != d) {
		// something goes wrong
			d = Atomic::Exchange(atomic_access, (d+_size));
			c = (c+_size);
			if (c != d) {
				// something goes wrong again
				AssertErrorCondition(FatalError,
								"EPICSHandler::get: Circular Buffer Fatal error TWICE ERROR");
			}
			else
				AssertErrorCondition(FatalError,
								"EPICSHandler::get: Circular Buffer Fatal error ONE ERROR");
	}
*/
	Atomic::Add(&buffer_free, _size);
	subList[idOut].statisticGet++;

// comment non e proprio necessario, i.e. buffer_head/buffer_tail dovrebbero venir modificate in
	// nello stesso istante di buffer_free in modo tale che questo check funzioni
/*	
	char* a = (char*) buffer_head;
	int b = buffer_free;
	if ( ((((char*)buffer_tail -buffer_ptr)
			+ (buffer_size -b)) % buffer_size) !=
			(a -buffer_ptr) )
		AssertErrorCondition(FatalError,
				"EPICSHandler::get: Circular Buffer Fatal error _ptr %p, _head %p, _tail %p, size %d, free%d",
				buffer_ptr, a, buffer_tail, buffer_size, b);
*/
	
	// TODO invalidate cbHeader buffer
	//free the space and mark magic as not valid 0x0000
	
	return dataLen;
}
//----------------------------------------------------------------------------- end get

// TODO why not writing a buffer walker? :-)
		

const char* EPICSHandler::css = "table.bltable {"
	"margin: 1em 1em 1em 2em;"
	"background: whitesmoke;"
	"border-collapse: collapse;"
	"}"
	"table.bltable th, table.bltable td {"
	"border: 1px silver solid;"
	"padding: 0.2em;"
	"}"
	"table.bltable th {"
	"background: gainsboro;"
	"text-align: left;"
	"}"
	"table.bltable caption {"
	"margin-left: inherit;"
	"margin-right: inherit;"
	"}";
// ----------------------------------------------------------------------------

#define TABLE_NEWROW hStream.Printf("<tr>\n")
#define TABLE_ENDROW hStream.Printf("</tr>\n")

bool EPICSHandler::ProcessHttpMessage( HttpStream &hStream )
{
    hStream.SSPrintf("OutputHttpOtions.Content-Type","text/html");
    hStream.keepAlive = False;

    hStream.Printf("<html><head><title>%s</title>", Name());
    hStream.Printf( "<style type=\"text/css\">\n" );
    hStream.Printf("%s\n", css);
    hStream.Printf( "</style></head><body>\n" );
    
    //the following version numeration comes from
    //epics/base-3-14-11/src/cas/generic/caServerI.cc
    //EPICS/base-3-14-11/configure/CONFIG_BASE_VERSION
    //epics/base-3-14-11/src/misc/epicsRelease.c
    hStream.Printf("<h1> EPICSLib Version </h1>\n");
    hStream.Printf("Channel Access V%s\n", CA_VERSION_STRING ( CA_MINOR_PROTOCOL_REVISION ) );
    hStream.Printf("<BR>\n");
//    hStream.Printf("revision @(#) %s\n", EPICS_VERSION_STRING );
    hStream.Printf("%s\n", epicsReleaseVersion );
    hStream.Printf("<BR>\n");        

    hStream.Printf("<h1> EPICSLib Parameters </h1>\n");
    hStream.Printf("<table class=\"bltable\">\n");
    TABLE_NEWROW; hStream.Printf("<td> PV prefix </td> <td>%s</td>\n", pvPrefix.Buffer() ); TABLE_ENDROW;
    TABLE_NEWROW; hStream.Printf("<td> scan on </td> <td>%s</td>\n", (scanOn==true) ? "true" : "false"); TABLE_ENDROW; 
    TABLE_NEWROW; hStream.Printf("<td> async scan on </td> <td>%s</td>\n", (asyncScan==true) ? "true" : "false"); TABLE_ENDROW;
    TABLE_NEWROW; hStream.Printf("<td> async delay </td> <td>%lf</td>\n", asyncDelay); TABLE_ENDROW;
    TABLE_NEWROW; hStream.Printf("<td> max simultaneous async </td> <td>%d</td>\n", maxSimultAsyncIO); TABLE_ENDROW;
    TABLE_NEWROW; hStream.Printf("<td> PV number </td> <td>%d</td>\n", numberOfPVs); TABLE_ENDROW;
    hStream.Printf("</table>\n");
    hStream.Printf("<BR>\n");

    // per process variable description
    hStream.Printf("<h1> Detailed Process Variables description </h1>\n");
    hStream.Printf("<table class=\"bltable\">\n");
    // table header
	TABLE_NEWROW; hStream.Printf("<td>PV</td> <td>NAME</td> <td>EGU</td> <td>TYPE</td> <td>SYNC</td> "
			"<td>HOPR</td> <td>LOPR</td> <td>HIHI</td> <td>HIGH</td> <td>LOW</td> <td>LOLO</td> "
			"<td>LEN</td> <td>SCAN</td>\n"); 
	hStream.Printf("<td>PREC</td>"
			"<td>HHSV</td> <td>HSV</td> <td>LSV</td> <td>LLSV</td> "
			"<td>ACKS</td> <td>ACKT</td>");
	TABLE_ENDROW;	
    for (int pv=0; pv<numberOfPVs; pv++) {
    	// build the PV alias (prefix + name) 
        char pvAlias[256];
        const char * const pNameFmtStr = "%.100s%.20s";
        sprintf(pvAlias, pNameFmtStr, pvPrefix.Buffer(), pvList[pv]->getName());
    	// output the HTTP data 
    	TABLE_NEWROW;
    	hStream.Printf("<td>%s</td> <td>%s</td> <td>%s</td> <td>%s</td> <td>%s</td> " // pvAlias, pvName, pvUnits, pvType, pvSync
    			"<td>%lf</td> <td>%lf</td> <td>%lf</td> <td>%lf</td> <td>%lf</td> <td>%lf</td> " //hopr, lopr, hihi, high, low, lolo
    			"<td>%d</td>\n",
    			pvAlias,
    			pvList[pv]->getName(), pvList[pv]->getUnits(),  
    			aitEnum_strings[ (unsigned) pvList[pv]->getType() ],
    			excasIoType_strings[ (unsigned) pvList[pv]->getIOType() ],
    			pvList[pv]->getHopr(), pvList[pv]->getLopr(),
    			pvList[pv]->getHihi(), pvList[pv]->getHigh(),
    			pvList[pv]->getLow(), pvList[pv]->getLolo(),
    			pvList[pv]->getElementCount() );
    	
    	int index = 0;
    	do {
    		if ( menuScan_values[index] == pvList[pv]->getScanPeriod() )
    			break;
    		index++;
    	} while (menuScan_values[index] != 0);
    	if (menuScan_values[index] == 0)
    		index = 0;
    	hStream.Printf ("<td>%s</td>\n", menuScan_strings[index]);     			
/*    	TABLE_ENDROW;
    	// additional information regarding the same PV
    	TABLE_NEWROW;*/
    	hStream.Printf("<td>%d </td>" 
    			"<td>%s</td> <td>%s</td> <td>%s</td> <td>%s</td> " // - - hhsv hsv lsv llsv 
    			"<td>%s</td> <td>%s</td>\n", // acks ackt
    			pvList[pv]->prec,
    			menuAlarmSevr_strings[ pvList[pv]->hhsv ], menuAlarmSevr_strings[ pvList[pv]->hsv ],
    			menuAlarmSevr_strings[ pvList[pv]->lsv ], menuAlarmSevr_strings[ pvList[pv]->llsv ],
    			menuAlarmSevr_strings[ pvList[pv]->acks ], menuYesNo_strings [pvList[pv]->ackt ] );
    	TABLE_ENDROW;	

    }
    hStream.Printf("</table>\n");
    hStream.Printf("<BR>\n");

    
    // per process variable description
    hStream.Printf("<h1> Subscriber table (num of subscriber %d)</h1>\n", subListSize);
    hStream.Printf("<table class=\"bltable\">\n");
    // table header
    TABLE_NEWROW; hStream.Printf("<td>index</td> <td>elements</td> <td>element size</td> <td>DDB type</td> "
    		"<td>statistic Get</td> <td>statistic Put</td>"); TABLE_ENDROW;
    for (int si=0; si<numberOfPVs; si++) {
    	BString bsbuf;
    	
    	TABLE_NEWROW;
    	hStream.Printf("<td> %d</td> <td>%d </td><td> %d</td> <td>%s </td> <td>%d </td> <td>%d </td>",
    			si, subList[si].count, subList[si].size,
    			(subList[si].size == 0) ? "not assigned" : (subList[si].type).ConvertToString(bsbuf),
    			subList[si].statisticGet, subList[si].statisticPut);
    	TABLE_ENDROW;
    }
    hStream.Printf("</table>\n");
    hStream.Printf("<BR>\n");
    
   
    hStream.Printf("</body></html>");
    hStream.WriteReplyHeader(True);
    return True;
}
//----------------------------------------------------------------------------- end ProcessHttpMessage

OBJECTLOADREGISTER(EPICSHandler,"$Id: EPICSHandler.cpp,v 1.1 2011/06/17 14:19:12 abarb Exp $")
