/*************************************************************************\
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
*     National Laboratory.
* Copyright (c) 2002 The Regents of the University of California, as
*     Operator of Los Alamos National Laboratory.
* EPICS BASE Versions 3.13.7
* and higher are distributed subject to a Software License Agreement found
* in file LICENSE-EPICS that is included with this distribution. 
\*************************************************************************/
//
//  Example EPICS CA server
//
//
//  caServer
//  |
//  exServer
//
//  casPV
//  |
//  exPV-----------
//  |             |
//  exScalarPV    exVectorPV
//  |
//  exAsyncPV
//
//  casChannel
//  |
//  exChannel
//


//MARTE BAseLib
#include "System.h"
#include "BasicTypes.h"


//
// ANSI C
//
#include <string.h>
#include <stdio.h>

//
// EPICS
//
//#define epicsAssertAuthor "Jeff Hill johill@lanl.gov"
#include "gddAppFuncTable.h"
#include "smartGDDPointer.h"
#include "epicsTimer.h"
#include "casdef.h"
#include "epicsAssert.h"
#include "resourceLib.h"
#include "tsMinMax.h"

#ifndef NELEMENTS
#   define NELEMENTS(A) (sizeof(A)/sizeof(A[0]))
#endif

//
// info about all pv in this server
//
enum excasIoType { excasIoSync, excasIoAsync };

class exPV;
class exServer;

//
// pvInfo 
// 
class pvInfo {
public: 
        
    pvInfo ( double scanPeriodIn, const  char * pNameIn, const char * pUnitsIn, 
        aitFloat32  hoprIn, aitFloat32  loprIn,
        double hihiIn, double highIn, double lowIn, double loloIn,
        aitEnum typeIn, excasIoType ioTypeIn, unsigned  countIn );
    
    pvInfo ( double scanPeriodIn, const  char * pNameIn, const char * pUnitsIn, 
        aitFloat32  hoprIn, aitFloat32  loprIn,
        double hihiIn, double highIn, double lowIn, double loloIn,
        aitEnum hhsvIn, aitEnum llsvIn, aitEnum hsvIn, aitEnum lsvIn,
        /*double lalmIn,*/ double hystIn,
        /*double alstIn,*/ double adelIn, /*double mlstIn,*/ double mdelIn,
        aitEnum acksIn, aitEnum acktIn, int precIn,
        aitEnum typeIn, excasIoType ioTypeIn, unsigned countIn );
    
    pvInfo (  const pvInfo  & copyIn  );
    ~pvInfo ();
    double getScanPeriod () const; 
    const  char * getName () const;
    const  char * getUnits () const; 
    double  getHopr () const; 
    double getLopr () const; 
    double  getHihi () const; 
    double getHigh () const; 
    double  getLow () const; 
    double getLolo () const; 
    aitEnum getType () const; 
    
    excasIoType  getIOType () const; 
    unsigned getElementCount () const; 
    void unlinkPV (); 
    exPV *createPV  ( exServer &  exCAS, bool  preCreateFlag, 
        bool  scanOn, double  asyncDelay ); 
    void deletePV ();
    exPV *getPV () { return pPV; };
    
  //  char * buffer;
  //  BasicTypeDescriptor btd;
    
private:
    const double scanPeriod;
    aitEnum scan;
    
    // TODO devono essere allocati qui: pvInfo e responsabile della loro creazione e distruzione
    // poteva andare bene per reference nell'implementazione precedente se erano statici a livello di routine
    // ma ora non č pių cosė..
     char * pName;
     char * pUnits;
     char * pDesc;
    
    const double hopr;
    const double lopr;
    
    const double hihi;
    const double high;
    const double low;
    const double lolo;

public:
    aitEnum hhsv;
    aitEnum llsv;
    aitEnum hsv;
    aitEnum lsv;
    
    double lalm; // last alarm value
    double hyst; // hysteresis value
    double alst; // last archived value
    double adel; // archive deadband
    double mlst; // last monitored value
    double mdel; // monitor deadband
    
    aitEnum acks;
    aitEnum ackt; // YESorNO
    
    int prec;
        
private:    
    aitEnum type;
    const excasIoType ioType;
    const unsigned elementCount;
    exPV * pPV;
    pvInfo & operator = ( const pvInfo & );
};

//
// pvEntry 
//
// o entry in the string hash table for the pvInfo
// o Since there may be aliases then we may end up
// with several of this class all referencing 
// the same pv info class (justification
// for this breaking out into a seperate class
// from pvInfo)
//
class pvEntry // X aCC 655
    : public stringId, public tsSLNode < pvEntry > {
public:
    pvEntry ( pvInfo  &infoIn, exServer & casIn, 
            const char * pAliasName );
    ~pvEntry();
    pvInfo & getInfo() const { return this->info; }
    void destroy ();

private:
    pvInfo & info;
    exServer & cas;
    pvEntry & operator = ( const pvEntry & );
    pvEntry ( const pvEntry & );
};


//
// exPV
//
class exPV : public casPV, public epicsTimerNotify, 
    public tsSLNode < exPV > {
public:
    exPV ( exServer & cas, pvInfo & setup, 
        bool preCreateFlag, bool scanOn );
    virtual ~exPV();

    void show ( unsigned level ) const;

    //
    // Called by the server libary each time that it wishes to
    // subscribe for PV the server tool via postEvent() below.
    //
    caStatus interestRegister ();

    //
    // called by the server library each time that it wishes to
    // remove its subscription for PV value change events
    // from the server tool via caServerPostEvents()
    //
    void interestDelete ();

    aitEnum bestExternalType () const;

    //
    // chCreate() is called each time that a PV is attached to
    // by a client. The server tool must create a casChannel object
    // (or a derived class) each time that this routine is called
    //
    // If the operation must complete asynchronously then return
    // the status code S_casApp_asyncCompletion and then
    // create the casChannel object at some time in the future
    //
    //casChannel *createChannel ();

    //
    // This gets called when the pv gets a new value
    //
    caStatus update ( const gdd & , bool processEvent, bool updateValue);

    //
    // Gets called when we add noise to the current value
    //
    virtual void scan () = 0;
    
    //
    // If no one is watching scan the PV with 10.0
    // times the specified period
    //
    double getScanPeriod ();

    caStatus read ( const casCtx &, gdd & protoIn );

    caStatus readNoCtx ( smartGDDPointer pProtoIn );

    caStatus write ( const casCtx &, const gdd & value );

    void destroy ();

    const pvInfo & getPVInfo ();

    const char * getName() const;
    
    
    caStatus putAckt ( const gdd & valueIn ); 
    caStatus putAcks ( const gdd & valueIn );
    

    static void initFT();

    casChannel * createChannel ( const casCtx &ctx,
        const char * const pUserName, 
        const char * const pHostName );

protected:
    smartGDDPointer pValue;
    exServer & cas;
    epicsTimer & timer;
    pvInfo & info; 
    bool interest;
    bool preCreate;
    bool scanOn;
    static epicsTime currentTime;

    virtual caStatus updateValue ( const gdd & ) = 0;

private:

    //
    // scan timer expire
    //
    expireStatus expire ( const epicsTime & currentTime );

    //
    // Std PV Attribute fetch support
    //
    gddAppFuncTableStatus getPrecision(gdd &value);
    gddAppFuncTableStatus getHighLimit(gdd &value);
    gddAppFuncTableStatus getLowLimit(gdd &value);
    gddAppFuncTableStatus getHighAlarm ( gdd & value );
    gddAppFuncTableStatus getLowAlarm ( gdd & value );
    gddAppFuncTableStatus getHighWarning ( gdd & value );
    gddAppFuncTableStatus getLowWarning ( gdd & value );
    gddAppFuncTableStatus getUnits(gdd &value);
    gddAppFuncTableStatus getValue(gdd &value);
    gddAppFuncTableStatus getEnums(gdd &value);
    gddAppFuncTableStatus getAckt (gdd & prec);
    gddAppFuncTableStatus getAcks (gdd & prec); 
  //  gddAppFuncTableStatus getTimeStamp (gdd & prec);
    
    
    
    exPV & operator = ( const exPV & );
    exPV ( const exPV & );

    //
    // static
    //
    static gddAppFuncTable<exPV> ft;
    static char hasBeenInitialized;
};

//
// exScalarPV
//
class exScalarPV : public exPV {
public:
    exScalarPV ( exServer & cas, pvInfo &setup, 
        bool preCreateFlag, bool scanOnIn ) :
        exPV ( cas, setup, 
            preCreateFlag, scanOnIn) {}
    void scan();
private:
    caStatus updateValue ( const gdd & );
    exScalarPV & operator = ( const exScalarPV & );
    exScalarPV ( const exScalarPV & );
};

//
// exVectorPV
//
class exVectorPV : public exPV {
public:
    exVectorPV ( exServer & cas, pvInfo &setup, 
        bool preCreateFlag, bool scanOnIn ) :
        exPV ( cas, setup, 
            preCreateFlag, scanOnIn) {}
    void scan();

    unsigned maxDimension() const;
    aitIndex maxBound (unsigned dimension) const;

private:
    caStatus updateValue ( const gdd & );
    exVectorPV & operator = ( const exVectorPV & );
    exVectorPV ( const exVectorPV & );
};

//
// exServer
//
class exServer : private caServer {
public:
    //we have removed the unneeded parameters
    /*exServer ( const char * const pvPrefix, 
        unsigned aliasCount, bool scanOn,
        bool asyncScan, double asyncDelay,
        unsigned maxSimultAsyncIO );
    */
    exServer (bool scanOn,
        bool asyncScan, double asyncDelay,
        unsigned maxSimultAsyncIO );
    ~exServer ();
    void show ( unsigned level ) const;
    void removeIO ();
    
    void removeAliasName ( pvEntry & entry );
    // todo change return type to integer o pvEntry.. dato che serve in removeAliasName..
    void installAliasName ( pvInfo & info, const char * pAliasName );

    class epicsTimer & createTimer ();
	void setDebugLevel ( unsigned level );

    void destroyAllPV ();
    
    unsigned maxSimultAsyncIO () const;

    
    pvExistReturn pvExistTest ( const casCtx &, 
        const caNetAddr &, const char * pPVName );
    pvExistReturn pvExistTest ( const casCtx &, 
        const char * pPVName );
//    pvExistReturn pvExistTest ( const char * pPVName ); // used by the prototype
    pvExistReturn pvExistTest (const char * pPVName );
    
    
private:
    resTable < pvEntry, stringId > stringResTbl;
    epicsTimerQueueActive * pTimerQueue;
    
    // TODO add eventQueue
    
    unsigned simultAsychIOCount;
    const unsigned _maxSimultAsyncIO;
    double asyncDelay;
    bool scanOn;
    
    pvAttachReturn pvAttach ( const casCtx &, 
        const char * pPVName );

    exServer & operator = ( const exServer & );
    exServer ( const exServer & );

/*   
    //
    // list of pre-created PVs
    //
    static pvInfo pvList[];
    static const unsigned pvListNElem;
    
    //
    // on-the-fly PVs 
    //
    static pvInfo bill;
    static pvInfo billy;
    static pvInfo bloater;
    static pvInfo bloaty;
    static pvInfo boot;
    static pvInfo booty;
*/
};

//
// exAsyncPV
//
class exAsyncPV : public exScalarPV {
public:
    exAsyncPV ( exServer & cas, pvInfo &setup, 
        bool preCreateFlag, bool scanOnIn, double asyncDelay );
    caStatus read ( const casCtx & ctxIn, gdd & protoIn );
    caStatus write ( const casCtx & ctxIn, const gdd & value );
    caStatus writeNotify ( const casCtx & ctxIn, const gdd & value );
    void removeReadIO();
    void removeWriteIO();
    caStatus updateFromAsyncWrite ( const gdd & );
private:
    double asyncDelay;
    smartConstGDDPointer pStandbyValue;
    unsigned simultAsychReadIOCount;
    unsigned simultAsychWriteIOCount;
    exAsyncPV & operator = ( const exAsyncPV & );
    exAsyncPV ( const exAsyncPV & );
};

//
// exChannel
//
class exChannel : public casChannel{
public:
    exChannel ( const casCtx & ctxIn );
    void setOwner ( const char * const pUserName, 
        const char * const pHostName );
    bool readAccess () const;
    bool writeAccess () const;
private:
    exChannel & operator = ( const exChannel & );
    exChannel ( const exChannel & );
};

//
// exAsyncWriteIO
//
class exAsyncWriteIO : public casAsyncWriteIO, public epicsTimerNotify {
public:
    exAsyncWriteIO ( exServer &, const casCtx & ctxIn, 
            exAsyncPV &, const gdd &, double asyncDelay );
    ~exAsyncWriteIO ();
private:
    exAsyncPV & pv;
    epicsTimer & timer;
    smartConstGDDPointer pValue;
    expireStatus expire ( const epicsTime & currentTime );
    exAsyncWriteIO & operator = ( const exAsyncWriteIO & );
    exAsyncWriteIO ( const exAsyncWriteIO & );
};

//
// exAsyncReadIO
//
class exAsyncReadIO : public casAsyncReadIO, public epicsTimerNotify {
public:
    exAsyncReadIO ( exServer &, const casCtx &, 
            exAsyncPV &, gdd &, double asyncDelay );
    virtual ~exAsyncReadIO ();
private:
    exAsyncPV & pv;
    epicsTimer & timer;
    smartGDDPointer pProto;
    expireStatus expire ( const epicsTime & currentTime );
    exAsyncReadIO & operator = ( const exAsyncReadIO & );
    exAsyncReadIO ( const exAsyncReadIO & );
};

//
// exAsyncExistIO
// (PV exist async IO)
//
class exAsyncExistIO : public casAsyncPVExistIO, public epicsTimerNotify {
public:
    exAsyncExistIO ( const pvInfo & pviIn, const casCtx & ctxIn,
            exServer & casIn );
    virtual ~exAsyncExistIO ();
private:
    const pvInfo & pvi;
    epicsTimer & timer;
    exServer & cas;
    expireStatus expire ( const epicsTime & currentTime );
    exAsyncExistIO & operator = ( const exAsyncExistIO & );
    exAsyncExistIO ( const exAsyncExistIO & );
};

 
//
// exAsyncCreateIO
// (PV create async IO)
//
class exAsyncCreateIO : public casAsyncPVAttachIO, public epicsTimerNotify {
public:
    exAsyncCreateIO ( pvInfo & pviIn, exServer & casIn, 
        const casCtx & ctxIn, bool scanOnIn, double asyncDelay );
    virtual ~exAsyncCreateIO ();
private:
    pvInfo & pvi;
    epicsTimer & timer;
    exServer & cas;
    double asyncDelay;
    bool scanOn;
    expireStatus expire ( const epicsTime & currentTime );
    exAsyncCreateIO & operator = ( const exAsyncCreateIO & );
    exAsyncCreateIO ( const exAsyncCreateIO & );
};

inline pvInfo::pvInfo ( double scanPeriodIn, const char *pNameIn, const char *pUnitsIn, 
    aitFloat32 hoprIn, aitFloat32 loprIn,
    double hihiIn, double highIn, double lowIn, double loloIn,
    aitEnum typeIn, excasIoType ioTypeIn, 
    unsigned countIn )
    /*,
    aitEnum hhsvIn, aitEnum llsvIn, aitEnum hsvIn, aitEnum lsvIn,
    double hystIn, double adelIn, double mdelIn) */:

    scanPeriod ( scanPeriodIn ), /*pName ( pNameIn ), pUnits ( pUnitsIn),*/
    hopr ( hoprIn ), lopr ( loprIn ),
    hihi (hihiIn), high (highIn), low (lowIn), lolo (loloIn),
    type ( typeIn ), 
    ioType ( ioTypeIn ), elementCount ( countIn ), 
    pPV ( 0 )
{
	// NAME
	if ( pNameIn ) {
		pName = new char[ strlen(pNameIn) ];
		if ( pName )
			memcpy (pName, pNameIn, strlen(pNameIn) +1 );
	}
	else 
		pName = 0;
	// EGU
	if ( pUnitsIn ) {
		pUnits = new char[ strlen(pUnitsIn) ];
		if ( pUnits )
			memcpy (pUnits, pUnitsIn, strlen(pUnitsIn) +1 );
	}
	else 
		pUnits = 0;
	
	//buffer = 0;
	//btd = 0;
	prec = 4;
	
	//have a look at dbStatic/alarm.h 
	/*epicsSevNone = NO_ALARM,
	    epicsSevMinor,
	    epicsSevMajor,
	    epicsSevInvalid,
	    ALARM_NSEV
	    */
	
	acks = (aitEnum) 0; // menuAlarmSevr start without alarms NO CONFIG
	ackt = (aitEnum) 0; // menuYesNo initial == YES (see menuYesNo.h in /include)  YES = 1
	
	// load it with default values, but there are no default values... ?!?!
	//menuAlarmSevr
	hhsv = (aitEnum) 2; // tobe configured - alarm
	llsv = (aitEnum) 2; // tobe configured - alarm
	hsv = (aitEnum) 1; // tobe configured - alarm
	lsv = (aitEnum) 1; // tobe configured - alarm
	    
	//all double
	lalm = 0.0 ;
	hyst = 0.0001; // tobe configured - alarm
	alst = 0.00;
	adel = 0.000000001; // tobe configured - log
	mlst = 0.00;
	mdel = 0.001; // tobe configured - value
}

inline pvInfo::pvInfo ( double scanPeriodIn, const  char * pNameIn, const char * pUnitsIn, 
	aitFloat32  hoprIn, aitFloat32  loprIn,
	double hihiIn, double highIn, double lowIn, double loloIn,
	aitEnum hhsvIn, aitEnum llsvIn, aitEnum hsvIn, aitEnum lsvIn,
	/*double lalmIn,*/ double hystIn,
	/*double alstIn,*/ double adelIn, /*double mlstIn,*/ double mdelIn,
	aitEnum acksIn, aitEnum acktIn, int precIn,
	aitEnum typeIn, excasIoType ioTypeIn, unsigned countIn ) :

	scanPeriod ( scanPeriodIn ), /*pName ( pNameIn ), pUnits ( pUnitsIn),*/
	hopr ( hoprIn ), lopr ( loprIn ),
	hihi (hihiIn), high (highIn), low (lowIn), lolo (loloIn),
	hhsv (hhsvIn), llsv (llsvIn), hsv (hsvIn), lsv (lsvIn),
	lalm ( 0.0 ), hyst (hystIn),
	alst ( 0.0 ), adel (adelIn), mlst ( 0.0 ), mdel (mdelIn),
	acks (acksIn), ackt (acktIn), prec (precIn),
	type ( typeIn ), ioType ( ioTypeIn ), elementCount ( countIn ), 
	pPV ( 0 )
{
	// NAME
	if ( pNameIn ) {
		pName = new char[ strlen(pNameIn) ];
		if ( pName )
			memcpy (pName, pNameIn, strlen(pNameIn) +1 );
	}
	else 
		pName = 0;
	// EGU
	if ( pUnitsIn ) {
		pUnits = new char[ strlen(pUnitsIn) ];
		if ( pUnits )
			memcpy (pUnits, pUnitsIn, strlen(pUnitsIn) +1 );
	}
	else 
		pUnits = 0;	

	// stuff to be removed
	//buffer = 0;
//	btd = 0;
}


//
// for use when MSVC++ will not build a default copy constructor 
// for this class
//
inline pvInfo::pvInfo ( const pvInfo & copyIn ) :

    scanPeriod ( copyIn.scanPeriod ), /*pName ( copyIn.pName ), pUnits ( copyIn.pUnits ),*/
    hopr ( copyIn.hopr ), lopr ( copyIn.lopr ),
    hihi ( copyIn.hihi ), high ( copyIn.high ), 
    low ( copyIn.low ), lolo ( copyIn.lolo ), type ( copyIn.type ),
    ioType ( copyIn.ioType ), elementCount ( copyIn.elementCount ),
    pPV ( copyIn.pPV )
{
//	buffer = copyIn.buffer;
//	btd = copyIn.btd;
	prec = copyIn.prec;
	
	acks = copyIn.acks;
	ackt = copyIn.ackt;
	
	hhsv = copyIn.hhsv;
	llsv = copyIn.llsv;
	hsv = copyIn.hsv;
	lsv = copyIn.lsv;
	    
	//all double
	lalm = copyIn.lalm ;
	hyst = copyIn.hyst;
	alst = copyIn.alst;
	adel = copyIn.adel;
	mlst = copyIn.mlst;
	mdel = copyIn.mdel;
	
	if ( copyIn.pName ) {
		pName = new char[ strlen(copyIn.pName) ];
		if ( pName )
			memcpy( pName, copyIn.pName, strlen(copyIn.pName) +1 );
	}
	
	if ( copyIn.pUnits ) {
		pUnits = new char[ strlen(copyIn.pUnits) ];
		if ( pUnits )
			memcpy( pUnits, copyIn.pUnits, strlen(copyIn.pUnits) +1 );
	}

}

inline pvInfo::~pvInfo ()
{
    //
    // GDD cleanup gets rid of GDD's that are in use 
    // by the PV before the file scope destructer for 
    // this class runs here so this does not seem to 
    // be a good idea
    //
    //if ( this->pPV != NULL ) {
    //   delete this->pPV;
    //}
	
	// NAME
	if ( pName )
		delete [] pName;
	// EGU
	if ( pUnits ) 
		delete [] pUnits;
}

inline void pvInfo::deletePV ()
{
    if ( this->pPV != NULL ) {
        delete this->pPV;
    }
}

inline double pvInfo::getScanPeriod () const 
{ 
    return this->scanPeriod; 
}

inline const char *pvInfo::getName () const 
{ 
    return this->pName; 
}

inline const char *pvInfo::getUnits () const 
{ 
    return this->pUnits; 
} 

inline double pvInfo::getHopr () const 
{ 
    return this->hopr; 
}

inline double pvInfo::getLopr () const 
{ 
    return this->lopr; 
}

inline double pvInfo::getHihi () const 
{ 
    return this->hihi; 
}

inline double pvInfo::getHigh () const 
{ 
    return this->high; 
}

inline double pvInfo::getLow () const 
{ 
    return this->low; 
}

inline double pvInfo::getLolo () const 
{ 
    return this->lolo; 
}

inline aitEnum pvInfo::getType () const 
{ 
    return this->type;
}

inline excasIoType pvInfo::getIOType () const 
{ 
    return this->ioType; 
}

inline unsigned pvInfo::getElementCount () const 
{ 
    return this->elementCount; 
}

inline void pvInfo::unlinkPV () 
{ 
    this->pPV = NULL; 
}
//-----------------------------------------------------------------------------


inline pvEntry::pvEntry ( pvInfo  & infoIn, exServer & casIn, 
        const char * pAliasName ) : 
    stringId ( pAliasName ), info ( infoIn ), cas ( casIn ) 
{
    assert ( this->stringId::resourceName() != NULL );
}

inline pvEntry::~pvEntry ()
{
    this->cas.removeAliasName ( *this );
}

inline void pvEntry::destroy ()
{
    delete this;
}

inline void exServer::removeAliasName ( pvEntry & entry )
{
    pvEntry * pE;
    pE = this->stringResTbl.remove ( entry );
    assert ( pE == &entry );
}

inline double exPV::getScanPeriod ()
{
    double curPeriod = this->info.getScanPeriod ();
    if ( ! this->interest ) {
        curPeriod *= 10.0L;
    }
    return curPeriod;
}

inline caStatus exPV::readNoCtx ( smartGDDPointer pProtoIn )
{
    return this->ft.read ( *this, *pProtoIn );
}

inline const pvInfo & exPV::getPVInfo ()
{
    return this->info;
}

inline const char * exPV::getName () const
{
    return this->info.getName();
}

inline void exServer::removeIO()
{
    if ( this->simultAsychIOCount > 0u ) {
        this->simultAsychIOCount--;
    }
    else {
        fprintf ( stderr, 
            "simultAsychIOCount underflow?\n" );
    }
}

inline unsigned exServer :: maxSimultAsyncIO () const
{
    return this->_maxSimultAsyncIO;
}

inline exChannel::exChannel ( const casCtx & ctxIn ) : 
    casChannel(ctxIn) 
{
}

