#include "rfkill.h"

enum rfkill_type {
    RFKILL_TYPE_ALL = 0,
    RFKILL_TYPE_WLAN,
    RFKILL_TYPE_BLUETOOTH,
    RFKILL_TYPE_UWB,
    RFKILL_TYPE_WIMAX,
    RFKILL_TYPE_WWAN
};

enum rfkill_operation {
    RFKILL_OP_ADD = 0,
    RFKILL_OP_DEL,
    RFKILL_OP_CHANGE,
    RFKILL_OP_CHANGE_ALL,
    RFKILL_OP_OFF
};

struct rfkill_event {
    quint32 idx;
    quint8 type;
    quint8 op;
    quint8 soft;
    quint8 hard;
};

Rfkill::Rfkill(QObject *parent) :
    QObject(parent),
    _mReadFd(-1),
    _mWriteFd(-1),
    _mState(Unknown)
{
//    init();
}

Rfkill::~Rfkill()
{
    if (_mReadFd != -1) {
        close(_mReadFd);
    }

    if (_mWriteFd != -1) {
        close(_mWriteFd);
    }
}

Rfkill::State Rfkill::state() const
{
    return _mState;
}

bool Rfkill::block()
{
    if (_mState == SoftBlocked || _mState == HardBlocked) {
        return true;
    }

    if (_mState != Unblocked) {
        return false;
    }

    return setSoftBlock(1);
}

bool Rfkill::unblock()
{
    if (_mState == Unblocked) {
        return true;
    }

    if (_mState != SoftBlocked) {
        return false;
    }

    return setSoftBlock(0);
}

size_t Rfkill::bluetooth_size(void)
{
    return _mDevices.size();
}

void Rfkill::init()
{
    _mReadFd = open("/dev/rfkill", O_RDONLY | O_CLOEXEC);

    if (_mReadFd == -1) {
        qDebug() << Q_FUNC_INFO << "Cannot open /dev/rfkill for reading!";
        return;
    }

    if (fcntl(_mReadFd, F_SETFL, O_NONBLOCK) < 0) {
        close(_mReadFd);
        _mReadFd = -1;
        return;
    }

    updateRfkillDevices();

    // add： 不存在蓝牙节点情况下，发送初始化信号
    if(_mDevices.size() == 0)
    {
        emit rfkillStatusChanged(RFKILL_OP_OFF);
    }

    QSocketNotifier *notifier = new QSocketNotifier(_mReadFd, QSocketNotifier::Read, this);
    connect(notifier, &QSocketNotifier::activated, this, &Rfkill::devReadyRead);
}

void Rfkill::devReadyRead()
{
    State oldState = _mState;

    updateRfkillDevices();

    if (_mState != oldState) {
        emit stateChanged(_mState);
    }
}

static Rfkill::State getState(rfkill_event event)
{
    if (event.hard) {
        return Rfkill::HardBlocked;
    } else if (event.soft) {
        return Rfkill::SoftBlocked;
    }
    return Rfkill::Unblocked;
}

void Rfkill::updateRfkillDevices()
{
    if (_mReadFd == -1) {
        return;
    }

    rfkill_event event;
    while (read(_mReadFd, &event, sizeof(event)) == sizeof(event)) {
        qDebug() << Q_FUNC_INFO << QString("idx %1 type %2 op %3 soft %4 hard %5").arg(event.idx).arg(event.type).arg(event.op).arg(event.soft).arg(event.hard);
        if (event.type != RFKILL_TYPE_BLUETOOTH) {
            continue;
        }

        switch (event.op) {
        case RFKILL_OP_ADD:
            _mDevices[event.idx] = getState(event);
            emit rfkillStatusChanged(RFKILL_OP_ADD);
            break;
        case RFKILL_OP_CHANGE:
            _mDevices[event.idx] = getState(event);
            break;

        case RFKILL_OP_DEL:
            _mDevices.remove(event.idx);
            emit rfkillStatusChanged(RFKILL_OP_DEL);
            break;

        case RFKILL_OP_CHANGE_ALL:
            for (auto it = _mDevices.begin(); it != _mDevices.end(); ++it) {
                it.value() = getState(event);
            }
            break;

        default:
            break;
        }
    }

    // Update global state
    _mState = Unknown;

    for (State state : qAsConst(_mDevices)) {
        Q_ASSERT(state != Unknown);

        if (_mState == Unknown) {
            _mState = state;
        } else if (state > _mState) {
            _mState = state;
        }
    }

    qDebug() << "Rfkill global state changed:" << _mState;
    qDebug() << "Rfkill currentThreadId :" << QThread::currentThreadId();
}

bool Rfkill::openForWriting()
{
    if (_mWriteFd != -1) {
            return true;
    }

    _mWriteFd = open("/dev/rfkill", O_WRONLY | O_CLOEXEC);

    if (_mWriteFd == -1) {
        qDebug() << "Cannot open /dev/rfkill for writing!";
        return false;
    }

    if (fcntl(_mWriteFd, F_SETFL, O_NONBLOCK) < 0) {
        close(_mWriteFd);
        _mWriteFd = -1;
        return false;
    }

    return true;
}

bool Rfkill::setSoftBlock(quint8 soft)
{
    if (!openForWriting()) {
        return false;
    }

    rfkill_event event;
    memset(&event, 0, sizeof(event));
    event.op = RFKILL_OP_CHANGE_ALL;
    event.type = RFKILL_TYPE_BLUETOOTH;
    event.soft = soft;

    bool ret = write(_mWriteFd, &event, sizeof(event)) == sizeof(event);
    qDebug() << "Setting Rfkill soft block succeeded:" << ret;
    return ret;
}
