/*
 * Copyright (c) 2020-2026 Valve Corporation
 * Copyright (c) 2020-2026 LunarG, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Authors:
 * - Christophe Riccio <christophe@lunarg.com>
 */

#include "widget_tab_driver_path.h"
#include "item_list_device.h"
#include "tab_drivers.h"
#include "mainwindow.h"
#include "style.h"

#include "../vkconfig_core/configurator.h"
#include "../vkconfig_core/json.h"

#include <QFileDialog>
#include <QShortcut>

TabDrivers::TabDrivers(MainWindow &window, std::shared_ptr<Ui::MainWindow> ui) : Tab(TAB_DRIVERS, window, ui) {
    Configurator &configurator = Configurator::Get();

    if (configurator.vulkan_system_info.loaderVersion < Version(1, 4, 322) && false) {
        this->ui->tab_widget->setTabEnabled(TAB_DRIVERS, false);
        this->ui->tab_widget->setTabToolTip(TAB_DRIVERS, "Additional Vulkan Drivers require Vulkan Loader 1.4.322 or newer");
    }

    this->connect(this->ui->driver_group_box_override, SIGNAL(toggled(bool)), this, SLOT(on_driver_override_toggled(bool)));
    this->connect(this->ui->driver_mode, SIGNAL(currentIndexChanged(int)), this, SLOT(on_driver_mode_changed(int)));
    this->connect(this->ui->driver_forced_name, SIGNAL(currentIndexChanged(int)), this, SLOT(on_driver_name_changed(int)));

    this->connect(this->ui->driver_group_box_paths, SIGNAL(toggled(bool)), this, SLOT(on_driver_paths_toggled(bool)));
    this->connect(this->ui->driver_browse, SIGNAL(clicked()), this, SLOT(on_driver_browse_pressed()));
    this->connect(this->ui->driver_path, SIGNAL(returnPressed()), this, SLOT(on_driver_append_pressed()));

    this->connect(this->ui->driver_search, SIGNAL(textEdited(QString)), this, SLOT(on_search_textEdited(QString)));
    this->connect(this->ui->driver_search, SIGNAL(returnPressed()), this, SLOT(on_search_next_pressed()));
    this->connect(this->ui->driver_search_clear, SIGNAL(clicked()), this, SLOT(on_search_clear_pressed()));

    QShortcut *shortcut_override = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_Space), this->ui->driver_group_box_override);
    this->connect(shortcut_override, SIGNAL(activated()), this, SLOT(on_driver_override_toggled()));

    QShortcut *shortcut_search = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_F), this->ui->driver_paths_list);
    this->connect(shortcut_search, SIGNAL(activated()), this, SLOT(on_focus_search()));
    QShortcut *shortcut_browse = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_O), this->ui->driver_paths_list);
    this->connect(shortcut_browse, SIGNAL(activated()), this, SLOT(on_driver_browse_pressed()));

    // this->ui->driver_group_box_override->installEventFilter(&window);
    this->ui->drivers_device_list->installEventFilter(&window);
    this->ui->driver_paths_list->installEventFilter(&window);

    this->UpdateUI(UPDATE_REBUILD_UI);
}

TabDrivers::~TabDrivers() {}

void TabDrivers::UpdateUI(UpdateUIMode ui_update_mode) {
    Configurator &configurator = Configurator::Get();

    this->ui->driver_search_clear->setEnabled(!this->ui->driver_search->text().isEmpty());
    this->ui->driver_search->setFocus();

    switch (ui_update_mode) {
        case UPDATE_REFRESH_UI:
        case UPDATE_REBUILD_UI: {
            this->ui->driver_group_box_override->blockSignals(true);
            this->ui->driver_group_box_override->setChecked(configurator.driver_override_enabled);
            this->ui->driver_group_box_override->blockSignals(false);
            this->ui->driver_mode->blockSignals(true);
            this->ui->driver_mode->setCurrentIndex(configurator.driver_override_mode);
            this->ui->driver_mode->blockSignals(false);

            switch (configurator.driver_override_mode) {
                case DRIVER_MODE_SINGLE: {
                    this->ui->driver_forced_name->blockSignals(true);
                    this->ui->driver_forced_name->clear();

                    for (std::size_t i = 0, n = configurator.vulkan_system_info.physicalDevices.size(); i < n; ++i) {
                        const VulkanPhysicalDeviceInfo &info = configurator.vulkan_system_info.physicalDevices[i];

                        const int current_index = this->ui->driver_forced_name->count();
                        this->ui->driver_forced_name->addItem(
                            (info.GetLabel() + format(" (%s)", info.GetVersion().c_str())).c_str());

                        const std::string &version =
                            format("%s driver: %s - Vulkan API version: %s", GetLabel(info.vendorID).c_str(),
                                   info.GetVersion().c_str(), info.apiVersion.str().c_str());

                        this->ui->driver_forced_name->setItemData(current_index, version.c_str(), Qt::ToolTipRole);
                    }
                    this->ui->driver_forced_name->setCurrentIndex(configurator.GetActivePhysicalDeviceIndex());
                    this->ui->driver_forced_name->setToolTip(
                        this->ui->driver_forced_name->itemData(configurator.GetActivePhysicalDeviceIndex(), Qt::ToolTipRole)
                            .toString());
                    this->ui->driver_forced_name->blockSignals(false);

                    this->ui->driver_name_label->setVisible(true);
                    this->ui->driver_forced_name->setVisible(true);
                    this->ui->drivers_label_first->setVisible(false);
                    this->ui->drivers_device_list->setVisible(false);
                    this->ui->drivers_label_last->setVisible(false);
                } break;
                case DRIVER_MODE_SORTED: {
                    this->ui->drivers_device_list->blockSignals(true);
                    this->ui->drivers_device_list->clear();
                    for (std::size_t i = 0, n = configurator.driver_override_list.size(); i < n; ++i) {
                        const VulkanPhysicalDeviceInfo *info = configurator.GetPhysicalDevice(configurator.driver_override_list[i]);
                        const std::string &version =
                            format("%s driver: %s - Vulkan API version: %s", GetLabel(info->vendorID).c_str(),
                                   info->GetVersion().c_str(), info->apiVersion.str().c_str());

                        QListWidgetItem *item = new ListItemDevice(configurator.driver_override_list[i], info->GetVersion());
                        item->setIcon(::Get(configurator.current_theme_mode, ICON_DRAG));
                        item->setToolTip(version.c_str());
                        this->ui->drivers_device_list->addItem(item);
                    }

                    this->ui->drivers_device_list->blockSignals(false);

                    this->ui->driver_name_label->setVisible(false);
                    this->ui->driver_forced_name->setVisible(false);
                    this->ui->drivers_label_first->setVisible(true);
                    this->ui->drivers_device_list->setVisible(true);
                    this->ui->drivers_label_last->setVisible(true);
                } break;
                case DRIVER_MODE_NONE: {
                    this->ui->driver_name_label->setVisible(false);
                    this->ui->driver_forced_name->setVisible(false);
                    this->ui->drivers_label_first->setVisible(false);
                    this->ui->drivers_device_list->setVisible(false);
                    this->ui->drivers_label_last->setVisible(false);
                } break;
                default: {
                    assert(0);
                    break;
                }
            }

            this->ui->driver_group_box_paths->blockSignals(true);
            this->ui->driver_group_box_paths->setChecked(configurator.driver_paths_enabled && configurator.driver_override_enabled);
            this->ui->driver_group_box_paths->blockSignals(false);

            this->ui->driver_paths_list->clear();
            this->ui->driver_paths_list->blockSignals(true);

            for (auto it = configurator.driver_paths.begin(); it != configurator.driver_paths.end(); ++it) {
                if (!this->driver_filter.empty()) {
                    const std::string text = it->first.AbsolutePath();

                    std::string lower_text = ::ToLowerCase(text);
                    std::string driver_filter_search = ::ToLowerCase(this->driver_filter);
                    if (lower_text.find(driver_filter_search.c_str()) == std::string::npos) {
                        continue;
                    }
                }

                QListWidgetItem *item_state = new QListWidgetItem;
                item_state->setFlags(item_state->flags() | Qt::ItemIsSelectable);
                item_state->setSizeHint(QSize(0, ITEM_HEIGHT));
                DriverPathWidget *drivers_path_widget = new DriverPathWidget(it->first, it->second);
                this->connect(drivers_path_widget, SIGNAL(itemChanged()), this, SLOT(on_paths_changed()));
                this->connect(drivers_path_widget, SIGNAL(itemToggled()), this, SLOT(on_paths_toggled()));

                ui->driver_paths_list->addItem(item_state);
                ui->driver_paths_list->setItemWidget(item_state, drivers_path_widget);
            }

            this->ui->driver_paths_list->blockSignals(false);
        } break;
    }

    this->window.UpdateUI_Status();
}

void TabDrivers::CleanUI() {}

bool TabDrivers::EventFilter(QObject *target, QEvent *event) {
    if (target == nullptr || event == nullptr) {
        return true;
    }

    QEvent::Type event_type = event->type();

    if (event_type == QEvent::Wheel) {
        return true;
    }

    if (target == this->ui->drivers_device_list && event_type == QEvent::ChildRemoved) {
        // Drivers were reordered, we need to update the driver list

        Configurator &configurator = Configurator::Get();

        configurator.driver_override_list.clear();
        for (int i = 0, n = ui->drivers_device_list->count(); i < n; ++i) {
            const ListItemDevice *item = static_cast<ListItemDevice *>(ui->drivers_device_list->item(i));

            configurator.driver_override_list.push_back(item->info);
        }

        configurator.Override(OVERRIDE_AREA_LOADER_SETTINGS_BIT);

        return true;
    }

    return false;
}

void TabDrivers::on_focus_search() { this->ui->driver_search->setFocus(); }

void TabDrivers::on_search_textEdited(const QString &text) {
    this->driver_filter = text.toStdString();

    this->ui->driver_search_clear->setEnabled(!text.isEmpty());

    this->UpdateUI(UPDATE_REBUILD_UI);
}

void TabDrivers::on_search_clear_pressed() {
    this->driver_filter.clear();

    this->ui->driver_search->clear();
    this->ui->driver_search_clear->setEnabled(false);

    this->UpdateUI(UPDATE_REBUILD_UI);
}

void TabDrivers::on_search_next_pressed() {}

void TabDrivers::on_driver_override_toggled() {
    this->ui->driver_group_box_override->setChecked(!this->ui->driver_group_box_override->isChecked());
}

void TabDrivers::on_driver_override_toggled(bool checked) {
    Configurator &configurator = Configurator::Get();

    int index = std::max<int>(this->ui->driver_forced_name->currentIndex(), 0);

    configurator.driver_override_enabled = checked;
    configurator.driver_override_info = ::GetDeviceInfo(configurator.vulkan_system_info.physicalDevices[index]);
    configurator.Override(OVERRIDE_AREA_LOADER_SETTINGS_BIT);

    this->UpdateUI(UPDATE_REBUILD_UI);
}

void TabDrivers::on_driver_mode_changed(int index) {
    Configurator &configurator = Configurator::Get();
    configurator.driver_override_mode = static_cast<DriverMode>(index);
    configurator.Override(OVERRIDE_AREA_LOADER_SETTINGS_BIT);

    this->UpdateUI(UPDATE_REBUILD_UI);
}

void TabDrivers::on_driver_name_changed(int index) {
    Configurator &configurator = Configurator::Get();
    configurator.driver_override_info = ::GetDeviceInfo(configurator.vulkan_system_info.physicalDevices[index]);
    configurator.Override(OVERRIDE_AREA_LOADER_SETTINGS_BIT);

    this->UpdateUI(UPDATE_REBUILD_UI);
}

void TabDrivers::on_driver_paths_toggled(bool checked) {
    Configurator &configurator = Configurator::Get();

    configurator.driver_paths_enabled = checked;
    configurator.UpdateVulkanSystemInfo();

    this->UpdateUI(UPDATE_REBUILD_UI);
}

void TabDrivers::on_paths_changed() {
    Configurator &configurator = Configurator::Get();

    configurator.UpdateVulkanSystemInfo();

    this->UpdateUI(UPDATE_REBUILD_UI);
}

void TabDrivers::on_paths_toggled() {
    Configurator &configurator = Configurator::Get();

    configurator.UpdateVulkanSystemInfo();

    this->UpdateUI(UPDATE_REBUILD_UI);
}

void TabDrivers::on_driver_append_pressed() {
    Configurator &configurator = Configurator::Get();
    const Path &selected_path = this->ui->driver_path->text().toStdString();

    if (selected_path.Empty()) {
        return;
    }

    if (!selected_path.Exists()) {
        QMessageBox alert;
        alert.setWindowTitle("Vulkan Driver Manifest directory not found");
        alert.setText("The directory:");
        alert.setInformativeText(selected_path.AbsolutePath().c_str());
        alert.setStandardButtons(QMessageBox::Ok);
        alert.setDefaultButton(QMessageBox::Ok);
        alert.setIcon(QMessageBox::Warning);
        alert.exec();
        return;
    }

    configurator.last_driver_dir = selected_path;

    const std::vector<Path> drivers_paths = ::CollectDriversPaths(selected_path);
    if (drivers_paths.empty()) {
        QMessageBox alert;
        alert.setWindowTitle("No Vulkan Driver Manifest found in the directory");
        alert.setText("The directory:");
        alert.setInformativeText(selected_path.AbsolutePath().c_str());
        alert.setStandardButtons(QMessageBox::Ok);
        alert.setDefaultButton(QMessageBox::Ok);
        alert.setIcon(QMessageBox::Warning);
        alert.exec();
        return;
    }

    for (std::size_t i = 0, n = drivers_paths.size(); i < n; ++i) {
        configurator.driver_paths.insert(std::pair(drivers_paths[i], true));
    }

    configurator.UpdateVulkanSystemInfo();

    this->UpdateUI(UPDATE_REBUILD_UI);
}

void TabDrivers::on_driver_browse_pressed() {
    Configurator &configurator = Configurator::Get();

    const Path &selected_path = QFileDialog::getExistingDirectory(this->ui->driver_browse, "Adding Driver Manifests Directory...",
                                                                  configurator.last_driver_dir.AbsolutePath().c_str())
                                    .toStdString();

    if (selected_path.Empty()) {
        return;
    }
    /*
        QJsonDocument document = ParseJsonFile(selected_path.AbsolutePath().c_str());
        if (document.isNull() || document.isEmpty()) {
            QMessageBox alert;
            alert.setWindowTitle("Could not open the JSON file");
            alert.setText("The path");
            alert.setInformativeText(selected_path.AbsolutePath().c_str());
            alert.setStandardButtons(QMessageBox::Ok);
            alert.setDefaultButton(QMessageBox::Ok);
            alert.setIcon(QMessageBox::Warning);
            alert.exec();
            return;
        }

        const QJsonObject& json_root_object = document.object();
        if (json_root_object.value("ICD") == QJsonValue::Undefined) {
            QMessageBox alert;
            alert.setWindowTitle("Could not open the JSON file");
            alert.setText("The path");
            alert.setInformativeText(selected_path.AbsolutePath().c_str());
            alert.setStandardButtons(QMessageBox::Ok);
            alert.setDefaultButton(QMessageBox::Ok);
            alert.setIcon(QMessageBox::Warning);
            alert.exec();
            return;
        }
    */
    this->ui->driver_path->setText(selected_path.AbsolutePath().c_str());

    this->on_driver_append_pressed();
}
