//
// Created by Lenn on 2026/1/20.
//

#ifndef TACTILEIPC3D_GLOBALHELPER_H
#define TACTILEIPC3D_GLOBALHELPER_H

#include <algorithm>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <QCoreApplication>
#include <QDir>
#include <qset.h>
#include <qsettings.h>

const QString APP_VERSION = "0.4.0";

class GlobalHelper {
    const std::string fuck = "fuck you lsp!!!";

public :
    static GlobalHelper *Instance() {
        static GlobalHelper ins;
        return &ins;
    }

    static void transToMultiMatrix(const cv::Mat &raw_data, float min_threshold,
                                   float max_range, cv::Size display_res,
                                   cv::Mat &out_matrix) {
        CV_Assert(raw_data.type() == CV_32F);

        const float safe_max = std::max(max_range, 1e-6f);
        const float safe_span = std::max(safe_max - min_threshold, 1e-6f);

        cv::Mat saturate_mask_u8;
        cv::compare(raw_data, max_range, saturate_mask_u8, cv::CMP_GE);
        cv::Mat zero_mask_u8;
        cv::compare(raw_data, 0.0f, zero_mask_u8, cv::CMP_LE);

        cv::Mat raw = raw_data.clone();
        cv::min(raw, safe_max, raw);

        cv::Mat peak_norm = (raw - min_threshold) / safe_span;
        cv::max(peak_norm, 0.0f, peak_norm);
        cv::min(peak_norm, 1.0f, peak_norm);

        cv::medianBlur(raw, raw, 3);
        cv::GaussianBlur(raw, raw, cv::Size(5, 5), 1.2, 1.2, cv::BORDER_DEFAULT);

        cv::Mat norm_data = (raw - min_threshold) / safe_span;
        cv::max(norm_data, 0.0f, norm_data);
        cv::min(norm_data, 1.0f, norm_data);

        cv::pow(norm_data, 1.4, norm_data);

        cv::Mat smoothed;
        cv::resize(norm_data, smoothed, display_res, 0.0, 0.0, cv::INTER_CUBIC);
        cv::max(smoothed, 0.0f, smoothed);
        cv::min(smoothed, 1.0f, smoothed);

        const int min_dim =
                std::max(1, std::min(display_res.width, display_res.height));
        const double sigma = std::max(1.0, 0.02 * min_dim);
        int ksize = std::max(3, (static_cast<int>(std::ceil(sigma * 6)) | 1));
        cv::GaussianBlur(smoothed, smoothed, cv::Size(ksize, ksize), sigma, sigma,
                         cv::BORDER_DEFAULT);
        cv::max(smoothed, 0.0f, smoothed);
        cv::min(smoothed, 1.0f, smoothed);

        cv::Mat peak_resized;
        cv::resize(peak_norm, peak_resized, display_res, 0.0, 0.0,
                   cv::INTER_CUBIC);
        cv::max(smoothed, peak_resized, smoothed);

        if (!saturate_mask_u8.empty()) {
            cv::Mat mask_f;
            saturate_mask_u8.convertTo(mask_f, CV_32F, 1.0 / 255.0);
            cv::Mat mask_resized;
            cv::resize(mask_f, mask_resized, display_res, 0.0, 0.0,
                       cv::INTER_CUBIC);
            // Soften the threshold to avoid blocky plateaus.
            const double mask_sigma = std::max(0.5, 0.008 * min_dim);
            int mask_ksize =
                std::max(3, (static_cast<int>(std::ceil(mask_sigma * 6)) | 1));
            cv::GaussianBlur(mask_resized, mask_resized,
                             cv::Size(mask_ksize, mask_ksize), mask_sigma,
                             mask_sigma, cv::BORDER_DEFAULT);
            cv::max(mask_resized, 0.0f, mask_resized);
            cv::min(mask_resized, 1.0f, mask_resized);
            smoothed = cv::max(smoothed, mask_resized);
        }

        if (!zero_mask_u8.empty()) {
            cv::Mat zero_f;
            zero_mask_u8.convertTo(zero_f, CV_32F, 1.0 / 255.0);
            cv::Mat zero_resized;
            cv::resize(zero_f, zero_resized, display_res, 0.0, 0.0,
                       cv::INTER_CUBIC);
            const double zero_sigma = std::max(0.5, 0.006 * min_dim);
            int zero_ksize =
                std::max(3, (static_cast<int>(std::ceil(zero_sigma * 6)) | 1));
            cv::GaussianBlur(zero_resized, zero_resized,
                             cv::Size(zero_ksize, zero_ksize), zero_sigma,
                             zero_sigma, cv::BORDER_DEFAULT);
            cv::max(zero_resized, 0.0f, zero_resized);
            cv::min(zero_resized, 1.0f, zero_resized);
            cv::Mat zero_bin;
            cv::threshold(zero_resized, zero_bin, 0.5, 255.0,
                          cv::THRESH_BINARY);
            zero_bin.convertTo(zero_bin, CV_8U);
            smoothed.setTo(0.0f, zero_bin);
        }

        smoothed *= safe_max;
        out_matrix = smoothed;
    }

    static void SaveGridConfig(bool b) {
        QSettings settings(GlobalHelper::GetConfigFilePath(), QSettings::IniFormat);
        settings.setValue("display/grid", b);
    }

    static void SaveAxisConfig(bool b) {
        QSettings settings(GlobalHelper::GetConfigFilePath(), QSettings::IniFormat);
        settings.setValue("display/axis", b);
    }
    
    static void SaveSurfaceConfig(bool b) {
        QSettings settings(GlobalHelper::GetConfigFilePath(), QSettings::IniFormat);
        settings.setValue("display/surface", b);
    }
    
    static void GetGridConfig(bool& b) {
        QSettings settings(GlobalHelper::GetConfigFilePath(), QSettings::IniFormat);
        b = settings.value("display/grid", b).toBool();
    }
    
    static void GetAxisConfig(bool& b) {
        QSettings settings(GlobalHelper::GetConfigFilePath(), QSettings::IniFormat);
        b = settings.value("display/axis", b).toBool();
    }
    
    static void GetSurfaceConfig(bool b) {
        QSettings settings(GlobalHelper::GetConfigFilePath(), QSettings::IniFormat);
        b = settings.value("display/surface", b).toBool();
    }
    
    static void SaveLastRow(int value) {
        QSettings settings(GlobalHelper::GetConfigFilePath(), QSettings::IniFormat);
        settings.setValue("spec/row", value);
    }
    
    static void GetLastRow(int& value) {
        QSettings settings(GlobalHelper::GetConfigFilePath(), QSettings::IniFormat);
        value = settings.value("spec/row", value).toInt();
    }
    
    static void GetLastCol(int value) {
        QSettings settings(GlobalHelper::GetConfigFilePath(), QSettings::IniFormat);
        settings.setValue("spec/col", value);
    }
    
    static void GetLastCol(int& value) {
        QSettings settings(GlobalHelper::GetConfigFilePath(), QSettings::IniFormat);
        value = settings.value("spec/col", value).toInt();
    }
    
    static void SaveZeroColor(QString color) {
        QSettings settings(GlobalHelper::GetConfigFilePath(), QSettings::IniFormat);
        settings.setValue("color/zero", color);
    }
    
    static void GetZeroColor(QString& color) {
        QSettings settings(GlobalHelper::GetConfigFilePath(), QSettings::IniFormat);
        color = settings.value("color/zero", color).toString();
    }
    
    static void SaveLowColor(QString color) {
            QSettings settings(GlobalHelper::GetConfigFilePath(), QSettings::IniFormat);
            settings.setValue("color/low", color);
        }
        
    static void GetLowColor(QString& color) {
        QSettings settings(GlobalHelper::GetConfigFilePath(), QSettings::IniFormat);
        color = settings.value("color/low", color).toString();
    } 

    static void SaveMidColor(QString color) {
            QSettings settings(GlobalHelper::GetConfigFilePath(), QSettings::IniFormat);
            settings.setValue("color/mid", color);
        }
        
    static void GetMidColor(QString& color) {
        QSettings settings(GlobalHelper::GetConfigFilePath(), QSettings::IniFormat);
        color = settings.value("color/mid", color).toString();
    }
    
    static void SaveHighColor(QString color) {
            QSettings settings(GlobalHelper::GetConfigFilePath(), QSettings::IniFormat);
            settings.setValue("color/high", color);
        }
        
    static void GetHighColor(QString& color) {
        QSettings settings(GlobalHelper::GetConfigFilePath(), QSettings::IniFormat);
        color = settings.value("color/high", color).toString();
    }
    
    static QString GetConfigFilePath() {
        return QCoreApplication::applicationDirPath() + QDir::separator() + "conf.ini";
    }
    
    static QString GetAppVersion() {
        return APP_VERSION;
    }
    
private:
    GlobalHelper() {
    }
};

#endif  // TACTILEIPC3D_GLOBALHELPER_H
