#ifndef DFUDEVICE_H
#define DFUDEVICE_H

#include <QObject>
class LQError;
class ProgressLog;

/* Базовый класс работы с USB устройстовм, реализующем протокол DFU, на базе
 * спецификации USB Device Firmware Upgrade Specification v1.1.
 * Реализует все базовые запросы, описанные в спецификации, кроме DFU_DETTACH.
 * Последовательность запросов уже должна определяться классом, реализующим протокол */
class DfuDevice : public QObject {
    Q_OBJECT
public:
    /* Режим работ устройства */
    enum class Mode {
        DFU, /* в режиме DFU */
        Application, /* режим приложения. необходим DETACH */
        Unknown
    };

    enum class Status {
        OK                  = 0x00, /* No error condition is present. */
        errTARGET           = 0x01, /* File is not targeted for use by this device. */
        errFILE             = 0x02, /* File is for this device but fails some vendor-specific verification test. */
        errWRITE            = 0x03, /* Device is unable to write memory. */
        errERASE            = 0x04, /* Memory erase function failed. */
        errCHECK_ERASED     = 0x05, /* Memory erase check failed. */
        errPROG             = 0x06, /* Program memory function failed. */
        errVERIFY           = 0x07, /* Programmed memory failed verification. */
        errADDRESS          = 0x08, /* Cannot program memory due to received address that is out of range. */
        errNOTDONE          = 0x09, /* Received DFU_DNLOAD with wLength = 0, but device does not think it has all of the data yet. */
        errFIRMWARE         = 0x0A, /* Device’s firmware is corrupt. It cannot return to run-time (non-DFU) operations. */
        errVENDOR           = 0x0B, /* iString indicates a vendor-specific error. */
        errUSBR             = 0x0C, /* Device detected unexpected USB reset signaling. */
        errPOR              = 0x0D, /* Device detected unexpected power on reset. */
        errUNKNOWN          = 0x0E, /* Something went wrong, but the device does not know what it was*/
        errSTALLEDPKT       = 0x0F, /* Device stalled an unexpected request. */
    };

    enum class State {
        appIDLE             = 0, /* Device is running its normal application. */
        appDETACH           = 1, /* Device is running its normal application, has received the DFU_DETACH request, and is waiting for a USB reset. */
        dfuIDLE             = 2, /* Device is operating in the DFU mode and is waiting for requests.  */
        dfuDNLOAD_SYNC      = 3, /* Device has received a block and is waiting for the host to solicit the status via DFU_GETSTATUS. */
        dfuDNBUSY           = 4, /* Device is programming a control-write block into its nonvolatile memories. */
        dfuDNLOAD_IDLE      = 5, /* Device is processing a download operation. Expecting DFU_DNLOAD requests. */
        dfuMANIFEST_SYNC    = 6, /* Device has received the final block of firmware from the host
                                    and is w aiting for receipt of DFU_GETSTATUS to begin the
                                    Manifestation phase; or device has completed the
                                    Manifestation phase and is waiting for receipt of
                                    DFU_GETSTATUS. (Devices that can enter this state after
                                    the Manifestation phase set bmAttributes bit
                                    bitManifestationTolerant to 1.) */
        dfuMANIFEST         = 7, /* Device is in the Manifestation phase. (Not all devices will be
                                    able to respond to DFU_GETSTATUS when in this state.) */
        dfuMANIFEST_WT_RST  = 8, /* Device has programmed its memories and is waiting for a
                                    USB reset or a power on reset. (Devices that must enter
                                    this state clear bitManifestationTolerant to 0.) */
        dfuUPLOAD_IDLE      = 9, /* The device is processing an upload operation. Expecting DFU_UPLOAD requests. */
        dfuERROR            = 10, /* An error has occurred. Awaiting the DFU_CLRSTATUS request. */
    };


    DfuDevice();
    ~DfuDevice();


    class StatusInfo {

    public:
        StatusInfo() {}
        StatusInfo(Status status, quint32 poll_tout, State state) :
            m_status{status}, m_poll_tout{poll_tout}, m_state{state} {}

        Status status() const {return m_status;}
        State state() const {return m_state;}
        quint32 pollTout() const {return m_poll_tout;}
    private:
        Status m_status {Status::OK};
        quint32 m_poll_tout {0};
        State m_state {State::appIDLE};
    };



    void detect(LQError &err);
    void upload(quint16 block_num, quint16 len, QByteArray &data, LQError &err);
    void dnload(quint16 block_num, const QByteArray &data, LQError &err);
    void getStatus(StatusInfo &info, LQError &err);
    void clearStatus(LQError &err);
    void getState(State &state, LQError &err);
    void abort(LQError &err);
    void close();
private:
    void ioreq_tx(quint8 req, quint16 wValue, const QByteArray &data, LQError &err);
    void ioreq_rx(quint8 req, quint16 wValue, quint16 req_len, QByteArray &data, LQError &err);

    quint16 m_vid;
    quint16 m_pid;
    Mode m_mode;
    quint16 m_iface_num {0};
    const unsigned m_ioreq_tout {3000};

    struct libusb_device_handle *m_usb_hnd {nullptr};
};

#endif // DFUDEVICE_H
