#ifndef TACTILEIPC3D_SERIAL_BACKEND_H
#define TACTILEIPC3D_SERIAL_BACKEND_H

#include <QObject>
#include <QStringList>
#include <QString>
#include <functional>
#include <memory>
#include "serial_manager.h"
#include "serial_threads.h"
#include "../data_frame.h"

class SerialBackend : public QObject {
    Q_OBJECT
    Q_PROPERTY(QString portName READ portName WRITE setPortName NOTIFY portNameChanged)
    Q_PROPERTY(int baudRate READ baudRate WRITE setBaudRate NOTIFY baudRateChanged)
    Q_PROPERTY(int pollIntervalMs READ pollIntervalMs WRITE setPollIntervalMs NOTIFY pollIntervalMsChanged)
    Q_PROPERTY(int deviceAddress READ deviceAddress WRITE setDeviceAddress NOTIFY deviceAddressChanged)
    Q_PROPERTY(QString mode READ mode WRITE setMode NOTIFY modeChanged)
    Q_PROPERTY(int requestFunction READ requestFunction WRITE setRequestFunction NOTIFY requestFunctionChanged)
    Q_PROPERTY(int requestStartAddress READ requestStartAddress WRITE setRequestStartAddress NOTIFY requestStartAddressChanged)
    Q_PROPERTY(int requestLength READ requestLength WRITE setRequestLength NOTIFY requestLengthChanged)
    Q_PROPERTY(QString protocol READ protocol WRITE setProtocol NOTIFY protocolChanged)
    Q_PROPERTY(bool connected READ connected NOTIFY connectedChanged)
    Q_PROPERTY(QStringList availablePorts READ availablePorts NOTIFY availablePortsChanged)
    Q_PROPERTY(QString sensorModel READ sensorModel NOTIFY sensorModelChanged)
    Q_PROPERTY(QString sensorGrid READ sensorGrid NOTIFY sensorGridChanged)

public:
    using FrameCallback = std::function<void(const DataFrame&)>;

    explicit SerialBackend(QObject* parent = nullptr);
    ~SerialBackend() override;

    QString portName() const { return m_config.portName; }
    int baudRate() const { return m_config.baudRate; }
    int pollIntervalMs() const { return m_config.pollIntervalMs; }
    int deviceAddress() const { return static_cast<int>(m_config.deviceAddress); }
    QString mode() const;

    int requestFunction() const { return m_request.functionCode; }
    int requestStartAddress() const { return static_cast<int>(m_request.startAddress); }
    int requestLength() const { return m_request.dataLength; }

    QString protocol() const { return m_manager.activeProtocol(); }
    bool connected() const { return m_connected; }
    QStringList availablePorts() const { return m_availablePorts; }
    QString sensorModel() const { return m_spec.model; }
    QString sensorGrid() const;

    void setPortName(const QString& name);
    void setBaudRate(int rate);
    void setPollIntervalMs(int intervalMs);
    void setDeviceAddress(int address);
    void setMode(const QString& mode);

    void setRequestFunction(int func);
    void setRequestStartAddress(int addr);
    void setRequestLength(int len);
    void setProtocol(const QString& name);
    Q_INVOKABLE void applySensorSpec(const QString& model, int rows, int cols);

    void setFrameCallback(FrameCallback cb) { m_frameCallback = std::move(cb); }
    void setTransport(std::unique_ptr<ISerialTransport> transport);

    Q_INVOKABLE void refreshPorts();
    Q_INVOKABLE bool open();
    Q_INVOKABLE void close();
    Q_INVOKABLE void requestOnce();
    Q_INVOKABLE void feedBytes(const QByteArray& data);

    int sensorWidth() const { return m_spec.cols; }
    int sensorHeight() const { return m_spec.rows; }

public slots:
    void setSensorWidth(int w);
    void setSensorHeight(int h);

signals:
    void portNameChanged();
    void baudRateChanged();
    void pollIntervalMsChanged();
    void deviceAddressChanged();
    void modeChanged();
    void requestFunctionChanged();
    void requestStartAddressChanged();
    void requestLengthChanged();
    void protocolChanged();
    void connectedChanged();
    void availablePortsChanged();
    void sensorModelChanged();
    void sensorGridChanged();

    void requestBuilt(const QByteArray& data);

private:
    void drainFrames_();
    void startPipeline_();
    void stopPipeline_();
    void updateProtocolBindings_();
    void syncSendConfig_();
    void syncSendRequest_();

    SerialConfig m_config;
    SensorRequest m_request;
    SensorSpec m_spec;
    SerialManager m_manager;
    PacketQueue m_packetQueue;
    FrameQueue m_frameQueue;
    SerialReadThread m_readThread;
    SerialDecodeThread m_decodeThread;
    QThread m_sendThread;
    SerialSendWorker* m_sendWorker = nullptr;

    FrameCallback m_frameCallback;
    bool m_connected = false;

    QStringList m_availablePorts;
};

#endif // TACTILEIPC3D_SERIAL_BACKEND_H
