/*************************************************************************\
* 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. 
\*************************************************************************/

#include "exServer.h"
#include "gddApps.h"

#define myPI 3.14159265358979323846

//
// SUN C++ does not have RAND_MAX yet
//
#if ! defined(RAND_MAX)
//
// Apparently SUN C++ is using the SYSV version of rand
//
#   if 0
#       define RAND_MAX INT_MAX
#   else
#       define RAND_MAX SHRT_MAX
#   endif
#endif

//
// special gddDestructor guarantees same form of new and delete
//
class exVecDestructor: public gddDestructor {
    virtual void run (void *);
};

//
// exVectorPV::maxDimension()
//
unsigned exVectorPV::maxDimension() const
{
    return 1u;
}

//
// exVectorPV::maxBound()
//
aitIndex exVectorPV::maxBound (unsigned dimension) const // X aCC 361
{
    if (dimension==0u) {
        return this->info.getElementCount();
    }
    else {
        return 0u;
    }
}

//
// exVectorPV::scan
//
void exVectorPV::scan()
{
    caStatus            status;
    double              radians;
    smartGDDPointer     pDD;
    aitFloat32          *pF, *pFE;
    const aitFloat32    *pCF;
    float               newValue;
    float               limit;
    exVecDestructor     *pDest;
    int                 gddStatus;

    //
    // update current time (so we are not required to do
    // this every time that we write the PV which impacts
    // throughput under sunos4 because gettimeofday() is
    // slow)
    //
    this->currentTime = epicsTime::getCurrent();
 
    pDD = new gddAtomic (gddAppType_value, aitEnumFloat64, 
            1u, this->info.getElementCount());
    if ( ! pDD.valid () ) {
        return;
    }
 
    //
    // smart pointer class manages reference count after this point
    //
    gddStatus = pDD->unreference();
    assert (!gddStatus);

    //
    // allocate array buffer
    //
    pF = new aitFloat32 [this->info.getElementCount()];
    if (!pF) {
        return;
    }

    pDest = new exVecDestructor;
    if (!pDest) {
        delete [] pF;
        return;
    }

    //
    // install the buffer into the DD
    // (do this before we increment pF)
    //
    pDD->putRef(pF, pDest);

    //
    // double check for reasonable bounds on the
    // current value
    //
    pCF=NULL;
    if ( this->pValue.valid () ) {
        if (this->pValue->dimension()==1u) {
            const gddBounds *pB = this->pValue->getBounds();
            if (pB[0u].size()==this->info.getElementCount()) {
                pCF = *this->pValue;
            }
        }
    }

    pFE = &pF[this->info.getElementCount()];
    while (pF<pFE) {
        radians = (rand () * 2.0 * myPI)/RAND_MAX;
        if (pCF) {
            newValue = *pCF++;
        }
        else {
            newValue = 0.0f;
        }
        newValue += (float) (sin (radians) / 10.0);
        limit = (float) this->info.getHopr();
        newValue = tsMin (newValue, limit);
        limit = (float) this->info.getLopr();
        newValue = tsMax (newValue, limit);
        *(pF++) = newValue;
    }

    aitTimeStamp gddts = this->currentTime;
    pDD->setTimeStamp ( & gddts );

// antonio code    
    if ( (this->info.getScanPeriod() > 0.0) ) {
    	status = this->update ( *pDD, true, false );
    	if (status!=S_casApp_success) {
    		errMessage ( status, "vector scan update failed\n" );
    	}
    }
    
  //  delete &((gddAtomic ) *pDD);
    //delete &((gdd) *pDD);
}

//
// exVectorPV::updateValue ()
//
// NOTES:
// 1) This should have a test which verifies that the
// incoming value in all of its various data types can
// be translated into a real number?
// 2) We prefer to unreference the old PV value here and
// reference the incomming value because this will
// result in value change events each retaining an
// independent value on the event queue. With large arrays
// this may result in too much memory consumtion on
// the event queue.
//
caStatus exVectorPV::updateValue ( const gdd & value )
{

    //
    // Check bounds of incoming request
    // (and see if we are replacing all elements -
    // replaceOk==true)
    //
    // Perhaps much of this is unnecessary since the
    // server lib checks the bounds of all requests
    //
    if ( value.isAtomic()) {
        if ( value.dimension() != 1u ) {
            return S_casApp_badDimension;
        }
        const gddBounds* pb = value.getBounds ();
        if ( pb[0u].first() != 0u ) {
            return S_casApp_outOfBounds;
        }
        else if ( pb[0u].size() > this->info.getElementCount() ) {
            return S_casApp_outOfBounds;
        }
    }
    else if ( ! value.isScalar() ) {
        //
        // no containers
        //
        return S_casApp_outOfBounds;
    }
        
    //
    // Create a new array data descriptor
    // (so that old values that may be referenced on the
    // event queue are not replaced)
    //
    smartGDDPointer pNewValue ( new gddAtomic ( gddAppType_value, aitEnumFloat64, 
        1u, this->info.getElementCount() ) );
    if ( ! pNewValue.valid() ) {
        return S_casApp_noMemory;
    }

    //
    // smart pointer class takes care of the reference count
    // from here down
    //
    gddStatus gdds = pNewValue->unreference( );
    assert ( ! gdds );
    
    //
    // allocate array buffer
    //
    aitFloat64 * pF = new aitFloat64 [this->info.getElementCount()];
    if (!pF) {
        return S_casApp_noMemory;
    }
    
    //
    // Install (and initialize) array buffer
    // if no old values exist
    //
    unsigned count = this->info.getElementCount();
    for ( unsigned i = 0u; i < count; i++ ) {
        pF[i] = 0.0f;
    }

    exVecDestructor * pDest = new exVecDestructor;
    if (!pDest) {
        delete [] pF;
        return S_casApp_noMemory;
    }

    //
    // install the buffer into the DD
    // (do this before we increment pF)
    //
    pNewValue->putRef ( pF, pDest );
    
    //
    // copy in the values that they are writing
    //
    gdds = pNewValue->put( & value );
    if ( gdds ) {
        return S_cas_noConvert;
    }
    
    this->pValue = pNewValue;
    

    
    return S_casApp_success;
}

//
// exVecDestructor::run()
//
// special gddDestructor guarantees same form of new and delete
//
void exVecDestructor::run ( void *pUntyped )
{
    aitFloat32 * pf = reinterpret_cast < aitFloat32 * > ( pUntyped );
    delete [] pf;
}
