#include "externalplayeripc.h"
#include <QLocalSocket>
#include <QTcpSocket>
#include <QTimer>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QDebug>


static constexpr long long TICKS_PER_SECOND = 10000000LL;
static constexpr int POLL_INTERVAL_MS = 5000;      
static constexpr int CONNECT_RETRY_MS = 1000;       
static constexpr int MAX_CONNECT_ATTEMPTS = 15;      




ExternalPlayerIPC* ExternalPlayerIPC::create(PlayerType type, const QString& ipcEndpoint, QObject* parent)
{
    switch (type) {
    case PlayerType::MPV:
    case PlayerType::IINA:
        return new MpvIPC(ipcEndpoint, parent);

    case PlayerType::VLC:
        return new EstimationIPC(parent);

    default:
        return new EstimationIPC(parent);
    }
}




MpvIPC::MpvIPC(const QString& pipePath, QObject* parent)
    : ExternalPlayerIPC(parent)
    , m_pipePath(pipePath)
{
}

MpvIPC::~MpvIPC()
{
    stop();
}

void MpvIPC::start(long long startPositionTicks)
{
    m_lastPositionTicks = startPositionTicks;
    m_connectAttempts = 0;
    m_connected = false;

    m_socket = new QLocalSocket(this);
    connect(m_socket, &QLocalSocket::readyRead, this, &MpvIPC::onReadyRead);
    connect(m_socket, &QLocalSocket::connected, this, [this]() {
        m_connected = true;
        m_connectAttempts = 0;
        qDebug() << "[MpvIPC] Connected to pipe:" << m_pipePath;

        
        if (!m_pollTimer) {
            m_pollTimer = new QTimer(this);
            connect(m_pollTimer, &QTimer::timeout, this, &MpvIPC::sendQuery);
        }
        m_pollTimer->start(POLL_INTERVAL_MS);
        sendQuery(); 
    });
    connect(m_socket, &QLocalSocket::disconnected, this, [this]() {
        m_connected = false;
        if (m_pollTimer) m_pollTimer->stop();
        qDebug() << "[MpvIPC] Disconnected from pipe";
    });

    
    m_connectTimer = new QTimer(this);
    m_connectTimer->setSingleShot(true);
    connect(m_connectTimer, &QTimer::timeout, this, &MpvIPC::tryConnect);
    m_connectTimer->start(CONNECT_RETRY_MS);
}

void MpvIPC::tryConnect()
{
    if (m_connected || m_connectAttempts >= MAX_CONNECT_ATTEMPTS) {
        if (!m_connected) {
            qDebug() << "[MpvIPC] Max connect attempts reached, giving up";
        }
        return;
    }

    m_connectAttempts++;
    qDebug() << "[MpvIPC] Attempting to connect (" << m_connectAttempts << "/" << MAX_CONNECT_ATTEMPTS << ")";
    m_socket->connectToServer(m_pipePath);

    if (!m_socket->waitForConnected(500)) {
        
        if (m_connectTimer)
            m_connectTimer->start(CONNECT_RETRY_MS);
    }
}

void MpvIPC::stop()
{
    if (m_pollTimer) {
        m_pollTimer->stop();
        m_pollTimer->deleteLater();
        m_pollTimer = nullptr;
    }
    if (m_connectTimer) {
        m_connectTimer->stop();
        m_connectTimer->deleteLater();
        m_connectTimer = nullptr;
    }
    if (m_socket) {
        if (m_socket->state() == QLocalSocket::ConnectedState) {
            m_socket->disconnectFromServer();
        }
        m_socket->deleteLater();
        m_socket = nullptr;
    }
    m_connected = false;
}

long long MpvIPC::currentPositionTicks() const
{
    return m_lastPositionTicks;
}

void MpvIPC::sendQuery()
{
    if (!m_socket || !m_connected) return;

    
    QJsonObject cmd;
    cmd["command"] = QJsonArray{"get_property", "time-pos"};
    QByteArray data = QJsonDocument(cmd).toJson(QJsonDocument::Compact) + "\n";
    m_socket->write(data);
    m_socket->flush();
}

void MpvIPC::onReadyRead()
{
    while (m_socket && m_socket->canReadLine()) {
        QByteArray line = m_socket->readLine().trimmed();
        if (line.isEmpty()) continue;

        QJsonParseError err;
        QJsonDocument doc = QJsonDocument::fromJson(line, &err);
        if (err.error != QJsonParseError::NoError) continue;

        QJsonObject obj = doc.object();

        
        if (obj.contains("data") && obj["error"].toString() == "success") {
            double seconds = obj["data"].toDouble(-1.0);
            if (seconds >= 0.0) {
                m_lastPositionTicks = static_cast<long long>(seconds * TICKS_PER_SECOND);
                Q_EMIT positionUpdated(m_lastPositionTicks);
            }
        }
    }
}




VlcRcIPC::VlcRcIPC(int port, QObject* parent)
    : ExternalPlayerIPC(parent)
    , m_port(port)
{
}

VlcRcIPC::~VlcRcIPC()
{
    stop();
}

void VlcRcIPC::start(long long startPositionTicks)
{
    m_lastPositionTicks = startPositionTicks;
    m_connectAttempts = 0;
    m_connected = false;

    m_socket = new QTcpSocket(this);
    connect(m_socket, &QTcpSocket::connected, this, &VlcRcIPC::onConnected);
    connect(m_socket, &QTcpSocket::readyRead, this, &VlcRcIPC::onReadyRead);
    connect(m_socket, &QTcpSocket::disconnected, this, [this]() {
        m_connected = false;
        if (m_pollTimer) m_pollTimer->stop();
        qDebug() << "[VlcRcIPC] Disconnected from RC interface";
    });

    
    m_connectTimer = new QTimer(this);
    m_connectTimer->setSingleShot(true);
    connect(m_connectTimer, &QTimer::timeout, this, &VlcRcIPC::tryConnect);
    m_connectTimer->start(2000); 
}

void VlcRcIPC::tryConnect()
{
    if (m_connected || m_connectAttempts >= MAX_CONNECT_ATTEMPTS) {
        if (!m_connected)
            qDebug() << "[VlcRcIPC] Max connect attempts reached";
        return;
    }

    m_connectAttempts++;
    qDebug() << "[VlcRcIPC] Attempting to connect to port" << m_port
             << "(" << m_connectAttempts << "/" << MAX_CONNECT_ATTEMPTS << ")";
    m_socket->connectToHost("127.0.0.1", m_port);

    if (!m_socket->waitForConnected(1000)) {
        if (m_connectTimer)
            m_connectTimer->start(CONNECT_RETRY_MS);
    }
}

void VlcRcIPC::onConnected()
{
    m_connected = true;
    m_connectAttempts = 0;
    qDebug() << "[VlcRcIPC] Connected to VLC RC on port" << m_port;

    if (!m_pollTimer) {
        m_pollTimer = new QTimer(this);
        connect(m_pollTimer, &QTimer::timeout, this, &VlcRcIPC::sendQuery);
    }
    m_pollTimer->start(POLL_INTERVAL_MS);
    
    QTimer::singleShot(500, this, &VlcRcIPC::sendQuery);
}

void VlcRcIPC::stop()
{
    if (m_pollTimer) {
        m_pollTimer->stop();
        m_pollTimer->deleteLater();
        m_pollTimer = nullptr;
    }
    if (m_connectTimer) {
        m_connectTimer->stop();
        m_connectTimer->deleteLater();
        m_connectTimer = nullptr;
    }
    if (m_socket) {
        if (m_socket->state() == QTcpSocket::ConnectedState) {
            m_socket->write("quit\n");
            m_socket->flush();
            m_socket->disconnectFromHost();
        }
        m_socket->deleteLater();
        m_socket = nullptr;
    }
    m_connected = false;
}

long long VlcRcIPC::currentPositionTicks() const
{
    return m_lastPositionTicks;
}

void VlcRcIPC::sendQuery()
{
    if (!m_socket || !m_connected) return;

    
    m_socket->write("get_time\n");
    m_socket->flush();
}

void VlcRcIPC::onReadyRead()
{
    while (m_socket && m_socket->canReadLine()) {
        QByteArray line = m_socket->readLine().trimmed();
        if (line.isEmpty()) continue;

        
        
        bool ok;
        int seconds = line.toInt(&ok);
        if (ok && seconds >= 0) {
            m_lastPositionTicks = static_cast<long long>(seconds) * TICKS_PER_SECOND;
            Q_EMIT positionUpdated(m_lastPositionTicks);
        }
    }
}


EstimationIPC::EstimationIPC(QObject* parent)
    : ExternalPlayerIPC(parent)
{
}

EstimationIPC::~EstimationIPC()
{
    stop();
}

void EstimationIPC::start(long long startPositionTicks)
{
    m_startTicks = startPositionTicks;
    m_elapsed.start();
    m_running = true;
}

void EstimationIPC::stop()
{
    m_running = false;
}

long long EstimationIPC::currentPositionTicks() const
{
    if (!m_running) return m_startTicks;
    
    long long elapsedMs = m_elapsed.elapsed();
    return m_startTicks + elapsedMs * 10000LL; 
}
