/*****************************************************************************/
/*
 * Import header for StreamParser version 1.1
 * 
 * Manufacturer
 * 
 *    SCANLAB GmbH
 *    Siemensstr. 2a
 *    82178 Puchheim
 *    Germany
 * 
 *    Tel. + 49 (89) 800 746-0
 *    Fax: + 49 (89) 800 746-199
 * 
 *    info@scanlab.de
 *    www.scanlab.de
 * 
 */
/*****************************************************************************/

#ifndef STREAM_PARSER_CPP_HEADER
#define STREAM_PARSER_CPP_HEADER

#include <cstdint>
#include <functional>
#include <string>
#include <memory>
#include <future>
#include <variant>

#include "StreamParser_Export.h"

namespace slsp
{

/*! Structure holding version information.
*/
struct VersionInfo
{
    uint32_t Major;
    uint32_t Minor;
    uint32_t Patch;
};

/*! Structure holding meta data send with every data package by the Provider.
*/
struct RtcMetaData
{
    uint32_t Period;
    uint64_t TimeStamp;
    uint32_t SerialNumber;
    uint32_t OUTVersion;
    uint32_t RBFVersion;
    uint32_t BiosVersion;
    uint32_t ListBusy;
    uint32_t OutputPointer;
    uint32_t ExtStartCounter;
    uint32_t OverrunCounter;
    uint32_t McBSPIn;
    double Laptime;
    double Timer;
    uint32_t WaitStatus;
    uint32_t FlyOffsetX;
    uint32_t FlyOffsetY;
    uint16_t ListStartCounter;
    uint16_t ListStopCounter;
    uint32_t MasterIP;
    uint32_t HeadStatus;
};

/*! Returns the API version (major.minor.patch)
* 
* The API is versioned according to Semantic Versioning (https://semver.org/).
* @returns Returns a slsp_VersionInfo struct that incorporates the major, minor and patch number.
*/
DLLAPI VersionInfo getVersionInfo();

/*! Abstract class for exceptions thrown by this library.
*/
class DLLAPI StreamParserException
{
public:
    enum class ErrorCode : uint32_t
    {
        NO_ERROR = 0,                    /*!< No error.*/
        ESTABLISH_CONNECTION_FAILED = 1, /*!< Connection to RTC6 Ethernet card could not be established.*/
        RECEIVE_PACKAGE_FAILED = 2,      /*!< Error while trying to receive a data package from RTC6 Ethernet card.*/
        UNKNOWN_ERROR = 3,               /*!< An unspecified error has occurred.*/
        RTC_VERSION_ERROR = 4            /*!< The version of the connected RTC is not compatible with this stream parser version.*/
    };

    virtual ~StreamParserException() noexcept = default;

    /*! @returns Returns string describing the type of error.*/
    virtual const char* what() const noexcept = 0;
    virtual ErrorCode getErrorCode() const noexcept = 0;
};

/*! Abstract class representing a section of the RtcDataStream.
* 
* A RtcDataPackage is a collection of RtcDataPoints.
* The number of RtcDataPoints in a RtcDataPackage can be queried via the member function size().
* Index based access to the individual RtcDataPoints is provided via the member function getRtcDataPoint().
* 
* It is possible to create a copy of a RtcDataPackage with the member function copy().
*/
class DLLAPI RtcDataPackage
{
public:
    virtual ~RtcDataPackage() noexcept = default;

    /*! @returns Returns the number of elements per channel in this package.
    */
    virtual size_t size() const = 0;

    /*! @returns Returns the number of DataPoints accumulated in this DataPackage.
    */
    virtual uint32_t getChannelCount() const = 0;
    /*! 
    * 
    * @param Channel 
    * @returns Returns an array 
    */
    virtual const std::vector<int32_t>& accessWaveformData(size_t Channel) const = 0;

    /*! Index based access to the accumulated DataPoints.
    * 
    * @param Index Position in the internal buffer of the DataPackage. Must be smaller than the value returned by size().
    * @returns Returns a pointer to the requested DataPoint.
    */
    virtual uint32_t getRtcWaveformType(size_t Channel) const = 0;

    /*! @return Returns a pointer to the rtc meta data.
    */
    virtual const RtcMetaData& getRtcMetaData() const = 0;
};

/*! Abstract class representing a section of the data stream.
* 
* A RtcDataStream is a collection of RtcDataPackages.
* The number of RtcDataPackages in a RtcDataStream can be queried via the member function size().
* The RtcDataPackages can be retrieved with pop.
* 
* It is possible to create a copy of a DataPackage with the member function copy().
*/
class DLLAPI RtcDataStream
{
public:
    virtual ~RtcDataStream() noexcept = default;

    /*! @returns Returns the number of remaining DataPackages
    * 
    * The size is reduced with each RtcDataStream::pop call by 1.
    */
    virtual size_t size() const = 0;

    /*! Returns the next RtcDataPackage
    * 
    * If there are no more packages. pop returns a nullptr.
    * 
    * @returns Returns a unique pointer to the next RtcDataPackage.
    */
    virtual std::unique_ptr<RtcDataPackage> pop() = 0;
};

/*! Abstract class representing a streaming session.
* 
* The streaming session starts on creation of a StreamParser instance.
* The streaming session stops at the latest on destruction of the associated StreamParser object.
* It may stop earlier due to a proper disconnect on RTC6 Ethernet side or because an error has occurred.
*
* The member function getState() returns the state of the associated streaming session.
* The member function rethrow() rethrows the exception if any was thrown in the extra thread of the associated streaming session.
*/
class DLLAPI StreamParser
{
public:
    virtual ~StreamParser() noexcept = default;
    /*! Enum representing the state of the streaming session.
    */
    enum class State : uint32_t
    {
        CONNECTED = 0,       /*!< TCP client is connected and ready to receive data. */
        RECEIVING = 1,       /*!< Stream is currently running, i.e. data packages are received from RTC6 Ethernet card and the StreamCallback is called repeatedly.  */
        DISCONNECTED = 2,    /*!< Stream has finished properly, because the RTC6 Ethernet card has stopped sending data packages or disconnect request has been sent. */
        STOPPED_ON_ERROR = 3 /*!< Stream has stopped, because an error has occurred.*/
    };

    /*! Starts the streaming session by establishing a connection to the TCP server (May throw)
    */
    virtual void connect() = 0;

    /*! Ends the streaming session by disconnecting from the TCP server (May throw)
    */
    virtual void disconnect() = 0;

    /*! Checks if the streaming session is connected (CONNECTED or RECEIVING)
     */
    virtual bool isConnected() const = 0;

    /*! @returns Returns the State of the associated streaming session.
    */
    virtual State getState() const = 0;

    /*! Rethrows the exception if any was thrown in the extra thread of the associated streaming session.
    */
    virtual void rethrow() const = 0;

    /*! Changes the default value of TCP connection timeout. (default = 10s)
    */
    virtual void setTcpTimeout(uint32_t Seconds) = 0;

    /*! Changes the default number of packages, which are passed to the callback handler.
    * Call this before calling <connect>"()"
    * @param NewPackageCounter Number of packages for the callback handler. Must be greater than 0. (default = 25)
    */
    virtual void setPackageCounter(uint32_t NewPackageCounter) = 0;

    /*! Sets the time period for the periodic timer, which reads the current stream and triggers the callback handler.
    * Call this before calling <connect>"()"
    * @param NewWaitTimeMs Time period for the periodic triggering of the callback handler. Must be greater than 0. (default = 250ms)
    */
    virtual void setWaitTimeMs(uint32_t NewWaitTimeMs) = 0;
};

/*! Defines the type of the function that gets called repeatedly during a streaming session.
* It is a type alias for a std::function, which wraps a function that takes a pointer to a DataPackage instance.
*/

using StreamCallback = std::function<void(RtcDataStream*)>;


/*! Start a streaming session by creating a StreamParser instance.
* 
* Ownership of the StreamParser instance is transferred to the caller via an unique_ptr.
* The caller ends the streaming session properly by deleting the StreamParser instance through this unique_ptr.
* 
* Throws a StreamParser exception if a connection to the RTC6 Ethernet card cannot be established.
* 
* During a streaming session, the user defined StreamCallback is called repeatedly after the specified number of DataPoints (MinDataPointsTillCallback)
* have been accumulated in a DataPackage. A pointer to this DataPackage is then passed to the StreamCallback for user defined processing.
* It is recommended to only call DataPackage::copy inside the StreamCallback and process the data in an extra thread.
* 
* @param  IpAddress IP address of the RTC6 Ethernet card to connect with.
* @param  CallbackHandler User defined StreamCallback.
* @returns Returns a std::unique_ptr to a StreamParser instance.
*/
DLLAPI std::unique_ptr<StreamParser> createStreamParser(const std::string& IpAddress,
                                                           StreamCallback CallbackHandler);


}

#endif // STREAM_PARSER_CPP_HEADER
