// $Header: https://oberon.oapd.inaf.it/shins/trunk/shins/src/tiptilt_ctrl/bcu/Interface/Sharknir/Bcu/ServiceWorkerSimulator.h 975 2018-10-31 09:24:04Z depa $
#pragma once

#include "tiptilt_ctrl/bcu/Interface/Sharknir/Bcu/ServiceWorker.h"
#include "tiptilt_ctrl/bcu/Interface/Sharknir/Bcu/PropertyName.h"
#include "tiptilt_ctrl/bcu/Interface/Sharknir/Bcu/BcuException.h"
#include "tiptilt_ctrl/bcu/Interface/Sharknir/Bcu/BcuSkeleton.h"

#include "tiptilt_ctrl/bcu/util/Assertions.h"
#include "tiptilt_ctrl/bcu/util/ClockPtr.h"

#include <map>


namespace Sharknir { namespace Bcu {

class ServiceWorkerSimulator : public Sharknir::Bcu::ServiceWorker

{
public:
    ServiceWorkerSimulator(const Nice::JointPtr& config);

    /** @note overridden */
    virtual void resetDevices(int /* firstDsp */, int /* lastDsp */,
            int /* address */, const Nice::SeqInt& /* buffer */)
    {
        N_LOG(name() << ": Resetting devices")
        throwExceptionIfRequested();
    }

    /** @note overridden */
    virtual void writeDsp(int firstDsp, int lastDsp,
            int startAddress, const Nice::SeqInt& buffer)
    {
        N_LOG(name() << ": Writing to DSP memory")

        writeTo(dspMemByIndex_, firstDsp, lastDsp, startAddress, buffer);
        throwExceptionIfRequested();
    }

    /** @note overridden */
    void writeSdram(int firstDsp, int lastDsp, int address,
            const Nice::SeqInt& buffer)
    {
        N_LOG(name() << ": Writing to SDRAM memory")
        writeTo(sdramMemByIndex_, firstDsp, lastDsp, address, buffer);
        throwExceptionIfRequested();
    }

    /** @note overridden */
    virtual void writeSram(int firstDsp, int lastDsp,
            int address, const Nice::SeqInt& buffer)
    {
        N_LOG(name() << ": Writing to SRAM memory")
        writeTo(sramMemByIndex_, firstDsp, lastDsp, address, buffer);
        throwExceptionIfRequested();
    }

    /** @note overridden */
    virtual Nice::SeqInt readDsp(int dspIndex, int startAddress,
              int lengthInDword)
    {
        N_LOG(name() << ": Reading from DSP memory")
        throwExceptionIfRequested();
        return readFrom(dspMemByIndex_, dspIndex, startAddress, lengthInDword);
    }

    /** @note overridden */
    virtual Nice::SeqInt readSdram(int dspIndex, int address,
              int lengthInDword)
    {
        N_LOG(name() << ": Reading from SDRAM memory")
        throwExceptionIfRequested();
        return readFrom(sdramMemByIndex_, dspIndex, address, lengthInDword);
    }

    /** @note overridden */
    virtual Nice::SeqInt readSram(int dspIndex, int address,
            int lengthInDword)
    {
        N_LOG(name() << ": Reading from SRAM memory")
        throwExceptionIfRequested();

        return readFrom(sramMemByIndex_, dspIndex, address, lengthInDword);
    }

    /** @note overridden */
    virtual void uploadDspCode(int /* firstDsp */, int /* lastDsp */,
            const std::string& /* dspProgram */)
    {
        N_LOG(name() << ": Uploading DSP code")
        throwExceptionIfRequested();
    }

    /** @note overridden */
    virtual void enableMasterDiagnostics(Nice::U16 /* decimationFactor */,
            const std::string& /* remoteHostIpv4Address */,
            const std::string& /* remoteHostMacAddress */,
            Nice::U16 /* remoteHostUdpPort */)
    {
        N_LOG("Enabling master diagnostics")
        throwExceptionIfRequested();
    }


    /** @note overridden */
    virtual void init()
    {
        N_LOG(name() << ": " <<
                Sharknir::Bcu::ServiceWorker::STARTED_UP_MESSAGE());
    }

    /** @note overridden */
    virtual void deinit()
    {
        N_LOG(name() << ": deinitializing");
    }

    /** @note overridden */
    virtual void activate()
    {
        N_LOG(name() << ": activating");
    }

    /** @note overridden */
    virtual void deactivate()
    {
        N_LOG(name() << ": deactivating");
    }

    /** @note overridden */
    virtual void terminate()
    {
        N_LOG(name() << ": terminating");
    }

    /** @note overridden */
    virtual void stop()
    {
        N_LOG(name() << ": stopping");
    }

    /** @note overridden */
    virtual void abort()
    {
        N_LOG(name() << ": aborting")
    }

    /** @note overridden */
    virtual void idleOnline()
    {
        ServiceWorker::idleOnline();
    }


    /** @note overridden */
    virtual ~ServiceWorkerSimulator()
    {
    }


    static const uint32_t APD_COUNT_0;
    static const uint32_t APD_COUNT_1;
    static const uint32_t APD_COUNT_2;
    static const uint32_t APD_COUNT_3;
    static const uint32_t FLAO_SLOPES_POSITION;

private:
    typedef Nice::Int Address;
    typedef Nice::Int CellValue;
    typedef std::map<Address, CellValue> Memory;

    typedef Nice::Int DspIndex;
    typedef std::map<DspIndex, Memory> MemoryByDspIndex;
    MemoryByDspIndex dspMemByIndex_;
    MemoryByDspIndex sdramMemByIndex_;
    MemoryByDspIndex sramMemByIndex_;

    void throwExceptionIfRequested()
    {
        if (config()->getAtNode<bool>(
                PropertyName::Simulator::externalOperationFailureRequested()))
        {
            E_THROW(Sharknir::BcuServiceException, "this failure was requested");
        }
    }


    void writeTo(MemoryByDspIndex& memByIndex,
            const DspIndex firstDsp,
            const DspIndex lastDsp,
            const Address startAddress,
            const Nice::SeqInt& buffer)
    {
        for (int dsp= firstDsp; dsp <= lastDsp; dsp++)
        {
            Memory& mem= memByIndex[dsp];
            for (size_t i= 0; i < buffer.size(); ++i)
            {
                Address addr= static_cast<Address>(i) + startAddress;
                mem[addr]= buffer[i];
            }
        }
    }

    Nice::SeqInt readFrom(MemoryByDspIndex& memByIndex,
            const DspIndex dspIndex,
            const Address startAddress,
            const Nice::Int lengthInDword)
    {
         Memory& dspMem= memByIndex[dspIndex];
         Nice::SeqInt buffer(lengthInDword);
         for (int i= 0; i < lengthInDword; ++i)
         {
             const Address addr= startAddress + static_cast<Address>(i);
             buffer[i]= dspMem[addr];
         }

         return buffer;
    }
};
typedef IceUtil::Handle<ServiceWorkerSimulator> ServiceWorkerSimulatorPtr;

}
}

