/*
 * 86Box    A hypervisor and IBM PC system emulator that specializes in
 *          running old operating systems and software designed for IBM
 *          PC systems and compatibles from 1981 through fairly recent
 *          system designs based on the PCI bus.
 *
 *          This file is part of the 86Box distribution.
 *
 *          Network devices configuration UI module.
 *
 *
 *
 * Authors: Joakim L. Gilje <jgilje@jgilje.net>
 *
 *          Copyright 2021 Joakim L. Gilje
 */
#include "qt_settingsnetwork.hpp"
#include "ui_qt_settingsnetwork.h"

extern "C" {
#include <86box/86box.h>
#include <86box/device.h>
#include <86box/machine.h>
#include <86box/timer.h>
#include <86box/thread.h>
#include <86box/network.h>
}

#include "qt_models_common.hpp"
#include "qt_deviceconfig.hpp"

void
SettingsNetwork::enableElements(Ui::SettingsNetwork *ui)
{
    for (int i = 0; i < NET_CARD_MAX; ++i) {
        auto *nic_cbox      = findChild<QComboBox *>(QString("comboBoxNIC%1").arg(i + 1));
        auto *net_type_cbox = findChild<QComboBox *>(QString("comboBoxNet%1").arg(i + 1));

        auto *intf_label = findChild<QLabel *>(QString("labelIntf%1").arg(i + 1));
        auto *intf_cbox  = findChild<QComboBox *>(QString("comboBoxIntf%1").arg(i + 1));

        auto *conf_btn      = findChild<QPushButton *>(QString("pushButtonConf%1").arg(i + 1));
//        auto *net_type_conf_btn      = findChild<QPushButton *>(QString("pushButtonNetTypeConf%1").arg(i + 1));

        auto *vde_socket_label = findChild<QLabel *>(QString("labelSocketVDENIC%1").arg(i + 1));
        auto *socket_line      = findChild<QLineEdit *>(QString("socketVDENIC%1").arg(i + 1));

        auto *bridge_label = findChild<QLabel *>(QString("labelBridgeTAPNIC%1").arg(i + 1));
        auto *bridge_line  = findChild<QLineEdit *>(QString("bridgeTAPNIC%1").arg(i + 1));

        auto *option_list_label = findChild<QLabel *>(QString("labelOptionList%1").arg(i + 1));
        auto *option_list_line  = findChild<QWidget *>(QString("lineOptionList%1").arg(i + 1));

        // Switch group
        auto *switch_group_label = findChild<QLabel *>(QString("labelSwitch%1").arg(i + 1));
//        auto *switch_group_hlayout = findChild<QHBoxLayout *>(QString("HLayoutSwitch%1").arg(i + 1));
//        auto *switch_group_hspacer = findChild<QWidget *>(QString("horizontalSpacerSwitch%1").arg(i + 1));
        auto *switch_group_value = findChild<QSpinBox *>(QString("spinnerSwitch%1").arg(i + 1));
        switch_group_value->setMinimum(1);
        switch_group_value->setMaximum(10);

        // Promiscuous option
        auto *promisc_label  = findChild<QLabel *>(QString("labelPromisc%1").arg(i + 1));
        auto *promisc_value = findChild<QCheckBox *>(QString("boxPromisc%1").arg(i + 1));

        // Remote switch hostname
        auto *hostname_label = findChild<QLabel *>(QString("labelHostname%1").arg(i + 1));
        auto *hostname_value = findChild<QLineEdit *>(QString("hostnameSwitch%1").arg(i + 1));

        bridge_line->setEnabled(net_type_cbox->currentData().toInt() == NET_TYPE_TAP);
        intf_cbox->setEnabled(net_type_cbox->currentData().toInt() == NET_TYPE_PCAP);
        conf_btn->setEnabled(network_card_has_config(nic_cbox->currentData().toInt()));
//        net_type_conf_btn->setEnabled(network_type_has_config(netType));

        // NEW STUFF
        // Make all options invisible by default

        // Switch group
        switch_group_label->setVisible(false);
        switch_group_value->setVisible(false);
//        switch_group_hspacer->setVisible(false);

        // Promiscuous options
        promisc_label->setVisible(false);
        promisc_value->setVisible(false);

        // Hostname
        hostname_label->setVisible(false);
        hostname_value->setVisible(false);

        // Option list label and line
        option_list_label->setVisible(false);
        option_list_line->setVisible(false);

        // VDE
        vde_socket_label->setVisible(false);
        socket_line->setVisible(false);

        // TAP
        bridge_label->setVisible(false);
        bridge_line->setVisible(false);

        // PCAP
        intf_cbox->setVisible(false);
        intf_label->setVisible(false);

        // Don't enable anything unless there's a nic selected
        if(nic_cbox->currentData().toInt() != 0) {
            // Then only enable as needed based on network type
            switch (net_type_cbox->currentData().toInt()) {
#ifdef HAS_VDE
                case NET_TYPE_VDE:
                    // option_list_label->setText("VDE Options");
                    option_list_label->setVisible(true);
                    option_list_line->setVisible(true);

                    vde_socket_label->setVisible(true);
                    socket_line->setVisible(true);
                    break;
#endif

                case NET_TYPE_PCAP:
                    // option_list_label->setText("PCAP Options");
                    option_list_label->setVisible(true);
                    option_list_line->setVisible(true);

                    intf_cbox->setVisible(true);
                    intf_label->setVisible(true);
                    break;

#if defined(__unix__) || defined(__APPLE__)
                case NET_TYPE_TAP:
                    // option_list_label->setText("TAP Options");
                    option_list_label->setVisible(true);
                    option_list_line->setVisible(true);

                    bridge_label->setVisible(true);
                    bridge_line->setVisible(true);
                    break;
#endif

#ifdef USE_NETSWITCH
                    case NET_TYPE_NMSWITCH:
//                    option_list_label->setText("Local Switch Options");
                    option_list_label->setVisible(true);
                    option_list_line->setVisible(true);

                    // Switch group
                    switch_group_label->setVisible(true);
                    switch_group_value->setVisible(true);
//                    switch_group_hspacer->setVisible(false);

                    // Promiscuous options
                    promisc_label->setVisible(true);
                    promisc_value->setVisible(true);
                    break;

                case NET_TYPE_NRSWITCH:
//                    option_list_label->setText("Remote Switch Options");
                    option_list_label->setVisible(true);
                    option_list_line->setVisible(true);

                    // Switch group
                    switch_group_label->setVisible(true);
                    switch_group_value->setVisible(true);
//                    switch_group_hspacer->setVisible(false);

                    // Hostname
                    hostname_label->setVisible(true);
                    hostname_value->setVisible(true);
                    break;
#endif /* USE_NETSWITCH */

                case NET_TYPE_SLIRP:
                default:
                    break;
            }
        }
    }
}

SettingsNetwork::SettingsNetwork(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::SettingsNetwork)
{
    ui->setupUi(this);

    onCurrentMachineChanged(machine);
    enableElements(ui);
    for (int i = 0; i < NET_CARD_MAX; i++) {
        auto *nic_cbox      = findChild<QComboBox *>(QString("comboBoxNIC%1").arg(i + 1));
        auto *net_type_cbox = findChild<QComboBox *>(QString("comboBoxNet%1").arg(i + 1));
        auto *intf_cbox     = findChild<QComboBox *>(QString("comboBoxIntf%1").arg(i + 1));
        connect(nic_cbox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &SettingsNetwork::on_comboIndexChanged);
        connect(net_type_cbox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &SettingsNetwork::on_comboIndexChanged);
        connect(intf_cbox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &SettingsNetwork::on_comboIndexChanged);
    }
}

SettingsNetwork::~SettingsNetwork()
{
    delete ui;
}

void
SettingsNetwork::save()
{
    for (int i = 0; i < NET_CARD_MAX; ++i) {
        auto *cbox                   = findChild<QComboBox *>(QString("comboBoxNIC%1").arg(i + 1));
#ifdef HAS_VDE
        auto *socket_line            = findChild<QLineEdit *>(QString("socketVDENIC%1").arg(i + 1));
#endif
#if defined(__unix__) || defined(__APPLE__)
        auto *bridge_line            = findChild<QLineEdit *>(QString("bridgeTAPNIC%1").arg(i + 1));
#endif
        net_cards_conf[i].device_num = cbox->currentData().toInt();
        cbox                         = findChild<QComboBox *>(QString("comboBoxNet%1").arg(i + 1));
        net_cards_conf[i].net_type   = cbox->currentData().toInt();
        cbox                         = findChild<QComboBox *>(QString("comboBoxIntf%1").arg(i + 1));
#ifdef USE_NETSWITCH
        auto *hostname_value         = findChild<QLineEdit *>(QString("hostnameSwitch%1").arg(i + 1));
        auto *promisc_value          = findChild<QCheckBox *>(QString("boxPromisc%1").arg(i + 1));
        auto *switch_group_value     = findChild<QSpinBox *>(QString("spinnerSwitch%1").arg(i + 1));
#endif /* USE_NETSWITCH */
        memset(net_cards_conf[i].host_dev_name, '\0', sizeof(net_cards_conf[i].host_dev_name));
        if (net_cards_conf[i].net_type == NET_TYPE_PCAP)
            strncpy(net_cards_conf[i].host_dev_name, network_devs[cbox->currentData().toInt()].device, sizeof(net_cards_conf[i].host_dev_name) - 1);
#ifdef HAS_VDE
        else if (net_cards_conf[i].net_type == NET_TYPE_VDE)
            strncpy(net_cards_conf[i].host_dev_name, socket_line->text().toUtf8().constData(), sizeof(net_cards_conf[i].host_dev_name));
#endif
#if defined(__unix__) || defined(__APPLE__)
        else if (net_cards_conf[i].net_type == NET_TYPE_TAP)
            strncpy(net_cards_conf[i].host_dev_name, bridge_line->text().toUtf8().constData(), sizeof(net_cards_conf[i].host_dev_name));
#endif
#ifdef USE_NETSWITCH
        else if (net_cards_conf[i].net_type == NET_TYPE_NRSWITCH) {
            memset(net_cards_conf[i].nrs_hostname, '\0', sizeof(net_cards_conf[i].nrs_hostname));
            strncpy(net_cards_conf[i].nrs_hostname, hostname_value->text().toUtf8().constData(), sizeof(net_cards_conf[i].nrs_hostname) - 1);
            net_cards_conf[i].switch_group = switch_group_value->value() - 1;
        } else if (net_cards_conf[i].net_type == NET_TYPE_NMSWITCH) {
            net_cards_conf[i].promisc_mode = promisc_value->isChecked();
            net_cards_conf[i].switch_group = switch_group_value->value() - 1;
        }
#endif /* USE_NETSWITCH */
    }
}

void
SettingsNetwork::onCurrentMachineChanged(int machineId)
{
    this->machineId = machineId;

    int                 c                           = 0;
    int                 selectedRow                 = 0;

    // Network Card
    QComboBox          *cbox_[NET_CARD_MAX]         = { 0 };
    QAbstractItemModel *models[NET_CARD_MAX]        = { 0 };
    int                 removeRows_[NET_CARD_MAX]   = { 0 };
    int                 selectedRows[NET_CARD_MAX]  = { 0 };
    int                 m_has_net                   = machine_has_flags(machineId, MACHINE_NIC);

    for (uint8_t i = 0; i < NET_CARD_MAX; ++i) {
        cbox_[i]       = findChild<QComboBox *>(QString("comboBoxNIC%1").arg(i + 1));
        models[i]      = cbox_[i]->model();
        removeRows_[i] = models[i]->rowCount();
    }

    c = 0;
    while (true) {
        const QString name = DeviceConfig::DeviceName(network_card_getdevice(c),
                                                      network_card_get_internal_name(c), 1);

        if (name.isEmpty())
            break;

        if (network_card_available(c)) {
            if (device_is_valid(network_card_getdevice(c), machineId)) {
                for (uint8_t i = 0; i < NET_CARD_MAX; ++i) {
                    if ((c != 1) || ((i == 0) && m_has_net)) {
                        int row = Models::AddEntry(models[i], name, c);

                        if (c == net_cards_conf[i].device_num)
                            selectedRows[i] = row - removeRows_[i];
                    }
                }
            }
        }

       c++;
    }

    for (uint8_t i = 0; i < NET_CARD_MAX; ++i) {
        models[i]->removeRows(0, removeRows_[i]);
        cbox_[i]->setEnabled(models[i]->rowCount() > 1);
        cbox_[i]->setCurrentIndex(-1);
        cbox_[i]->setCurrentIndex(selectedRows[i]);

        auto cbox       = findChild<QComboBox *>(QString("comboBoxNet%1").arg(i + 1));
        auto model      = cbox->model();
        auto removeRows = model->rowCount();
        Models::AddEntry(model, tr("Null Driver"), NET_TYPE_NONE);
        Models::AddEntry(model, "SLiRP", NET_TYPE_SLIRP);

        if (network_ndev > 1)
            Models::AddEntry(model, "PCap", NET_TYPE_PCAP);

#ifdef HAS_VDE
        if (network_devmap.has_vde)
            Models::AddEntry(model, "VDE", NET_TYPE_VDE);
#endif
        
#if defined(__unix__) || defined(__APPLE__)
        Models::AddEntry(model, "TAP", NET_TYPE_TAP);
#endif

#ifdef USE_NETSWITCH
        Models::AddEntry(model, "Local Switch", NET_TYPE_NMSWITCH);
#ifdef ENABLE_NET_NRSWITCH
        Models::AddEntry(model, "Remote Switch", NET_TYPE_NRSWITCH);
#endif /* ENABLE_NET_NRSWITCH */
#endif /* USE_NETSWITCH */

        model->removeRows(0, removeRows);
        cbox->setCurrentIndex(cbox->findData(net_cards_conf[i].net_type));

        selectedRow = 0;

        if (network_ndev > 0) {
            QString currentPcapDevice = net_cards_conf[i].host_dev_name;
            cbox                      = findChild<QComboBox *>(QString("comboBoxIntf%1").arg(i + 1));
            model                     = cbox->model();
            removeRows                = model->rowCount();
            for (int c = 0; c < network_ndev; c++) {
                Models::AddEntry(model, tr(network_devs[c].description), c);
                if (QString(network_devs[c].device) == currentPcapDevice) {
                    selectedRow = c;
                }
            }
            model->removeRows(0, removeRows);
            cbox->setCurrentIndex(selectedRow);
        }

        if (net_cards_conf[i].net_type == NET_TYPE_VDE) {
#ifdef HAS_VDE
            QString currentVdeSocket = net_cards_conf[i].host_dev_name;
            auto editline = findChild<QLineEdit *>(QString("socketVDENIC%1").arg(i+1));
            editline->setText(currentVdeSocket);
#else
            ;
#endif
#if defined(__unix__) || defined(__APPLE__)
        } else if (net_cards_conf[i].net_type == NET_TYPE_TAP) {
            QString currentTapDevice = net_cards_conf[i].host_dev_name;
            auto editline = findChild<QLineEdit *>(QString("bridgeTAPNIC%1").arg(i+1));
            editline->setText(currentTapDevice);
#endif
#ifdef USE_NETSWITCH
        } else if (net_cards_conf[i].net_type == NET_TYPE_NMSWITCH) {
            auto *promisc_value = findChild<QCheckBox *>(QString("boxPromisc%1").arg(i + 1));
            promisc_value->setCheckState(net_cards_conf[i].promisc_mode == 1 ? Qt::CheckState::Checked : Qt::CheckState::Unchecked);
            auto *switch_group_value = findChild<QSpinBox *>(QString("spinnerSwitch%1").arg(i + 1));
            switch_group_value->setValue(net_cards_conf[i].switch_group + 1);
        } else if (net_cards_conf[i].net_type == NET_TYPE_NRSWITCH) {
            auto *hostname_value = findChild<QLineEdit *>(QString("hostnameSwitch%1").arg(i + 1));
            hostname_value->setText(net_cards_conf[i].nrs_hostname);
            auto *switch_group_value = findChild<QSpinBox *>(QString("spinnerSwitch%1").arg(i + 1));
            switch_group_value->setValue(net_cards_conf[i].switch_group + 1);
#endif /* USE_NETSWITCH */
        }
    }
}

void
SettingsNetwork::on_comboIndexChanged(int index)
{
    if (index < 0)
        return;

    enableElements(ui);
}

void
SettingsNetwork::on_pushButtonConf1_clicked()
{
    int   netCard = ui->comboBoxNIC1->currentData().toInt();
    auto *device  = network_card_getdevice(netCard);
    if (netCard == NET_INTERNAL)
        device = machine_get_net_device(machineId);
    DeviceConfig::ConfigureDevice(device, 1);
}

void
SettingsNetwork::on_pushButtonConf2_clicked()
{
    int   netCard = ui->comboBoxNIC2->currentData().toInt();
    auto *device  = network_card_getdevice(netCard);
    DeviceConfig::ConfigureDevice(device, 2);
}

void
SettingsNetwork::on_pushButtonConf3_clicked()
{
    int   netCard = ui->comboBoxNIC3->currentData().toInt();
    auto *device  = network_card_getdevice(netCard);
    DeviceConfig::ConfigureDevice(device, 3);
}

void
SettingsNetwork::on_pushButtonConf4_clicked()
{
    int   netCard = ui->comboBoxNIC4->currentData().toInt();
    auto *device  = network_card_getdevice(netCard);
    DeviceConfig::ConfigureDevice(device, 4);
}
