#ifndef _BASECOMPONENTCORE_H_
#define _BASECOMPONENTCORE_H_

/***********************************************************************\
 IRA Istituto di Radioastronomia                                 
 This code is under GNU General Public License (GPL).          
                                                              
 Andrea Orlati (andrea.orlati@inaf.it): Author
\***********************************************************************/

#include "Configuration.h"
#include <ReceiverControl.h>
#include <LocalOscillatorInterfaceC.h>
#include <ReceiversErrors.h>
#include <ManagmentDefinitionsC.h>

#define NUMBER_OF_STAGES 1
#define PHYSICAL_FEEDS 3
#define KBAND_FEED 0
#define QBAND_FEED 1
#define WBAND_FEED 2

/**
 * This class extends the ReceiverControl class in order to include the bypass activation on the Wband RF chain to enable Sun observations
 * @author <a href=mailto:andrea.orlati@inaf.it>Andrea Orlati</a>,
 * Istituto di Radioastronomia, Italia
 * <br>  
*/

class CKQWReceiverControl: public virtual IRA::ReceiverControl {
public:
	CKQWReceiverControl(const std::string dewar_ip,
                       const unsigned short dewar_port, 
                       const std::string lna_ip, 
                       const unsigned short lna_port, 
                       const unsigned int guard_time=250000, 
                       const unsigned short number_of_feeds=1) :  ReceiverControl(dewar_ip,dewar_port,lna_ip,lna_port,guard_time,number_of_feeds) {}
	virtual ~CKQWReceiverControl() {}
	/**
	* @throw ReceiverControlEx
	*/
	bool enableBypass();
	/**
	* @throw ReceiverControlEx
	*/
	bool disableBypass();
};

/**
 * This class contains the code of almost all the features  of the component
 * @author <a href=mailto:andrea.orlati@inaf.it>Andrea Orlati</a>,
 * Istituto di Radioastronomia, Italia
 * <br>
  */
class CComponentCore {

public:

    CComponentCore();
    virtual ~CComponentCore();


    /**
     * This method initializes the object
     * @param service pointer to container services object provided by the container
     */
    virtual void initialize(maci::ContainerServices* services);


    /**
     * This method prepares the object for execution.
     * @return the pointer to the configuration class
     * @throw (ComponentErrors::CDBAccessExImpl, ComponentErrors::MemoryAllocationExImpl, ComponentErrors::SocketErrorExImpl,
     *       ReceiversErrors::ModeErrorExImpl)
     */
    virtual CConfiguration<maci::ContainerServices> const * const execute();
    

    /**
     * This function is responsible to free all allocated resources
     */
    virtual void cleanup();


    /*
     * It sets the local oscillators. Before commanding the new value 
     * some check are done. The  corresponding signal amplitude is computed.
     * @param lo lists of values for the local oscillator (MHz), one for each IF. 
     * In that case just the first one is significant. In a -1 is passed the present value is kept.
     * @throw  ComponentErrors::ValidationErrorExImpl
     * @throw ComponentErrors::ValueOutofRangeExImpl
     * @throw ComponentErrors::CouldntGetComponentExImpl
     * @throw ComponentErrors::CORBAProblemExImpl
     * @thorw ReceiversErrors::LocalOscillatorErrorExImpl
     */
    void setLO(const ACS::doubleSeq& lo);

    /**
     * It activate the receiver, in other words it allows to setup the default configuration 
     * and to make sure the LNA are turned on.
     * @throw ReceiversErrors::ModeErrorExImpl,
     * @throw ComponentErrors::ValidationErrorExImpl,
     * @throw ComponentErrors::ValueOutofRangeExImpl,
     * @throw ComponentErrors::CouldntGetComponentExImpl,
     * @throw ComponentErrors::CORBAProblemExImpl,
     * @throw ReceiversErrors::LocalOscillatorErrorExImpl,
     * @throw ReceiversErrors::NoRemoteControlErrorExImpl,
     * @throw ReceiversErrors::ReceiverControlBoardErrorExImpl
     */
    void activate();

    /**
     * It deactivates the receiver.
     * @throw ReceiversErrors::NoRemoteControlErrorExImpl
     * @throw ReceiversErrors::ReceiverControlBoardErrorExImpl
     */
    void deactivate();


    /**
     * It allows to compute the value of the calibration mark for any given sub bands in 
     * the IF space.
     * @param result this the sequence of computed mark values, the first entry correspond to 
     * first sub band and so on....
     * @param, resFreq the sequence reports the initial observed sky frequency (MHz), the  
     * first entry correspond to first sub band and so on....
     * @param resBw the sequence reports the real bandwidth observed (MHz), the  first entry 
     * correspond to first sub band and so on....
     * @param freqs  list of start frequencies (MHz)
     * @param bandwidth list of the band widths (MHz)
     * @param feeds list of feed identifier, it allows to specifies form which feed the sub 
     * band comes from. In that case it is neglected since the receiver is a single feed
     * @param ifs list of IF identifier, it allows to specifies from which receiver IF the 
     * sub band comes from.
     * @param scale value to scale tsys measurement
     * @throw ComponentErrors::ValidationErrorExImpl
     * @throw ComponentErrors::ValueOutofRangeExImpl
     */
    void getCalibrationMark(
            ACS::doubleSeq& result,
            ACS::doubleSeq& resFreq,
            ACS::doubleSeq& resBw,
            const ACS::doubleSeq& freqs,
            const ACS::doubleSeq& bandwidths,
            const ACS::longSeq& feeds,
            const ACS::longSeq& ifs,
            bool& onoff,
            double &scale
     );

    /**
     * It is called to get the all the receiver output information in one call.
     * An output is identified by providing the feed and the IF identifier. It can process any number of requests at a time.
     * @param feeds is a list that stores the corresponding feed of the output we are asking for
     * @param ifs is a list that identifies which IFs of the feed we are interested in, usually 0..<i>IFs</i>-1
     * @param freq used to return the start frequency of the band provided by the output  the oscillator
     * (if present) is not  added (MHz)
     * @param bw used to return the total provided bandwidth. (MHz)
     * @param pols it specifies the polarization of the receiver output, since ACS does not support for enum
     * sequences the correct value must be matched against the <i>Receivers::TPolarization</i> enumeration.
     * @param LO it gives (if present) the value of the local oscillator (MHz).
     * @throw ComponentErrors::ValidationErrorExImpl
     * @throw ComponentErrors::ValueOutofRangeExImpl
     */
     void getIFOutput(
             const ACS::longSeq& feeds,
             const ACS::longSeq& ifs,
             ACS::doubleSeq& freqs,
             ACS::doubleSeq& bw,
             ACS::longSeq& pols,
             ACS::doubleSeq& LO
     );



    /**
     * It computes the taper given a reference band.
     * @param freq start frequency of the reference band
     * @param bw width of the reference band
     * @param feed feed number
     * @param ifNumber IF chain identifier
     * @param waveLen wave length of the reference band, the band is transformed in a real sky 
     * observed band and the the central frequency is taken.
     * @throw ComponentErrors::ValidationErrorExImpl 
     * @thorw ComponentErrors::ValueOutofRangeExImpl
     */
    double getTaper(
            const double& freq,
            const double& bw,
            const long& feed,
            const long& ifNumber,
            double& waveLen
    );


    /** It turns the calibration diode on.
     * @throw ReceiversErrors::NoRemoteControlErrorExImpl
     * @throw ComponentErrors::ValidationErrorExImpl
     * @throw ReceiversErrors::ReceiverControlBoardErrorExImpl    
    */
    void calOn();


    /** It turns the calibration diode off
     * @throw ReceiversErrors::NoRemoteControlErrorExImpl
     * @throw ComponentErrors::ValidationErrorExImpl
     * @throw ReceiversErrors::ReceiverControlBoardErrorExImpl    
     */
    void calOff();


    /** It turns the external calibration diode on.
     * @throw ReceiversErrors::NoRemoteControlErrorExImpl
     * @throw ComponentErrors::ValidationErrorExImpl
     * @throw ReceiversErrors::ReceiverControlBoardErrorExImpl
    */
    void externalCalOn();

    /** It turns the external calibration diode off.
     * @throw ReceiversErrors::NoRemoteControlErrorExImpl
     * @throw ComponentErrors::ValidationErrorExImpl
     * @throw ReceiversErrors::ReceiverControlBoardErrorExImpl
    */
    void externalCalOff();

    /** It turns on the sensor for vacuum measurement.
     * @throw ReceiversErrors::NoRemoteControlErrorExImpl
     * @throw ReceiversErrors::ReceiverControlBoardErrorExImpl
    */
    void vacuumSensorOn();


    /** It turns off the sensor for vacuum measurement.
     * @throw ReceiversErrors::NoRemoteControlErrorExImpl
     * @throw ReceiversErrors::ReceiverControlBoardErrorExImpl)
    */
    void vacuumSensorOff();


    /** It allows to turn LNA on
    * @throw ReceiversErrors::NoRemoteControlErrorExImpl
    * @throw ReceiversErrors::ReceiverControlBoardErrorExImpl)
    */
    void lnaOn();


    /** It allows to turn LNA off
    * @throw ReceiversErrors::NoRemoteControlErrorExImpl
    * @throw ReceiversErrors::ReceiverControlBoardErrorExImpl)
    */
    void lnaOff();


    /**
     * It reads and updates from the control board the current value of the vacuum
     * @throw ReceiversErrors::ReceiverControlBoardErrorExImpl
     */
    void updateVacuum();


    /**
     * It check if the vacuum pump is on and check is the status is fault or not (<i>VACUUMPUMPFAULT</i>)
     * @throw ReceiversErrors::ReceiverControlBoardErrorExImpl
     */
    void updateVacuumPump();


    /**
     * It checks if the vacuum valve is opened or not
     * @throw ReceiversErrors::ReceiverControlBoardErrorExImpl
     */
    void updateVacuumValve();


    /**
     * It reads and updates from the control board the current cryo temperature measured near the cool head
     * @throw ReceiversErrors::ReceiverControlBoardErrorExImpl
     */
    void updateCryoCoolHead();


    /**
     * It reads and updates from the control board the current cryo temperature measured near the cool head window
     * @throw ReceiversErrors::ReceiverControlBoardErrorExImpl
     */
    void updateCryoCoolHeadWin();


    /**
     * It reads and updates from the control board the current cryo temperature measured near the LNA
     */
    void updateCryoLNA();


    /**
     * It reads and updates from the control board the current cryo temperature measured near the LNA window
     * @throw ReceiversErrors::ReceiverControlBoardErrorExImpl
     */
    void updateCryoLNAWin();


    /**
     * It reads and updates from the control board the current vertex temperature
     * @throw ReceiversErrors::ReceiverControlBoardErrorExImpl
     */
    void updateVertexTemperature();


    /**
     * It checks if the Dewar power box is in remote or not
     * @throw ReceiversErrors::ReceiverControlBoardErrorExImpl
     */
    void updateIsRemote();


    /**
     * It checks if the cool head is turned on or not
     * @throw ReceiversErrors::ReceiverControlBoardErrorExImpl
     */
    void updateCoolHead();


    /**
     * It checks is the status of the noise mark correspond to the commanded status, 
     * otherwise it sets the <i>NOISEMARKERROR</i> bit. It also check if the
     * external control of the noise mark has been enabled or not
     */
    void updateNoiseMark();


    /**
     * This method resumes the whole status of the component. It set the <i>componentStatus</i> member variable.
     */
    void updateComponent();


    /**
     * This is getter method. No need to make it thread safe......
     * @return the current value of the vacuum in mbar
     */
    double getVacuum() const { return m_vacuum; }


    /**
     * This is getter method. No need to make it thread safe......
     * @return the current value of the cryogenic temperature at cool head in °K
     */
    CConfiguration<maci::ContainerServices>::BoardValue getCryoCoolHead() const { return m_cryoCoolHead; }


    /**
     * This is getter method. No need to make it thread safe......
     * @return the current value of the cryogenic temperature at cool head window in °K
     */
    CConfiguration<maci::ContainerServices>::BoardValue getCryoCoolHeadWin() const { return m_cryoCoolHeadWin; }


    /**
     * This is getter method. No need to make it thread safe......
      * @return the current value of the cryogenic temperature at LNA in °K
     */
    CConfiguration<maci::ContainerServices>::BoardValue getCryoLNA() const { return m_cryoLNA; }


    /**
     * This is getter method. No need to make it thread safe......
     * @return the current value of the cryogenic temperature at LNA  window in °K
     */
    CConfiguration<maci::ContainerServices>::BoardValue getCryoLNAWin() const { return m_cryoLNAWin; }


    /**
     * This is getter method. No need to make it thread safe......
     * @return the current vertex temperature in °K
     */
    CConfiguration<maci::ContainerServices>::BoardValue getVertexTemperature() const { return m_envTemperature; }


    /**
     * This is getter method. No need to make it thread safe......
     * @return the current status word
     */
    DWORD getStatusWord() const  { return  m_statusWord; }


    /**
     * It returns the feed geometry of the receiver with respect to the central one. 
     * For this implementation it is just a placeholder since there is just one feed.
     */
    long getFeeds(ACS::doubleSeq& X, ACS::doubleSeq& Y, ACS::doubleSeq& power);

    /**
     * It returns the current operating mode of the receiver.
     * @return output string
     */
    const IRA::CString& getActualMode();

    /**
     * It returns the number of IF chains for each feed
     * @return output value
     */
    const DWORD& getIFs();


    /**
     * It returns the number of feeds
     * @return output value
     */
    const DWORD& getFeeds();

    /**
     * @return the status flag of the component
     */
    const Management::TSystemStatus& getComponentStatus();

    /**
     * Allows to set the "default_value" for the vacuum characteristic. In principle it is possible 
     * to read it directly from CDB, but I found it more
     * comfortable to get it directly from the characteristic itself.
     */
    inline void setVacuumDefault(const double& val) { m_vacuumDefault=val; }
    
    void getBandwidth(ACS::doubleSeq& bw);
    void getInitialFreq(ACS::doubleSeq& f);
    void getLocalOscillator(ACS::doubleSeq& lo);
    void getPolarizations(ACS::longSeq& pol);

    /** 
     * It allows to change the operating mode of the receiver. 
     * If the mode does not correspond to a valid mode an error is thrown.
     * @param  mode mode code as a string
	  * @throw ComponentErrors::ValidationErrorExImpl
     * @throw ComponentErrors::ValueOutofRangeExImpl
     * @throw ComponentErrors::CouldntGetComponentExImpl
     * @throw ComponentErrors::CORBAProblemExImpl
     * @throw ReceiversErrors::LocalOscillatorErrorExImpl
     * @throw ComponentErrors::CDBAccessExImpl
     * @throw ReceiversErrors::ModeErrorExImpl
     * @throw ReceiversErrors::ReceiverControlBoardErrorExImpl
     */
    virtual void setMode(const char * mode);
    
    /*  
     * This method will update the last readout of the gate voltage parameters of the LNAs    
     * @throw ReceiversErrors::ReceiverControlBoardErrorExImpl
    */
	 void updateVgLNAControls();


	 /*  
     * This method will update the last readout of the drain voltage parameters of the LNAs    
     * @throw ReceiversErrors::ReceiverControlBoardErrorExImpl
    */
	 void updateVdLNAControls();

	    /*  
     * This method will update the last readout of the drain current parameters of the LNAs    
     * @throw ReceiversErrors::ReceiverControlBoardErrorExImpl
    */	
	 void updateIdLNAControls();
	 
	 ACS::doubleSeq getStageValues(const IRA::ReceiverControl::FetValue& control, DWORD ifs, DWORD stage);


protected:

    /** Obtain a valid reference to the local oscillator device
	  * @throw ComponentErrors::CouldntGetComponentExImpl   
     */
    void loadLocalOscillator(Receivers::LocalOscillator_ptr device,bool &fault,const IRA::CString& name);


    /** Used to free the reference to the local oscillator device */
    void unloadLocalOscillator(Receivers::LocalOscillator_ptr device,const IRA::CString& name);


    /************************ CONVERSION FUNCTIONS **************************/
    // Convert the voltage value of the vacuum to mbar
    static double voltage2mbar(double voltage) { return(pow(10, 1.5 * voltage - 12)); }


    // Convert the voltage value of the temperatures to Kelvin
    static double voltage2Kelvin(double voltage) {
        return voltage < 1.12 ? \
                  (660.549422889947 * pow(voltage, 6)) - (2552.334255456860 * \
                  pow(voltage, 5)) + (3742.529989384570 * pow(voltage, 4)) - \
                  (2672.656926956470 * pow(voltage, 3)) + (947.905578508975 * \
                  pow(voltage, 2)) - 558.351002849576 * voltage + 519.607622398508 \
                :
                  (865.747519105672 * pow(voltage, 6)) - (7271.931957100480 * \
                  pow(voltage, 5)) + (24930.666241800500 * pow(voltage, 4)) - \
                  (44623.988512320400 * pow(voltage, 3)) + (43962.922216886600 * \
                  pow(voltage, 2)) - 22642.245121997700 * voltage + 4808.631312836750;
    }


    // Convert the voltage value of the temperatures to Celsius (Sensor B57703-10K)
    static double voltage2Celsius(double voltage) 
    { return -5.9931 * pow(voltage, 5) + 40.392 * pow(voltage, 4) - 115.41 * pow(voltage, 3) + 174.67 * pow(voltage, 2) - 174.23 * voltage + 112.79; }

    // Convert the ID voltage value to the mA value
    static double currentConverter(double voltage) { return(10 * voltage); }


    // Convert the VD and VG voltage values using a right scale factor
    static double voltageConverter(double voltage) { return(voltage); }
    /********************** END CONVERSION FUNCTIONS ***********************/

    enum TStatusBit {
        LOCAL=0,
        VACUUMSENSOR=1,
        VACUUMPUMPSTATUS=2,
        VACUUMPUMPFAULT=3,
        VACUUMVALVEOPEN=4,
        COOLHEADON=5,
        COMPRESSORFAULT=6,
        NOISEMARK=7,
        NOISEMARKERROR=8,
        EXTNOISEMARK=9,
        CONNECTIONERROR=10,
        UNLOCKED=11
    };


    /** This function will set the a status bit. It may be considered thread safe due to its definition */
    inline void setStatusBit(TStatusBit bit) { m_statusWord |= 1 << bit; }


    /** This function will unset (clear) a status bit. It may be considered thread safe due to its definition */
    inline void clearStatusBit(TStatusBit  bit) { m_statusWord &= ~(1 << bit); }


    /** This function check is a bit is set or not. It may be considered thread safe due to its definition */
    inline bool checkStatusBit(TStatusBit bit) { return m_statusWord & (1 << bit); }


    CConfiguration<maci::ContainerServices> m_configuration;
    CKQWReceiverControl *m_control; // This object is thread safe
    BACIMutex m_mutex;
    //IRA::CString m_actualMode;
    //IRA::CString m_setupMode; 


private:

    maci::ContainerServices* m_services;
    Receivers::LocalOscillator_var m_localOscillatorDevice_K;
    bool m_localOscillatorFault_K;
    Receivers::LocalOscillator_var m_localOscillatorDevice_Q;
    bool m_localOscillatorFault_Q;
    Receivers::LocalOscillator_var m_localOscillatorDevice_WL;
    bool m_localOscillatorFault_WL;
    Receivers::LocalOscillator_var m_localOscillatorDevice_WH;
    bool m_localOscillatorFault_WH;    
    
    //double m_localOscillatorValue;
    double m_vacuum;
    CConfiguration<maci::ContainerServices>::BoardValue m_cryoCoolHead;
    CConfiguration<maci::ContainerServices>::BoardValue m_cryoCoolHeadWin;
    CConfiguration<maci::ContainerServices>::BoardValue m_cryoLNA;
    CConfiguration<maci::ContainerServices>::BoardValue m_cryoLNAWin;
    CConfiguration<maci::ContainerServices>::BoardValue m_envTemperature;
    std::vector<IRA::ReceiverControl::StageValues> m_vdStageValues;
    std::vector<IRA::ReceiverControl::StageValues> m_idStageValues;
    std::vector<IRA::ReceiverControl::StageValues> m_vgStageValues;
    double m_vacuumDefault;
    bool m_calDiode;
    IRA::ReceiverControl::FetValues m_fetValues;
    DWORD m_statusWord;
    // m_ioMarkError is a flag used to know if we already got an IO
    // error.
    bool m_ioMarkError;
    Management::TSystemStatus m_componentStatus;

    void setComponentStatus(const Management::TSystemStatus& status) 
    { 
        if (status>m_componentStatus) m_componentStatus=status;  
    }
    
    double linearFit(ACS::doubleSeq& X,ACS::doubleSeq& Y, const WORD& size, double x) const;

    double linearFit(double *X,double *Y,const WORD& size,double x) const;

};

#endif
