//
// Copyright (C) ValoFly GmbH - All Rights Reserved
//
/** \file
 * TetherCom Library header file
 *
 * Declaration of TetherCom Library functionality for implementation in software projects which have to communicate with ValoFly Tether\.Solutions Ground Stations.
 * For more details how to integrate TetherCom Library into a software project see \ref package_usage "TetherCom Library package usage".
 */

#ifndef _VALOFLY_TETHERCOM_LIB_HEADER_
#define _VALOFLY_TETHERCOM_LIB_HEADER_

#include <TetherCom/TetherComDefinitions.h>

#include <exception>
#include <vector>
#include <string>
#include <sstream>
#include <iostream>

/// Namespace for classes and members developed by ValoFly GmbH
namespace VALOFLY {

    /// IO handling exception class
    /** Exception class to represent exceptions in IO handling. */
    class IOException : public std::exception {
        // Disable copy constructors
        IOException& operator=(const IOException&);
        std::string _exWhat;

    public:
        /**
        * Instance a new IOException object
        *
        * @param[in] description Character array to describe what rises IOException
        */
        explicit IOException(const char* description)
        {
            std::stringstream ss;
            ss << "IO Exception: " << description;
            _exWhat = ss.str();
        }
        /**
        * Instance a new IOException object
        *
        * @param[in] other Create new instance from a given IOException object
        */
        IOException(const IOException& other) : _exWhat(other._exWhat) {}

        virtual ~IOException() noexcept {}

        /**
        * Returns a description of IOException object
        *
        * @return Character array of IOException description
        */
        virtual const char* what() const throw () {
            return _exWhat.c_str();
        }
    };

    /// Data handling exception class
    /** Exception class to represent exceptions in data handling. */
    class DataException : public std::exception {
        // Disable copy constructors
        DataException& operator=(const DataException&);
        std::string _exWhat;

    public:
        /**
        * Instance a new DataException object
        *
        * @param[in] description Character array to describe what rises DataException
        */
        explicit DataException(const char* description)
        {
            std::stringstream ss;
            ss << "Data Stream Exception: " << description;
            _exWhat = ss.str();
        }
        /**
        * Instance a new DataException object from given DataException object
        *
        * @param[in] other Create new instance from a given DataException object
        */
        DataException(const DataException& other) : _exWhat(other._exWhat) {}

        virtual ~DataException() noexcept {}

        /**
        * Returns a description of DataException object
        *
        * @return Character array of DataException description
        */
        virtual const char* what() const throw () {
            return _exWhat.c_str();
        }
    };

    /// Communication class for ValoFly Tether\.Solutions Ground Stations
    /** Base class to establish communication to ValoFly Tether\.Solutions Ground Station. */
    class TetherCom
    {
    public:
        /******************************************************************/
        /*                Public communication definitions                */
        /******************************************************************/

        /// Configuration parameter structure
        /** Data struct to setup a set of configuration parameters to transmit at once to ValoFly Tether\.Solutions Ground Station. \n
            Data types of members are directly compatible with responded single set configuration function like @ref setSystemActivation() or @ref setCableRetractionTorque().
        */
        struct systemConfig //
        {
            bool systemActivation;              /**< Config activation of Ground Station [on/off] &rArr; [true/false] */
            float cableRetractionTorque;        /**< Config cable retraction torque in percent, 0 &rArr; no retraction / 100 &rArr; max retraction, precision 1% */
            float cableRetractionStartLength;   /**< Config unwounded cable length in meter from which cable retraction will be activated, precision 0.001m */
            int cableRetractionStatus;          /**< Configuration status of cable retraction system, only set by ValoFly Tether\.Solutions Ground Station on request */
        };

        /** Data structs to represent current status of ValoFly Tether\.Solutions tether cable */
        struct cableStatus
        {
            float lengthUnwound = 0;            /**< Length of currently unwounded tether cable in meter, precision 0.01m */
            float speed = 0;                    /**< Velocity of which tether cable is currently (un)wounded in meters per seconds */
            int retractionStatus = static_cast<int>(TetherComDefinitions::cableRetractionStatus::not_available);    /**< Cable retraction status of Ground Station, value referenced to cableRetraction */
            float retractionTorque = 0;         /**< Setting of cable retraction torque in percent, 0 &rArr; no retraction / 100 &rArr; max retraction, precision 1% */
            float retractionStartLength = 0;    /**< Setting of unwounded cable length in meter from which cable retraction will be activated, precision 0.01m */
            int tetherStatus = static_cast<int>(TetherComDefinitions::cableTetherStatus::failure);      /**< Status of tether cable */
            int guideStatus = static_cast<int>(TetherComDefinitions::cableGuideStatus::not_available);  /**< Status of tether cable guide */
            int reelStatus = static_cast<int>(TetherComDefinitions::cableReelStatus::not_available);    /**< Status of tether cable reel */
        };

        /** Data structs to represent current status of ValoFly Tether\.Solutions Ground Station operational environment*/
        struct envStatus
        {
            float temp1 = -128;     /**< Temperature at Ground Station system PCB in °C, initialize to not available */
            float temp2 = -128;     /**< Temperature at temp sensor 2 in °C, initialize to not available */
            float temp3 = -128;     /**< Temperature at temp sensor 3 in °C, initialize to not available */
            float temp4 = -128;     /**< Temperature at temp sensor 4 in °C, initialize to not available */
            float humidity1 = -128;  /**< Humidity at Ground Station in %, initialize to not available */
            float pressure1 = 0;     /**< Air pressure at Ground Station in hPa */
            int envSensor1Status = static_cast<int>(TetherComDefinitions::envSensorStatus::not_available);   /**< Status of environment sensor 1 */
            int envSensor2Status = static_cast<int>(TetherComDefinitions::envSensorStatus::not_available);   /**< Status of environment sensor 2 */
            int envSensor3Status = static_cast<int>(TetherComDefinitions::envSensorStatus::not_available);   /**< Status of environment sensor 3 */
            int envSensor4Status = static_cast<int>(TetherComDefinitions::envSensorStatus::not_available);   /**< Status of environment sensor 4 */
            int fan1RPM = 0;        /**< RPM of Ground Station system fan 1 */
            int fan1Status = static_cast<int>(TetherComDefinitions::fanStatus::not_available);  /**< Status of Ground Station system fan 1 */
            int fan2RPM = 0;        /**< RPM of Ground Station system fan 2 */
            int fan2Status = static_cast<int>(TetherComDefinitions::fanStatus::not_available);  /**< Status of Ground Station system fan 2 */
        };

        /** Data structs to represent current status of ValoFly Tether\.Solutions Ground Stations supplies */
        struct energyGSStatus
        {
            float mainsU = 0;               /**< Voltage of Ground Station main supply in V */
            float mainsI = 0;               /**< Current of Ground Station main supply in A */
            float mainsF = 0;               /**< Frequency of Ground Station main supply in Hz */
            int mainsStatus = static_cast<int>(TetherComDefinitions::supplyStatus::not_available);      /**< Status of Ground Station main supply */
            int ctrlSupplyStatus = static_cast<int>(TetherComDefinitions::supplyStatus::not_available); /**< Status of system control supplies */
            float backupBatteryVoltage = static_cast<float>(TetherComDefinitions::backupBatteryStatus::not_available);  /**< Voltage of backup battery of Ground Station in percent, 0 &rArr; Battery empty / 100 &rArr; Battery full charged, precision 1%. In case of any error value is 127. If there is no backup battery available in the system configuration, value is 255 */
            int backupBatteryStatus = static_cast<int>(TetherComDefinitions::backupBatteryStatus::not_available);       /**< Status of backup battery of Ground Station, value referenced to backupBatteryStatus */
        };

        /** Data structs to represent current status of ValoFly Tether\.Solutions UAV supply */
        struct energyUAVStatus
        {
            float uavVoltage = 0;           /**< UAV voltage consumption in V */
            float uavCurrent = 0;           /**< UAV current consumption in A */
            float uavPower = 0;             /**< UAV power consumption in W */
            int extSupplyStatus = static_cast<int>(TetherComDefinitions::extUAVSupplyStatus::supply_error); /**< Status of external UAV supply (supply via tether cable) */
            int uavBatCellCount = 0;        /**< Amount of cells of UAV battery */
        };

        /** Data structs to represent current operation states of ValoFly Tether\.Solutions Ground Station */
        struct systemStates
        {
            int activation = static_cast<int>(TetherComDefinitions::systemActivationStatus::off);   /**< Activation status of Ground Station, value referenced to systemActivationStatus */
            int retractionStatus = static_cast<int>(TetherComDefinitions::cableRetractionStatus::not_available);    /**< Cable retraction status of Ground Station, value referenced to cableRetraction */
            int backupBatteryStatus = static_cast<int>(TetherComDefinitions::backupBatteryStatus::not_available);   /**< Status of backup battery of Ground Station, value referenced to backupBatteryStatus */
            uint32_t systemTime = 0;                /**< Time since last mains supply of Ground Station in seconds */
            uint32_t operationTime = 0;             /**< Time since last activation of Ground Station in seconds */
            uint32_t flightTime = 0;                /**< Time since last flight begin until flight end in seconds */
            uint32_t operationFlightTimeSum = 0;    /**< Time sum of all flights during operation in seconds (since activation of Ground Station) */
            uint32_t operationFlightCountSum = 0;   /**< Count of all flights during operation in seconds (since activation of Ground Station) */
            int error = static_cast<int>(TetherComDefinitions::systemError::generic_error); /**< Flag for error states */
        };

        /** Data structs to setup and review content of system status messages of ValoFly Tether\.Solutions Ground Station */
        struct systemStatusConfig
        {
            int cableEnabled = 0;           /**< Flag to control content of receiveded Status Data, controls (enable/disable or notice of system side availability) cableStatus */
            int envEnabled = 0;             /**< Flag to control content of receiveded Status Data, controls (enable/disable or notice of system side availability) envStatus */
            int energyGSEnabled = 0;        /**< Flag to control content of receiveded Status Data, controls (enable/disable or notice of system side availability) energyGSStatus */
            int energyUAVEnabled = 0;       /**< Flag to control content of receiveded Status Data, controls (enable/disable or notice of system side availability) energyUAVStatus */
            int systemStatesEnabled = 0;    /**< Flag to control content of receiveded Status Data, controls (enable/disable or notice of system side availability) systemStates */
        };

        /** Data structs to represent current system status of ValoFly Tether\.Solutions Ground Station */
        struct systemStatus
        {
            systemStatusConfig conf;    /**< Data struct of configuration of system status messages */
            cableStatus cable;          /**< Data struct of tether cable status */
            envStatus env;              /**< Data struct of Ground Station operational environment */
            energyGSStatus energyGS;    /**< Data struct of Ground Station supply status */
            energyUAVStatus energyUAV;  /**< Data struct of UAV tether supply */
            systemStates sysStates;     /**< Data struct of system operation states */
        };

        /** Data struct of product details of ValoFly Tether\.Solutions Ground Station */
        struct systemDetails
        {
            std::string productType = "";       /**< Type of ValoFly Tether\.Solutions product */
            std::string model = "";             /**< Model code of ValoFly Tether\.Solutions product */
            std::string serialNumber = "";      /**< Serial number of ValoFly Tether\.Solutions product */
            std::string dayOfProduction = "";   /**< Day of production of ValoFly Tether\.Solutions product */
            std::string hardwareVersion = "";   /**< Version of hardware design of ValoFly Tether\.Solutions product */
            /** Data struct of equipped software versions of ValoFly Tether\.Solutions Ground Station */
            struct softwareVersions
            {
                std::string mainCtrl = "";          /**< Software version of main control unit */
                std::string reelCtrl = "";          /**< Software version of tether reel control unit */
                std::string lpwrCtrl = "";          /**< Software version of low power control unit */
                std::string hpwrCtrl = "";          /**< Software version of high power control unit */
                std::string envCtrl = "";           /**< Software version of environment measurement control unit */
                std::string netCtrl = "";           /**< Software version of network control unit */
                std::string backupBatteryCtrl = ""; /**< Software version of backup battery control unit */
            } softwareVersion;                   /**< Data struct of equipped software versions of ValoFly Tether\.Solutions Ground Station */
            std::string protocolVersion = "";   /**< Version of used communication protocol */
            std::string uavCellVoltages = "";   /**< By ValoFly Tether\.Solutions Ground Station supported UAV battery cell types / voltages */
            int maxPower = 0;                   /**< Maximum power of ValoFly Tether\.Solutions Ground Station */
            std::string cableType = "";         /**< Installed tether cable type */
            int maxCableLength = 0;             /**< Maximum length of installed tether cable */
            int networkType = static_cast<int>(TetherComDefinitions::systemNetworkType::not_available); /**< Installed network type */
            uint32_t flightTimeSum = 0;         /**< Time sum of all operated flights (from start to land) in seconds */
            uint32_t flightCount = 0;           /**< Count of all operated flights (from start to land) */
            uint32_t operationTimeSum = 0;      /**< Time sum of all system operations (from activate to deactivate ValoFly Tether\.Solutions Ground Station) in seconds */
            uint32_t operationCount = 0;        /**< Count of all system operations (from activate to deactivate ValoFly Tether\.Solutions Ground Station) */
            uint32_t systemTimeSum = 0;         /**< Time sum of all system usages (from mains supplied to mains disconnected from ValoFly Tether\.Solutions Ground Station) in seconds */
            uint32_t systemUseCount = 0;        /**< Count of all system usages (from mains supplied to mains disconnected from ValoFly Tether\.Solutions Ground Station) in seconds */
            uint32_t timeToMaintenance = 0;     /**< Operation time to next maintenance in seconds, depends on operation and flight time */
            int maintenance_required = 0;       /**< Flag to show if maintenance is needed */
        };

    private:
        /**
        * Tether communication implementation
        */
        class TetherComImpl;
        TetherComImpl* _pImpl;

    public:
        TetherCom();
        TetherCom(TetherCom&&);
        TetherCom(const TetherCom&) = delete;   // non construction-copyable
        TetherCom& operator=(TetherCom&&) = default;    // move assignment operator
        TetherCom& operator=(const TetherCom&) = delete;    // non copyable
        ~TetherCom();

        /**
        * Provide hardware information of all available serial ports of the system.
        *
        * @param[out] devListPort List of available ports
        * @param[out] devListDesc Description of available ports
        * @param[out] devListHwID Hardware ID of available ports
        */
        void getSerialPortsInfo(std::vector<std::string>& devListPort, std::vector<std::string>& devListDesc, std::vector<std::string>& devListHwID);

        /** Print a list of all available serial ports of the system to standard output. */
        void printSerialPortList();

        /** Print a list of all available serial ports of the system to given output stream.
        *
        * @param[in] stream Output stream
        */
        void printSerialPortList(std::ostream& stream);

        /**
        * Open serial connection on given port.
        *
        * @param[in] portName Name of the serial port which should used to establish connection to ValoFly Tether\.Solutions Ground Station
        *
        * @throw IOException Throws IOException if connection could not established
        */
        void openConnection(const std::string& portName);

        /**
        * Close a open serial connection.
        *
        * @throw IOException Throws IOException if connection could not closed
        */
        void closeConnection();

        /**
        * Status of serial connection
        *
        * @return int Integer number represent connection status
        */
        int getConnectionStatus();

        /**
        * Status of serial connection
        *
        * @return bool true if connection is established, otherwise false
        */
        bool isConnected();

        /**
        * Set communication timeout.
        * On serial communication, timeout controls time to wait until incoming data buffer is checked for new data
        *
        * @param[in] timeout Communication timeout in ms
        */
        void setConnectionTimeout(const unsigned int timeout);

        /**
        * Get current configured communication timeout
        *
        * @return int Communication timeout in ms
        */
        int getConnectionTimeout();

        /**
        * Request product details of ValoFly Tether\.Solutions Ground Station.
        *
        * @param[out] sysDetails Struct of systemDetails which contains product details of ValoFly Tether\.Solutions Ground Station
        *
        * @throw IOException Throws IOException on any errors on serial communication
        * @throw DataException Throws DataException on any errors during data reception
        */
        void getSystemDetails(systemDetails& sysDetails);

        /**
        * Request current system status from ValoFly Tether\.Solutions Ground Station.
        *
        * @param[out] sysStatus Struct of systemStatus which represents current system status
        *
        * @throw IOException Throws IOException on any errors on serial communication
        * @throw DataException Throws DataException on any errors during data reception
        */
        void getSystemStatus(systemStatus& sysStatus);

        /**
        * Check if ValoFly Tether.Solutions System operates within specification. Request necessary system data directly from
        * Tether.Solutions Ground Station AND include check requirement of system maintenance
        * If system has error and warnings, only one errors are returned, since they have higher priority.
        * 
        * Errors occurred if:
        *   * Tether\.Solutions system report a system error
        *   * Tether cable is damaged
        *   * Tether cable guide reports an error
        *   * Tether cable reel reports an error
        *   * External UAV supply reports an error
        *   * Tether\.Solutions systems control supply reports an error
        *   * Tether\.Solutions systems mains supply reports an error
        *   * One of available fans reports error
        * 
        * Warnings occurred if:
        *   * Tether\.Solutions system report a system warning
        *   * Tether cable is at round about 80% or more (up to full length) unwounded
        *   * One of available environment sensors reports an error
        *   * Maintenance is required
        * 
        * @return bool Operation state, FALSE if system operates within specification, TRUE if system runs in warning or error state
        * 
        * @param[out] TetherComDefinitions::systemError Enum representative system error status
        * 
        * @throw IOException Throws IOException on any errors on serial communication
        * @throw DataException Throws DataException on any errors during data reception
        */
        bool checkSystemStatus(TetherComDefinitions::systemError & errorStatus);

        /**
        * Check if ValoFly Tether.Solutions System operates within specification based on given systemStatus struct.
        * Optionaly include requirement of system maintenance in system check.
        *
        * @return bool Operation state, FALSE if system operates within specification, TRUE if system runs in warning or error state
        *
        * @param[in] sysStatus Struct of systemStatus which represents current system status
        * @param[in] checkMaintenance Bool to activate additional maintenance check, default = FALSE
        * @param[out] TetherComDefinitions::systemError Enum representative system error status
        *
        * @throw IOException Throws IOException on any errors on serial communication
        * @throw DataException Throws DataException on any errors during data reception
        */
        bool checkSystemStatus(systemStatus& sysStatus, TetherComDefinitions::systemError& errorStatus, bool checkMaintenance = false);

        /**
        * Check if ValoFly Tether.Solutions System operates within specification based on given systemStatus and systemDetails struct.
        * Check will also include requirement of maintenance.
        *
        * @return bool Operation state, FALSE if system operates within specification, TRUE if system runs in warning or error state
        *
        * @param[in] sysStatus Struct of systemStatus which represents current system status
        * @param[in] sysDetails Struct of systemDetails which contains product details of ValoFly Tether\.Solutions Ground Station
        * @param[in] checkMaintenance Bool to activate additional maintenance check, default = TRUE
        * @param[out] TetherComDefinitions::systemError Enum representative system error status
        */
        bool checkSystemStatus(systemStatus& sysStatus, systemDetails& sysDetails, TetherComDefinitions::systemError& errorStatus, bool checkMaintenance = true);

        /**
        * Request current system configuration parameters from ValoFly Tether\.Solutions Ground Station.
        *
        * @param[out] config Struct of systemConfig which represents current system configuration parameters
        *
        * @throw IOException Throws IOException on any errors on serial communication
        * @throw DataException Throws DataException on any errors during data reception
        */
        void getSystemConfig(systemConfig& config);

        /**
        * Set given system configuration on ValoFly Tether\.Solutions Ground Station.
        * Out of range values are clamped to minimum / maximum of value range.
        *
        * @param[in] conf Struct of new system configuration parameters
        *
        * @throw IOException Throws IOException on any errors on serial communication
        * @throw DataException Throws DataException on any errors during data reception
        */
        void setSystemConfig(const systemConfig& conf);

        /**
        * Set given activation status on ValoFly Tether\.Solutions Ground Station.
        *
        * @param[in] activation New activation configuration parameter (ON &rArr; true / OFF &rArr; false)
        *
        * @throw IOException Throws IOException on any errors on serial communication
        * @throw DataException Throws DataException on any errors during data reception
        */
        void setSystemActivation(bool activation);

        /**
        * Set given cable retraction torque in percent on ValoFly Tether\.Solutions Ground Station.
        * Out of range values are clamped to minimum / maximum of value range.
        *
        * @param[in] retractionTorque New cable retraction torque configuration parameter, limited to 0 <= retractionTorque <= 100, precision 1.0
        *
        * @throw IOException Throws IOException on any errors on serial communication
        * @throw DataException Throws DataException on any errors during data reception
        */
        void setCableRetractionTorque(float retractionTorque);

        /**
        * Set given length in meter on which cable retraction will be activated on ValoFly Tether\.Solutions Ground Station.
        * Out of range values are clamped to minimum of value range.
        *
        * @param[in] startLength New unwound cable length configuration parameter on which cable retraction will be activated [in m]
        *
        * @throw IOException Throws IOException on any errors on serial communication
        * @throw DataException Throws DataException on any errors during data reception
        */
        void setCableRetractionStartLength(float startLength);

        /**
        * get system status configuration of ValoFly Tether\.Solutions Ground Station.
        *
        * @param[out] conf Struct of new system status configuration parameters
        *
        * @throw IOException Throws IOException on any errors on serial communication
        * @throw DataException Throws DataException on any errors during data reception
        */
        void getSystemStatusConfig(systemStatusConfig& conf);

        /**
        * Set given system status configuration on ValoFly Tether\.Solutions Ground Station.
        *
        * @param[in] conf Struct of new system status configuration parameters
        *
        * @throw IOException Throws IOException on any errors on serial communication
        * @throw DataException Throws DataException on any errors during data reception
        */
        void setSystemStatusConfig(systemStatusConfig& conf);

        /**
        * Get current system status and system details and print them to standard output
        *
        * @throw IOException Throws IOException on any errors on serial communication
        * @throw DataException Throws DataException on any errors during data reception
        *
        * @sa getSystemStatus
        * @sa getSystemDetails
        */
        void print();

        /**
        * Print given systemStatus struct to given output stream
        *
        * @param[in] sysStatus System status struct
        * @param[in] stream Output stream, default is standard output
        */
        void print(systemStatus& sysStatus, std::ostream& stream = std::cout);

        /**
        * Print given systemDetails struct to given output stream
        *
        * @param[in] sysDetails System details struct
        * @param[in] stream Output stream, default is standard output
        */
        void print(systemDetails& sysDetails, std::ostream& stream = std::cout);

        /**
        * Get library build number as string
        *
        * @return Library build number string
        */
        std::string getLibBuild();

        /**
        * Get library build number as string
        *
        * @param[out] build Library build number string
        */
        void getLibBuild(std::string& build);

        /**
        * Get implemented communication protocol version as string
        *
        * @return communication protocol version string
        */
        std::string getLibProtocolVersion();

        /**
        * Get implemented communication protocol version as string
        *
        * @param[out] version communication protocol version string
        */
        void getLibProtocolVersion(std::string& version);
        
        /**
        * Print given version details of TetherComLib and used Protocol to standard output
        */
        void printVersionDetails();
        
        /**
        * Print given version details of TetherComLib and used Protocol
        *
        * @param[in] stream Output stream, default is standard output
        */
        void printVersionDetails(std::ostream& stream);
        
    };
};

#endif // _VALOFLY_TETHERCOM_LIB_HEADER_