/**
 * yateradio.h
 * Radio library
 * This file is part of the YATE Project http://YATE.null.ro
 *
 * Yet Another Telephony Engine - a fully featured software PBX and IVR
 * Copyright (C) 2011-2014 Null Team
 * Copyright (C) 2015 LEGBA Inc
 *
 * This software is distributed under multiple licenses;
 * see the COPYING file in the main directory for licensing
 * information for this specific distribution.
 *
 * This use of this software may be subject to additional restrictions.
 * See the LEGAL file in the main directory for details.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 */

#ifndef __YATERADIO_H
#define __YATERADIO_H

#include <yateclass.h>
#include <yatexml.h>

#ifdef _WINDOWS

#ifdef LIBYRADIO_EXPORTS
#define YRADIO_API __declspec(dllexport)
#else
#ifndef LIBYRADIO_STATIC
#define YRADIO_API __declspec(dllimport)
#endif
#endif

#endif /* _WINDOWS */

#ifndef YRADIO_API
#define YRADIO_API
#endif


namespace TelEngine {

class GSML3Codec;                        // GSM Layer codec
class RadioCapability;                   // Radio device capabilities
class RadioInterface;                    // Generic radio interface

class YRADIO_API GSML3Codec
{
    YNOCOPY(GSML3Codec);
public:
    /**
     * Codec flags
     */
    enum Flags {
	XmlDumpMsg = 0x01,
	XmlDumpIEs = 0x02,
	MSCoder    = 0x04,
    };

    /**
     * Codec return status
     */
    enum Status {
	NoError = 0,
	MsgTooShort,
	UnknownProto,
	ParserErr,
	MissingParam,
	IncorrectOptionalIE,
	IncorrectMandatoryIE,
	MissingMandatoryIE,
	UnknownMsgType,
    };

    /**
     * Protocol discriminator according to ETSI TS 124 007 V11.0.0, section 11.2.3.1.1
     */
    enum Protocol {
	GCC       = 0x00, // Group Call Control
	BCC       = 0x01, // Broadcast Call Control
	EPS_SM    = 0x02, // EPS Session Management
	CC        = 0x03, // Call Control; Call Related SS messages
	GTTP      = 0x04, // GPRS Transparent Transport Protocol (GTTP)
	MM        = 0x05, // Mobility Management
	RRM       = 0x06, // Radio Resources Management
	EPS_MM    = 0x07, // EPS Mobility Management
	GPRS_MM   = 0x08, // GPRS Mobility Management
	SMS       = 0x09, // SMS
	GPRS_SM   = 0x0a, // GPRS Session Management
	SS        = 0x0b, // Non Call Related SS messages
	LCS       = 0x0c, // Location services
	Extension = 0x0e, // reserved for extension of the PD to one octet length
	Test      = 0x0f, // used by tests procedures described in 3GPP TS 44.014, 3GPP TS 34.109 and 3GPP TS 36.509
	Unknown   = 0xff,
    };

    /**
     * IE types
     */
    enum Type {
	NoType = 0,
	T,
	V,
	TV,
	LV,
	TLV,
	LVE,
	TLVE,
    };

    /**
     * Type of XML data to generate
     */
    enum XmlType {
	Skip,
	XmlElem,
	XmlRoot,
    };

    /**
     * EPS Security Headers
     */
    enum EPSSecurityHeader {
	PlainNAS                           = 0x00,
	IntegrityProtect                   = 0x01,
	IntegrityProtectCiphered           = 0x02,
	IntegrityProtectNewEPSCtxt         = 0x03,
	IntegrityProtectCipheredNewEPSCtxt = 0x04,
	ServiceRequestHeader               = 0xa0,
    };

    /**
     * Constructor
     */
    GSML3Codec(DebugEnabler* dbg = 0);

    /**
     * Decode layer 3 message payload
     * @param in Input buffer containing the data to be decoded
     * @param len Length of input buffer
     * @param out XmlElement into which the decoded data is returned
     * @param params Encoder parameters
     * @return Parsing result: 0 (NoError) if succeeded, error status otherwise
     */
    unsigned int decode(const uint8_t* in, unsigned int len, XmlElement*& out, const NamedList& params = NamedList::empty());

    /**
     * Encode a layer 3 message
     * @param in Layer 3 message in XML form
     * @param out Output buffer into which to put encoded data
     * @param params Encoder parameters
     * @return Parsing result: 0 (NoError) if succeeded, error status otherwise
     */
    unsigned int encode(const XmlElement* in, DataBlock& out, const NamedList& params = NamedList::empty());

    /**
     * Decode layer 3 message from an existing XML
     * @param xml XML which contains layer 3 messages to decode and into which the decoded XML will be put
     * @param params Decoder parameters
     * @return Parsing result: 0 (NoError) if succeeded, error status otherwise
     */
    unsigned int decode(XmlElement* xml, const NamedList& params = NamedList::empty());

    /**
     * Encode a layer 3 message from an existing XML
     * @param xml XML which contains a layer 3 message in XML form. The message will be replaced with its encoded buffer
     * @param params Encoder parameters
     * @return Parsing result: 0 (NoError) if succeeded, error status otherwise
     */
    unsigned int encode(XmlElement* xml, const NamedList& params = NamedList::empty());

    /**
     * Set data used in debug
     * @param enabler The DebugEnabler to use (0 to to use the engine)
     * @param ptr Pointer to print, 0 to use the codec pointer
     */
    void setCodecDebug(DebugEnabler* enabler = 0, void* ptr = 0);

    /**
     * Retrieve codec flags
     * @return Codec flags
     */
    inline uint8_t flags() const
	{ return m_flags; }

    /**
     * Set codec flags
     * @param flgs Flags to set
     * @param reset Reset flags before setting these ones
     */
    inline void setFlags(uint8_t flgs, bool reset = false)
    {
	if (reset)
	    resetFlags();
	m_flags |= flgs;
    }

    /**
     * Reset codec flags
     * @param flgs Flags to reset. If 0, all flags are reset
     */
    inline void resetFlags(uint8_t flgs = 0)
    {
	if (flgs)
	    m_flags &= ~flgs;
	else
	    m_flags = 0;
    }

    /**
     * Activate printing of debug messages
     * @param on True to activate, false to disable
     */
    inline void setPrintDbg(bool on = false)
	{ m_printDbg = on; }

    /**
     * Get printing of debug messages flag
     * @return True if debugging is activated, false otherwise
     */
    inline bool printDbg() const
	{ return m_printDbg; }

    /**
     * Get DebugEnabler used by this codec
     * @return DebugEnabler used by the codec
     */
    inline DebugEnabler* dbg() const
	{ return m_dbg; }

    /**
     * Retrieve the codec pointer used for debug messages
     * @return Codec pointer used for debug messages
     */
    inline void* ptr() const
	{ return m_ptr; }

    /**
     * Decode GSM 7bit buffer
     * @param buf Input buffer
     * @param len Input buffer length
     * @param text Destination text
     * @param heptets Maximum number of heptets in buffer
     */
    static void decodeGSM7Bit(unsigned char* buf, unsigned int len, String& text,
	unsigned int heptets = (unsigned int)-1);

    /**
     * Encode GSM 7bit buffer
     * @param text Input text
     * @param buf Destination buffer
     * @return True if all characters were encoded correctly
     */
    static bool encodeGSM7Bit(const String& text, DataBlock& buf);

    /**
     * IE types dictionary
     */
    static const TokenDict s_typeDict[];

    /**
     * L3 Protocols dictionary
     */
    static const TokenDict s_protoDict[];

    /**
     * EPS Security Headers dictionary
     */
    static const TokenDict s_securityHeaders[];

    /**
     * Errors dictionary
     */
    static const TokenDict s_errorsDict[];

    /**
     * Mobility Management reject causes dictionary
     */
    static const TokenDict s_mmRejectCause[];

    /**
     * GPRS Mobility Management reject causes dictionary
     */
    static const TokenDict s_gmmRejectCause[];

private:

    unsigned int decodeXml(XmlElement* xml, const NamedList& params, const String& pduTag);
    unsigned int encodeXml(XmlElement* xml, const NamedList& params, const String& pduTag);
    void printDbg(int dbgLevel, const uint8_t* in, unsigned int len, XmlElement* xml, bool encode = false);

    uint8_t m_flags;                 // Codec flags
    // data used for debugging messages
    DebugEnabler* m_dbg;
    void* m_ptr;
    // activate debug
    bool m_printDbg;
};


/**
 * @short Radio device capabilities
 * Radio capability object describes the parameter ranges of the radio handware.
 */
class YRADIO_API RadioCapability
{
public:
    /**
     * Constructor
     */
    RadioCapability();

    unsigned maxPorts;                   // Available number of ports
    unsigned currPorts;                  // Number of used (available) ports
    uint64_t maxTuneFreq;                // Maximum allowed tuning frequency (in Hz)
    uint64_t minTuneFreq;                // Minimum allowed tuning frequency (in Hz)
    unsigned maxSampleRate;              // Maximum allowed sampling rate (in Hz)
    unsigned minSampleRate;              // Minimum allowed sampling rate (in Hz)
    unsigned maxFilterBandwidth;         // Maximum allowed anti-alias filter bandwidth (in Hz)
    unsigned minFilterBandwidth;         // Minimum allowed anti-alias filter bandwidth (in Hz)
    unsigned rxLatency;                  // Estimated radio latency (in samples)
    unsigned txLatency;                  // Estimated transmit latency (in samples)
};


/**
 * Keeps a buffer pointer with offset and valid samples
 * @short A buffer description
 */
class RadioBufDesc
{
public:
    /**
     * Constructor
     */
    inline RadioBufDesc()
	: samples(0), offs(0), valid(0)
	{}

    /**
     * Reset the buffer
     * @param value Offset and valid samples value
     */
    inline void reset(unsigned int value = 0)
	{ offs = valid = value; }

    /**
     * Reset the buffer
     * @param offset New offset
     * @param validS New valid samples value
     */
    inline void reset(unsigned int offset, unsigned int validS) {
	    offs = offset;
	    valid = validS;
	}

    /**
     * Check if the buffer is valid
     * @param minSamples Required minimum number of valid samples
     * @return True if valid, false otherwise
     */
    inline bool validSamples(unsigned int minSamples) const
	{ return !minSamples || minSamples >= offs || minSamples >= valid; }

    float* samples;                      // Current read buffer
    unsigned int offs;                   // Current buffer offset (in sample periods)
    unsigned int valid;                  // The number of valid samples in buffer
};


/**
 * Keeps buffers used by RadioInterface::read()
 * @short RadioInterface read buffers
 */
class RadioReadBufs : public GenObject
{
public:
    /**
     * Constructor
     * @param len Single buffer length (in sample periods)
     * @param validThres Optional threshold for valid samples. Used when read timestamp
     *  data is in the future and a portion of the buffer need to be reset.
     *  If valid samples are below threshold data won't be set or copied
     */
    inline RadioReadBufs(unsigned int len = 0, unsigned int validThres = 0)
	: m_bufSamples(len), m_validMin(validThres)
	{}

    /**
     * Reset buffers
     * @param len Single buffer length (in sample periods)
     * @param validThres Optional threshold for valid samples
     */
    inline void reset(unsigned int len, unsigned int validThres) {
	    m_bufSamples = len;
	    m_validMin = validThres;
	    crt.reset();
	    aux.reset();
	    extra.reset();
	}

    /**
     * Retrieve the length of a single buffer
     * @return Buffer length (in sample periods)
     */
    inline unsigned int bufSamples() const
	{ return m_bufSamples; }

    /**
     * Check if a given buffer is full (offset is at least buffer length)
     * @param buf Buffer to check
     * @return True if the given buffer is full, false otherwise
     */
    inline bool full(RadioBufDesc& buf) const
	{ return buf.offs >= m_bufSamples; }

    /**
     * Check if a given is valid (have enough valid samples)
     * @param buf Buffer to check
     * @return True if the given buffer is valid, false otherwise
     */
    inline bool valid(RadioBufDesc& buf) const
	{ return buf.validSamples(m_validMin); }

    /**
     * Dump data for debug purposes
     * @param buf Destination buffer
     * @return Destination buffer reference
     */
    String& dump(String& buf);

    RadioBufDesc crt;
    RadioBufDesc aux;
    RadioBufDesc extra;

protected:
    unsigned int m_bufSamples;           // Buffers length in sample periods
    unsigned int m_validMin;             // Valid samples threshold
};


/**
 * @short Generic radio interface
 *
 * @note Some parameters are quantized by the radio hardware. If the caller requests a parameter
 * value that cannot be matched exactly, the setting method will set the parameter to the
 * best avaiable match and return "NotExact". For such parameters, there is a corresponding
 * readback method to get the actual value used.
 *
 * @note If a method does not include a radio port number, then that method applies to all connected ports.
 *
 * @note The interface may control multiple radios, with each one appearing as a port. However, in this case,
 *  - all radios are to be synched on the sample clock
 *  - all radios are to be of the same hardware type
 *
 * @note If the performance of the radio hardware changes, the API indicates this with the RFHardwareChange
 * flag. If this flag appears in a result code, the application should:
 *  - read a new RadioCapabilties object
 *  - revisit all of the application-level parameter settings on the radio
 *  - check the status() method on each port to identify single-port errors
 */
class YRADIO_API RadioInterface : public RefObject, public DebugEnabler
{
    YCLASS(RadioInterface,RefObject);
public:
    /** Error code bit positions in the error code mask. */
    enum ErrorCode {
	NoError = 0,
	Failure = (1 << 1),              // Unknown error
	HardwareIOError = (1 << 2),      // Communication error with HW
	NotInitialized = (1 << 3),       // Interface not initialized
	NotSupported = (1 << 4),         // Feature not supported
	NotCalibrated = (1 << 5),        // The radio is not calibrated
	TooEarly = (1 << 6),             // Timestamp is in the past
	TooLate = (1 << 7),              // Timestamp is in the future
	OutOfRange = (1 << 8),           // A requested parameter setting is out of range
	NotExact = (1 << 9),             // The affected value is not an exact match to the requested one
	DataLost = (1 << 10),            // Received data lost due to slow reads
	Saturation = (1 << 11),          // Data contain values outside of +/-1+/-j
	RFHardwareFail = (1 << 12),      // Failure in RF hardware
	RFHardwareChange = (1 << 13),    // Change in RF hardware, not outright failure
	EnvironmentalFault = (1 << 14),  // Environmental spec exceeded for radio HW
	InvalidPort = (1 << 15),         // Invalid port number
	Pending = (1 << 16),             // Operation is pending
	Cancelled = (1 << 17),           // Operation cancelled
	Timeout = (1 << 18),             // Operation timeout
	HardwareNotAvailable = (1 << 19),// Device not found
	InsufficientSpeed = (1 << 20),   // Device has insufficient speed to fulfill required transfer rate
	// Masks
	// Errors indicating automatic application restart should not be performed
	// These may indicate misconfig or hardware failures requiring system restart
	NoAutoRestartMask = HardwareNotAvailable | InsufficientSpeed,
	// Errors requiring radio or port shutdown
	FatalErrorMask = HardwareIOError | RFHardwareFail | EnvironmentalFault | Failure |
	    NoAutoRestartMask,
	// Errors that can be cleared
	ClearErrorMask = TooEarly | TooLate | NotExact | DataLost | Saturation |
	    InvalidPort | Timeout,
	// Errors that are specific to a single call
	LocalErrorMask = NotInitialized | NotCalibrated | TooEarly | TooLate | OutOfRange |
	    NotExact | DataLost | Saturation | RFHardwareChange | InvalidPort,
    };

    /**
     * Operations
     * Used to handle pending state
     */
    enum Operation {
	PendingInitialize = 0,           // initialize() is pending
	PendingCount,
    };

    /**
     * Poll for a pending operation
     * @param oper Operation to check
     * @param waitMs Optional time (in milliseconds) to wait for operation completion
     * @return Error code (0 on success)
     */
    unsigned int pollPending(unsigned int oper, unsigned int waitMs = 0);

    /**
     * Retrieve the radio device path
     * @param devicePath Destination buffer
     * @return Error code (0 on success)
     */
    virtual unsigned int getInterface(String& devicePath) const
	{ return NotSupported; }

    /**
     * Retrieve radio capabilities
     * @return RadioCapability pointer, 0 if unknown or not implemented
     */
    virtual const RadioCapability* capabilities() const
	{ return m_radioCaps; }

    /**
     * Initialize the radio interface.
     * Any attempt to transmit or receive prior to this operation will return NotInitialized.
     * @param params Optional parameters list
     * @return Error code (0 on success)
     */
    virtual unsigned int initialize(const NamedList& params = NamedList::empty()) = 0;

    /**
     * Set radio loopback
     * @param name Loopback name (NULL for none)
     * @return Error code (0 on success)
     */
    virtual unsigned int setLoopback(const char* name = 0)
	{ return NotSupported; }

    /**
     * Set multiple interface parameters.
     * Each command must start with 'cmd:' to allow the code to detect unhandled commands.
     * Command sub-params should not start with the prefix
     * @param params Parameters list
     * @param shareFate True to indicate all parameters share the fate
     *  (return on first error, except for Pending), false to process all
     * @return Error code (0 on success). Failed command(s) will be set in the list
     *  with cmd_name.code=error_code
     */
    virtual unsigned int setParams(NamedList& params, bool shareFate = true) = 0;

    /**
     * Update (set/reset) interface data dump
     * @param dir Direction to update. 0: both, negative: RX only, positive: TX only
     * @param level Dump level to update. 0: both, negative: interface level (data
     *  sent/received by the upper layer), positive: device level (data sent to or
     *  read from radio device)
     * @param params Optional parameters
     * @return Error code (0 on success)
     */
    virtual unsigned int setDataDump(int dir = 0, int level = 0,
	const NamedList* params = 0) = 0;

    /**
     * Run internal calibration procedures and/or load calibration parmameters
     * @return Error code (0 on success)
     */
    virtual unsigned int calibrate()
	{ return NotSupported; }

    /**
     * Set the number of ports to be used
     * @param count The number of ports to be used
     * @return Error code (0 on success)
     */
    virtual unsigned int setPorts(unsigned count) = 0;

    /**
     * Return any persistent error codes.
     * ("Persistent" means a condition of the radio interface itself,
     * as opposed to a status code related to a specific method call)
     * @param port Port number to check, or -1 for all ports OR'd together
     * @return Error(s) mask
     */
    virtual unsigned int status(int port = -1) const = 0;

    /**
     * Clear all error codes that can be cleared.
     * Note the not all codes are clearable, like HW failures for example.
     */
    virtual void clearErrors()
	{ m_lastErr &= ~ClearErrorMask; }

    /**
     * Send a frame of complex samples at a given time, interleaved IQ format.
     * If there are gaps in the sample stream, the RadioInterface must zero-fill.
     * All ports are sent together in interleaved format; example:
     * I0 Q0 I1 Q1 I2 Q2 I0 Q0 I1 Q1 I2 Q2, etc for a 3-port system
     * Block until the send is complete.
     * Return TooEarly if the blocking time would be too long.
     * Return TooLate if any of the block is in the past.
     * @param when Time to schedule the transmission
     * @param samples Data to send (array of 2 * size * ports floats, interleaved IQ)
     * @param size The number of sample periods in the samples array
     * @param powerScale Optional pointer to power scale value
     * @return Error code (0 on success)
     */
    virtual unsigned int send(uint64_t when, float* samples, unsigned size,
	float* powerScale = 0) = 0;

    /**
     * Receive the next available samples and associated timestamp.
     * All ports are received together in interleaved format.
     *  e.g: I0 Q0 I1 Q1 I2 Q2 I0 Q0 I1 Q1 I2 Q2, etc for a 3-port system.
     * The method will wait for proper timestamp (to be at least requested timestamp).
     * The caller must increase the timestamp after succesfull read.
     * The method may return less then requested size.
     * @param when Input: current timestamp. Output: read data timestamp
     * @param samples Destination buffer (array of 2 * size * ports floats, interleaved IQ)
     * @param size Input: requested number of samples. Output: actual number of read samples
     * @return Error code (0 on success)
     */
    virtual unsigned int recv(uint64_t& when, float* samples, unsigned& size) = 0;

    /**
     * Receive the next available samples and associated timestamp.
     * Compensate timestamp difference.
     * Copy any valid data in the future to auxiliary buffers.
     * Adjust the timestamp (no need for the caller to do it).
     * Handles buffer rotation also.
     * All sample counters (length/offset) are expected in sample periods.
     * @param when Input: current timestamp. Output: next read data timestamp
     * @param bufs Buffers to use
     * @param skippedBufs The number of skipped full buffers
     * @return Error code (0 on success)
     */
    virtual unsigned int read(uint64_t& when, RadioReadBufs& bufs,
	unsigned int& skippedBufs);

    /**
     * Get the time of the data currently being received from the radio
     * @param when Destination buffer for requested radio time
     * @return Error code (0 on success)
     */
    virtual unsigned int getRxTime(uint64_t& when) const = 0;

    /**
     * Get the time of the the data currently being sent to the radio
     * @param when Destination buffer for requested radio time
     * @return Error code (0 on success)
     */
    virtual unsigned int getTxTime(uint64_t& when) const = 0;

    /**
     * Set the frequency offset
     * @param offs Frequency offset to set
     * @param newVal Optional pointer to value set by interface (requested value may
     *  be adjusted to fit specific device value)
     * @return Error code (0 on success)
     */
    virtual unsigned int setFreqOffset(float offs, float* newVal = 0) = 0;

    /**
     * Set the sample rate
     * @param hz Sample rate value in Hertz
     * @return Error code (0 on success)
     */
    virtual unsigned int setSampleRate(uint64_t hz) = 0;

    /**
     * Get the actual sample rate
     * @param hz Sample rate value in Hertz
     * @return Error code (0 on success)
     */
    virtual unsigned int getSampleRate(uint64_t& hz) const = 0;

    /**
     * Set the anti-aliasing filter BW
     * @param hz Anti-aliasing filter value in Hertz
     * @return Error code (0 on success)
     */
    virtual unsigned int setFilter(uint64_t hz) = 0;

    /**
     * Get the actual anti-aliasing filter BW
     * @param hz Anti-aliasing filter value in Hertz
     * @return Error code (0 on success)
     */
    virtual unsigned int getFilterWidth(uint64_t& hz) const = 0;

    /**
     * Set the transmit frequency in Hz
     * @param hz Transmit frequency value
     * @return Error code (0 on success)
     */
    virtual unsigned int setTxFreq(uint64_t hz) = 0;

    /**
     * Readback actual transmit frequency
     * @param hz Transmit frequency value
     * @return Error code (0 on success)
     */
    virtual unsigned int getTxFreq(uint64_t& hz) const = 0;

    /**
     * Set the output power in dBm.
     * This is power per active port, compensating for internal gain differences.
     * @param dBm The output power to set
     * @return Error code (0 on success)
     */
    virtual unsigned int setTxPower(unsigned dBm) = 0;

    /**
     * Set the receive frequency in Hz
     * @param hz Receive frequency value
     * @return Error code (0 on success)
     */
    virtual unsigned int setRxFreq(uint64_t hz) = 0;

    /**
     * Readback actual receive frequency
     * @param hz Receive frequency value
     * @return Error code (0 on success)
     */
    virtual unsigned int getRxFreq(uint64_t& hz) const = 0;

    /**
     * Set the transmit pre-mixer gain in dB wrt max
     * @return Error code (0 on success)
     */
    virtual unsigned int setTxGain1(int val, unsigned port)
	{ return NotSupported; }

    /**
     * Set the transmit post-mixer gain in dB wrt max
     * @return Error code (0 on success)
     */
    virtual unsigned int setTxGain2(int val, unsigned port)
	{ return NotSupported; }

    /**
     * Set the receive pre-mixer gain in dB wrt max
     * @return Error code (0 on success)
     */
    virtual unsigned int setRxGain1(int val, unsigned port)
	{ return NotSupported; }

    /**
     * Set the receive post-mixer gain in dB wrt max
     * @return Error code (0 on success)
     */
    virtual unsigned int setRxGain2(int val, unsigned port)
	{ return NotSupported; }

    /**
     * Automatic tx/rx gain setting
     * Set post mixer value. Return a value to be used by upper layer
     * @param tx Direction
     * @param val Value to set
     * @param port Port to use
     * @param newVal Optional pointer to value to use for calculation by the upper layer
     * @return Error code (0 on success)
     */
    virtual unsigned int setGain(bool tx, int val, unsigned int port,
	int* newVal = 0) const
	{ return NotSupported; }

    /**
     * Retrieve the interface name
     * @return Interface name
     */
    virtual const String& toString() const;

    /**
     * Complete device info parameters
     * @param p Destination list
     * @param full Put full device info
     * @param retData True to set out pointer in destination, false to add name only
     */
    virtual void completeDevInfo(NamedList& p, bool full = false, bool retData = false);

    /**
     * Fill (set) error related parameters
     * @param p Destination list
     * @param code Error code
     * @param str Optional error string (not error name)
     */
    virtual void setError(NamedList& p, unsigned int code, const char* str = 0);

    /**
     * Retrieve the error string associated with a specific code
     * @param code Error code
     * @param defVal Optional default value to retrieve if not found
     * @return Valid TokenDict pointer
     */
    static inline const char* errorName(int code, const char* defVal = 0)
	{ return lookup(code,errorNameDict(),defVal); }

    /**
     * Retrieve the error name dictionary
     * @return Valid TokenDict pointer
     */
    static const TokenDict* errorNameDict();

protected:
    /**
     * Constructor
     * @param name Interface name
     */
    RadioInterface(const char* name);

    /**
     * Set pending state
     * @param oper Operation to set
     * @param code Status code
     */
    inline void setPending(unsigned int oper, unsigned int code = Pending) {
	    Lock lck(m_mutex);
	    if (oper < PendingCount)
		m_pendingCode[oper] = code;
	}
	
    unsigned int m_lastErr;               // Last error that appeared during functioning
    unsigned int m_totalErr;              // All the errors that appeared
    RadioCapability* m_radioCaps;         // Radio capabilities

private:
    String m_name;
    Mutex m_mutex;
    unsigned int m_pendingCode[PendingCount];
};


/**
 * @short Radio data file header
 * This class describes records in radio data files
 */
class YRADIO_API RadioDataDesc
{
public:
    /**
     * Samples data type
     */
    enum ElementType {
	Float = 0,
	Int16 = 1,
    };

    /**
     * Timestamp type
     */
    enum TsType {
	TsApp = 0,                       // Application level timestamp
	TsBoard = 1,                     // Board (device) level timestamp
    };

    /**
     * Constructor
     * @param eType Element type
     * @param tsType Racords timestamp type
     * @param sLen Sample length in elements
     * @param ports Number of device ports
     */
    inline RadioDataDesc(uint8_t eType = Float, uint8_t tsType = TsApp,
	uint8_t sLen = 2, uint8_t ports = 1)
	: m_elementType(eType), m_sampleLen(sLen), m_ports(ports ? ports : 1),
	m_tsType(tsType),
#ifdef LITTLE_ENDIAN
	m_littleEndian(true)
#else
	m_littleEndian(false)
#endif
	{
	    m_signature[0] = 'Y';
	    m_signature[1] = 'R';
	    m_signature[2] = 0;
	}

    uint8_t m_signature[3];              // File signature
    uint8_t m_elementType;               // Element data type
    uint8_t m_sampleLen;                 // Sample length in elements
    uint8_t m_ports;                     // The number of ports
    uint8_t m_tsType;                    // Records timestamp type
    bool m_littleEndian;                 // Endiannes
};


/**
 * @short Radio data file helper
 * This class implements utilities used to read or write radio data to/from file
 * The String contains object name used for debug
 */
class YRADIO_API RadioDataFile : public String
{
public:
    /**
     * Constructor
     * @param name Name used for debug
     * @param dropOnError Terminate on file/data error
     */
    RadioDataFile(const char* name, bool dropOnError = true);

    /**
     * Destructor
     */
    virtual ~RadioDataFile();

    /**
     * Retrieve data description
     * @return Data description object
     */
    inline const RadioDataDesc& desc() const
	{ return m_header; }

    /**
     * Check if enabled
     * @return True if enabled, false otherwise
     */
    inline bool valid() const
	{ return m_file.valid(); }

    /**
     * Check if machine endiannes is the same as file endiannes
     * @return True if they are the same
     */
    inline bool sameEndian() const
	{ return m_littleEndian == m_header.m_littleEndian; }

    /**
     * Open a file for read/write. Terminate current data dump if any.
     * Write or read the file header
     * @param fileName File to open
     * @param data Data description. Pass a NULL pointer for read or valid data for write
     * @param dbg Optional DebugEnabler pointer (show debug on failure)
     * @param error Optional destination for file operation error code
     * @return True on success, false on failure (read: error set to 0 means
     *  invalid file size, write: error set to 0 means invalid header)
     */
    bool open(const char* fileName, const RadioDataDesc* data,
	DebugEnabler* dbg = 0, int* error = 0);

    /**
     * Write a record to file
     * @param ts Record timestamp
     * @param buf Buffer to write
     * @param len Buffer length in bytes
     * @param dbg Optional DebugEnabler pointer (show debug on failure)
     * @param error Optional destination for file operation error code
     * @return True on success, false on failure (error set to 0 means invalid length)
     */
    bool write(uint64_t ts, const void* buf, uint32_t len, DebugEnabler* dbg = 0,
	int* error = 0);

    /**
     * Read a record from file
     * The method don't check the record length, this must be done by the upper layer
     * @param ts Record timestamp
     * @param buffer Destination buffer. It will be resized to read data length
     * @param dbg Optional DebugEnabler pointer (show debug on failure)
     * @param error Optional destination for file operation error code
     * @return True on success (empty buffer means file EOF),
     *  false on failure (error set to 0 means invalid record size)
     */
    bool read(uint64_t& ts, DataBlock& buffer, DebugEnabler* dbg = 0, int* error = 0);

    /**
     * Terminate data dump, close file
     * @param dbg Optional DebugEnabler pointer (show terminate message)
     */
    void terminate(DebugEnabler* dbg = 0);

    /**
     * Convert endiannes
     * @param buf The buffer to convert
     * @param bytes Element length in bytes
     * @return True on success, false if not supported
     */
    static bool fixEndian(DataBlock& buf, unsigned int bytes);

protected:
    bool ioError(bool send, DebugEnabler* dbg, int* error, const char* extra);

    bool m_littleEndian;                 // Machine endiannes
    bool m_dropOnError;                  // Terminate (close file) on error
    uint32_t m_chunkSize;                // Item size (used to check data validity)
    RadioDataDesc m_header;              // File header
    File m_file;                         // File to use
    DataBlock m_writeBuf;
};

}; // namespace TelEngine

#endif /* __YATERADIO_H */

/* vi: set ts=8 sw=4 sts=4 noet: */
