#pragma once

#pragma comment(lib, "Ws2_32.lib")
#include "BaseCommunication.h"
#include "UsbSettings.h"

#include <thread>
#include "UsbInformation.h"
#include "ClockUtilities.h"

#ifdef STARFLEETTOOLBOX_EXPORTS
#define USBCOMM __declspec(dllexport)
#else
#define USBCOMM __declspec(dllimport)
#endif

/// <summary>
/// A structure definition to handle USB callback event
/// </summary>
typedef struct UsbDevice {
    struct libusb_transfer* transfer;
    struct libusb_device_handle* device_handle;
    unsigned char input_endpoint; 
    struct libusb_context* usbContext;

    BaseDataExtractor* extractor;           // a pointer to the communication extractor
    HANDLE dataAvailableSemaphore;          // semaphore to warn that Command ACK is available
    HANDLE rawDataAvailableSemaphore;		// semaphore to warn that RAW Data are available
    HANDLE semReadyToConsume;               // synchronisation for data consumed
    string commandResult;                   // the command result
    bool treat = false;                     // indicates if data treater is in treatment
    vector<string> localVector;             // temporary local vector
    RawData extractedData;                  // the extracted raw data    
    bool interruptTransfer = true;          // indicates a transfert interruption
    CommunicationStatus commStatus;         // the connection status
    atomic<chrono::time_point<std::chrono::steady_clock>> lastUsbDataTime; // the last raw data  sent
    int* callbackConnectionStatus;
};

/// <summary>
/// This class handles the USB communication
/// </summary>
class USBCOMM UsbCommunication :
    public BaseCommunication
{
public:
    const int COMMAND_ACK_TIMEOUT = 20000;
    const int MAX_SIZE_TO_READ = 40000;

    /// <summary>
    /// Base constructor
    /// </summary>
    UsbCommunication();
    UsbCommunication(MONITORING_MODE monitoring);
    /// <summary>
    /// Copy constructor
    /// </summary>
    UsbCommunication(UsbSettings* settings);
    UsbCommunication(UsbSettings* settings, MONITORING_MODE monitoring);
    /// <summary>
    /// Destructor
    /// </summary>
    ~UsbCommunication();

    /// <summary>
    /// Connects to the USB Device
    /// </summary>
    CommunicationStatus Connect();
    /// <summary>
  /// Connects to the USB Device
  /// </summary>
    CommunicationStatus Connect(bool reconnecting);

    /// <summary>
    /// Disconnect from the USB device
    /// </summary>
    void Disconnect();
    

    /// <summary>
    /// Send a string command on the appropriate communication channel media
    /// </summary>
    /// <param name="commandToSend">The command to send as a string</param>
    /// <returns>A string representing the command replied on the communication channel media</returns>
    string SendCommand(string commandToSend);


    /// <summary>
    /// Wait the callback to produce a RAW DATA. This method will wait on the appropriate semaphore to produce a data
    /// </summary>
    /// <param name="waitTimeout">The amount of miliseconds the process has to wait befor being released</param>
    /// <param name="semStatus">The status of the semaphore (updated by reference)</param>
    /// <returns>A pointer to the RAW Data</returns>
    RawData WaitForRawData(int waitTimeout, unsigned long* semStatus);

    /// <summary>
    /// A static method that retrieve the devices wired to the computer
    /// throug USB.
    /// </summary>
    /// <returns>A detailed list of conected devices</returns>
    static vector<UsbInformation> GetDeviceList();
protected:
    /// <summary>
    /// Returns the comminication status (NOT IMPLEMENTED FOR USB) -> Always return 0
    /// </summary>
    /// <returns>Success = 0, -1 for failure</returns>
    CommunicationStatus GetConnectionStatus();    
private:
    atomic<chrono::time_point<std::chrono::steady_clock>> _lastUsbDataTime;

    void Disconnect(bool reconnecting);
    CommunicationStatus Reconnect();
    std::atomic<bool> _stopHandle;
    std::atomic<bool> _connected;

    std::atomic<bool> _alreadydisconnected;
    std::atomic<bool> _disconnectCalledByPeer;


    std::atomic<int> _mainConnectionStatus; // useful for monitoring
    int _callbackConnectionStatus = 0;

    UsbDevice usbDevice;
    thread _usbReceiveData;    

    HANDLE _mutexSendAvailable;		    // prevent the socket to be used from another process to send data

    /// <summary>
    /// Receiver business
    /// </summary>
    /// <param name="param">A pointer to the UsbDevice object</param>
    void UsbEventDataReceiverBusiness(void* param);

    /// <summary>
    /// the USB callback
    /// </summary>
    /// <param name="xfr">A pointer to the library transfert</param>
    /// <returns></returns>
    static void __stdcall callbackUSBReadTransferComplete(struct libusb_transfer* xfr);
};

