#include "mainwin.h"
#include "gutil/qjson.h"
#include "pcaprethread.h"
#include "pcap.h"
#include "fast.h"
#include "expertwin.h"
#include "brightwin.h"
#include "testwin.h"
#include "videowin.h"
#include <QLabel>
#include <QPushButton>
#include <QToolButton>
#include <QComboBox>
#include <QMouseEvent>
#include <QPainter>
#include <QPainterPath>
#include <QTimer>
#include <QMessageBox>
#include "globalfunc.h"
#include "basewin.h"
#include <QSettings>
#include <QDir>
#include <QApplication>
#include <QtEndian>
#include <QStandardPaths>
#include <QNetworkDatagram>
#include <QNetworkInterface>

class ImgBtn : public QToolButton {
public:
    ImgBtn() {
        setStyleSheet("QToolButton{border: none; padding-top: 4px;}");
    }
    bool isEnter{false};
protected:
    void resizeEvent(QResizeEvent *event) override {
        QToolButton::resizeEvent(event);
        if(isEnter) setIconSize(QSize(width(), width()));
        else setIconSize(QSize(width()-8, width()-8));
    }
    void enterEvent(QEvent *event) override {
        QToolButton::enterEvent(event);
        setStyleSheet("QToolButton{border: none;}");
        setIconSize(QSize(width(), width()));
    }
    void leaveEvent(QEvent *event) override {
        QToolButton::leaveEvent(event);
        setIconSize(QSize(width()-8, width()-8));
        setStyleSheet("QToolButton{border: none; padding-top: 4px;}");
    }
};

inline QLabel *newImgLabel(QPixmap pixmap){
    QLabel *lb = new QLabel();
    lb->setPixmap(pixmap);
    return lb;
}
inline QLabel *newSubLabel(QString txt){
    QLabel *lb = new QLabel(txt);
    lb->setAlignment(Qt::AlignCenter);
    return lb;
}
ImgBtn *addImg(QHBoxLayout *parent, const QIcon &icon, const QString &text) {
    auto imgBtn = new ImgBtn();
    auto ft = imgBtn->font();
    ft.setPixelSize(14);
    imgBtn->setFont(ft);
    imgBtn->setIcon(icon);
    imgBtn->setText(text);
    imgBtn->setFixedSize(76, 100);
    imgBtn->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
    parent->addWidget(imgBtn);
    return imgBtn;
}

MainWin::MainWin() {
    setWindowTitle("LedSet Express");
    resize(900, 600);

    auto ft = font();
    ft.setPixelSize(14);
    setFont(ft);

    auto vBox = new QVBoxLayout(center);
    vBox->setContentsMargins(0,0,0,0);

    vBox->addLayout(addBtns(new QHBoxLayout()));

    QHBoxLayout *imgsBar = new QHBoxLayout();
    auto btnImg = addImg(imgsBar, QPixmap(":/imgs/fast.png").scaledToWidth(128, Qt::SmoothTransformation), "快速调屏");
    connect(btnImg, &ImgBtn::clicked, this, [=] {
        (new Fast(this))->show();
    });
    btnImg = addImg(imgsBar, QPixmap(":/imgs/expert.png").scaledToWidth(128, Qt::SmoothTransformation), tr("专家调屏"));
    connect(btnImg, &ImgBtn::clicked, this, [=] {
        auto item = table->selectedItem();
        if(item) (new ExpertWin(this, (enum_rcvCardType) item->data("type").toInt()))->show();
        else if(QMessageBox::question(this, "Info", tr("未选择卡, 是否继续?"))==QMessageBox::Yes) (new ExpertWin(this, NoCard))->show();
    });
    btnImg = addImg(imgsBar, QPixmap(":/imgs/bright.png").scaledToWidth(128, Qt::SmoothTransformation), tr("亮度控制"));
    connect(btnImg, &ImgBtn::clicked, this, [=] {
        (new BrightWin(this))->show();
    });
    btnImg = addImg(imgsBar, QPixmap(":/imgs/correct.png").scaledToWidth(128, Qt::SmoothTransformation), tr("相机矫正"));
    connect(btnImg, &ImgBtn::clicked, this, [=] {

    });
    btnImg = addImg(imgsBar, QPixmap(":/imgs/monitor.png").scaledToWidth(128, Qt::SmoothTransformation), tr("屏体监控"));
    connect(btnImg, &ImgBtn::clicked, this, [=] {

    });
    btnImg = addImg(imgsBar, QPixmap(":/imgs/multi.png").scaledToWidth(128, Qt::SmoothTransformation), tr("多功能卡"));
    connect(btnImg, &ImgBtn::clicked, this, [=] {
        //win->move(win->x()-1, win->y());
    });
    btnImg = addImg(imgsBar, QPixmap(":/imgs/test.png").scaledToWidth(128, Qt::SmoothTransformation), tr("协议调试"));
    connect(btnImg, &ImgBtn::clicked, this, [=] {
        if(reThd->status) return;
        (new TestWin(this))->show();
    });
    btnImg = addImg(imgsBar, QPixmap(":/imgs/idea.png").scaledToWidth(128, Qt::SmoothTransformation), tr("模拟同步"));
    connect(btnImg, &ImgBtn::clicked, this, [=] {
        auto ins = VideoWin::newIns(net_name, this);
        if(ins) ins->show();
    });
    vBox->addLayout(imgsBar);
    vBox->addSpacing(9);
    vBox->addWidget(new QLabel("  硬件信息"));

    table = new TreeWidget{
        {"_num_", "", 20},
        {"type", "控制系统类型"},
        {"name", "名称"},
        {"link", "连接方式"},
        {"vcsNum", "接收卡数量"},
        {"netPorts", "网口统计P1~Pn"},
        {"info", "其他信息", QHeaderView::Stretch},
    };
    table->setDefs();
    table->setSortingEnabled(true);
    table->sortItems("name");
    table->setStyleSheet("TreeWidget::item{height: 26px;}");
    vBox->addWidget(table);

    auto hBox = new HBox(vBox);
    hBox->addLabel("V" __VER__" - " __DATE__);
    hBox->addStretch();

    auto btnNetSelect = new QPushButton("通讯选择");
    connect(btnNetSelect, &QPushButton::clicked, this, [this] {
        auto name = getNetDev(this, net_name, false);
        if(name.isEmpty()) return;
        //PCAP_OPENFLAG_DATATX_UDP:2,它定义了数据传输(假如是远程抓包)是否用UDP协议来处理。
        //PCAP_OPENFLAG_NOCAPTURE_RPCAP:4,它定义了远程探测器是否捕获它自己产生的数据包。
        char errbuf[PCAP_ERRBUF_SIZE]{0};
        auto pcapR = pcap_open_live(name.data(), 65536, PCAP_OPENFLAG_PROMISCUOUS|4|8|16, 50, errbuf);
        if(pcapR == 0) {
            QMessageBox::critical(this, "Error", QString(tr("打开网卡失败"))+errbuf);
            return;
        }
        auto pcapS = pcap_open_live(name.data(), 65536, 0, 50, errbuf);
        if(pcapS == 0) {
            QMessageBox::critical(this, "Error", QString(tr("打开网卡失败"))+errbuf);
            return;
        }
        if(reThd) reThd->status = 2;
        if(pcapSend) pcap_close(pcapSend);
        pcapSend = pcapS;
        reThd = new PcapReThread(pcapR);
        reThd->start();
        QSettings().setValue("net_name", net_name = name);
    });
    hBox->addWidget(btnNetSelect);

    auto btnRefresh = new QPushButton(tr("刷新"));
    connect(btnRefresh, &QPushButton::clicked, this, &MainWin::getCard);
    hBox->addWidget(btnRefresh);

    show();

    QSettings config;
    gFileHome = config.value("FileHome").toString();
    if(gFileHome.isEmpty() || ! QFileInfo(gFileHome).isDir()) gFileHome = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
    auto name = config.value("net_name").toByteArray();
    name = getNetDev(this, name, true);
    if(! name.isEmpty()) {
        char errbuf[PCAP_ERRBUF_SIZE]{0};
        auto pcapRe = pcap_open_live(name.data(), 65536, PCAP_OPENFLAG_PROMISCUOUS|4|8|16, 50, errbuf);
        if(pcapRe == 0) QMessageBox::critical(this, "Error", QString(tr("打开网卡失败"))+errbuf);
        else {
            pcapSend = pcap_open_live(name.data(), 65536, 0, 50, errbuf);
            if(pcapSend == 0) QMessageBox::critical(this, "Error", QString(tr("打开网卡失败"))+errbuf);
            else {
                config.setValue("net_name", net_name = name);
                reThd = new PcapReThread(pcapRe);
                reThd->start();
            }
        }
    }
    connect(&mUdpSocket, &QUdpSocket::readyRead, this, [this] {
        while(mUdpSocket.hasPendingDatagrams()) {
            auto gram = mUdpSocket.receiveDatagram();
            auto data = gram.data();
            if(data.isEmpty()) continue;
            auto senderAddress = gram.senderAddress();
            bool ok = true;
            if(senderAddress.protocol()==QUdpSocket::IPv6Protocol) senderAddress.setAddress(senderAddress.toIPv4Address(&ok));
            auto addr = ok ? senderAddress.toString() : "";
            if(data.startsWith("{\"")) {
                QString error;
                auto json = JFrom(gram.data(), &error);
                if(! error.isEmpty()) {
                    qDebug()<<"Json Error: "+error;
                    continue;
                }
                auto cardId = json["cardId"].toStr();
                TreeWidgetItem *item;
                for(int rr=0; rr<table->topLevelItemCount(); rr++) if((item = table->item(rr))->text("name")==cardId) {
                    item->setText("link", addr);
                    goto end;
                }
                item = new TreeWidgetItem(table);
                item->setText("type", tr("异步卡"), enum_xixun_async);
                item->setText("name", cardId);
                item->setText("link", addr);
            } else {
                auto bytes = gram.data();
                auto packet = (UDPPacket *)bytes.data();
                TreeWidgetItem *item;
                for(int rr=0; rr<table->topLevelItemCount(); rr++) if((item = table->item(rr))->text("name")==packet->serialCode) {
                    item->setText("link", addr);
                    goto end;
                }
                item = new TreeWidgetItem(table);
                item->setText("type", tr("异步卡"), enum_xixun_async);
                item->setText("name", packet->serialCode);
                item->setText("link", addr);
            }
            end:;
        }
    });
    reThd->addMultiCallback(0x105B14, [=](int, const QByteArray data) {
        unsigned short ver = *(quint16_be *)(data.data()+headMap_zrf.protcolFlag);
        unsigned char pkgType = *(quint8 *)(data.data()+headMap_zrf.pkgType);
        if(ver!=0x105B || pkgType != 0x14) return;
        auto strDeviceName = QString(QLatin1String(data.data()+headMap_zrf.paramStart+st_zrf_rb_param.deviceName, 9));
        TreeWidgetItem *item;
        for(int i=0; i<table->topLevelItemCount(); i++) if((item = table->item(i))->text("name")==strDeviceName) goto end;
        item = new TreeWidgetItem(table);
        end:
        int virtualVCM = *(quint8*)(data.data()+headMap_zrf.netPort) + 1;
        int rcvIdex = *(quint8*)(data.data()+headMap_zrf.rcvIndex) + 1;
        if(rcvIdex > maxNetPort_zrf) maxNetPort_zrf = rcvIdex;
        item->setText("type", tr("PC虚拟卡V1.0"), enum_zrf);
        item->setText("name", strDeviceName);
        item->setText("link", "千兆网直连");
        item->setText("vcsNum", QString::number(maxNetPort_zrf));
        item->setText("netPorts", "P: "+QString::number(virtualVCM));
        item->setText("info", "版本: "+QLatin1String(data.data()+headMap_zrf.paramStart+st_zrf_rb_param.deviceVer, 8)+" [备注:可直接配屏,无需发送卡]");
        if(table->selectedItem()==0) item->setSelected(true);
    });
    getCard();
}
MainWin::~MainWin() {
    mUdpSocket.close();
    QSettings config;
    config.setValue("FileHome", gFileHome);
}

void MainWin::getCard() {
    table->clear();
    auto msg = QByteArray::fromHex("5555 01 0D 0008 FFFFFFFF 0000ABCD A0000000 0000 38CB847E  00000000 0000ABCD CD040446");
    auto res = sendMsg(msg, 0x1E0, 10000, [=](int, const QByteArray data) {
        if(*(quint32_be*)(data.data()+headMap.ptr) != 0xA0000000) return;
        auto item = new TreeWidgetItem(table);
        int virtualVCM = *(quint16_be*)(data.data()+headMap.srcAddr);
        //modified by alahover -s 20230822
        auto ver = *(byte*)(data.data()+headMap.ver);
        if(ver==0x01) item->setText("type", tr("PC虚拟卡V0.0"), enum_xixun_sync);
        else if(ver==0x58) item->setText("type", tr("PC虚拟卡V1.0"), enum_zrf);
        item->setText("name", QString::number(virtualVCM)+" (网口)");
        item->setText("link", "千兆网直连");
        item->setText("vcsNum", QString::number(*(quint32_be*)(data.data()+headMap.body)));
        item->setText("netPorts", "P: "+QString::number(virtualVCM));
        item->setText("info", "备注: 可直接配屏,无需发送卡");
        if(table->selectedItem()==0) item->setSelected(true);
    });
    if(res) {
        QString err = pcap_geterr(pcapSend);
        if(! err.endsWith("(2150891551)")) QMessageBox::critical(this, "Error", QString(tr("发送失败: "))+QString::fromLocal8Bit(pcap_geterr(pcapSend)));
    }
    //modified by alahover -s 20230822
    //查询105B协议类型ZRF版本fpga的接收卡, 0x105B1400 表示是zrf协议类型并且是回读数据包
    msg = QByteArray::fromHex("5555 001122334455 001122334455 105A 13 00 00 00 00 e0 ff ff 00 f2 f2 f2 f2 00 00 00 00 00 00 00 00 00 00 00 00");
    res = pcap_sendpacket(pcapSend, (const u_char *)msg.data(), msg.size());
    if(res) {
        QString err = pcap_geterr(pcapSend);
        if(! err.endsWith("(2150891551)")) QMessageBox::critical(this, "Error", QString(tr("发送失败: "))+QString::fromLocal8Bit(pcap_geterr(pcapSend)));
    }

    auto data = JToBytes(JObj{{"action", "getInfo"}});
    uchar ccc[]{0x7E, 0x7E, 0x7E, 0x90, 0x42, 0x72, 0x6F, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x21,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1C, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x9F};
    if(mUdpSocket.writeDatagram(data, QHostAddress("255.255.255.255"), 22222) != data.length()) qDebug() << "getInfo write to 255.255.255.255 failed";
    if(mUdpSocket.writeDatagram((char *)ccc, sizeof(ccc), QHostAddress("255.255.255.255"), 31296) != sizeof(ccc)) qDebug() << "getInfo write to 255.255.255.255 failed";
    auto networkinterfaces = QNetworkInterface::allInterfaces();
    foreach(auto face, networkinterfaces) {
        auto flags = face.flags();
        bool can = (flags & QNetworkInterface::IsRunning) && (flags & QNetworkInterface::CanBroadcast) && ! (flags & QNetworkInterface::IsLoopBack);
        if(! can) continue;
        auto addrEntries = face.addressEntries();
        foreach(QNetworkAddressEntry addrEntry, addrEntries) {
            auto ip = addrEntry.ip();
            if(ip.protocol()!=QAbstractSocket::IPv4Protocol) continue;
            auto broadcast = addrEntry.broadcast();
            if(mUdpSocket.writeDatagram(data, broadcast, 22222) != data.length()) qDebug() << "getInfo write failed." << ip.toString() << "->" << broadcast.toString();
        }
    }
}