/*
 * Copyright (C) 2023, KylinSoft Co., Ltd.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
 *
**/
#include "outputconfig.h"
#include "resolutionslider.h"
#include "utils.h"
#include "scalesize.h"
#include "ukcccommon.h"
using namespace ukcc;

#include <QStringBuilder>
#include <QFormLayout>
#include <QComboBox>
#include <QCheckBox>
#include <QHBoxLayout>
#include <QLabel>
#include <QPushButton>
#include <QGroupBox>
#include <QMessageBox>

#include <QComboBox>
#include <QGSettings>
#include <QDBusInterface>
#include <QProcess>
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
#include <QStandardPaths>

#include <KF5/KScreen/kscreen/output.h>
#include <KF5/KScreen/kscreen/edid.h>
#include <KF5/KScreen/kscreen/log.h>
#include <KF5/KScreen/kscreen/configmonitor.h>
#include <KF5/KScreen/kscreen/getconfigoperation.h>
#include <KF5/KScreen/kscreen/setconfigoperation.h>
#ifdef KY_SDK_SYSINFO
#include <kysdk/kysdk-system/libkysysinfo.h>
#endif
#include "combobox.h"

double mScaleres = 0;
bool autoScaleChange = false;
QList<float> kRadeonRate{59.9402, 29.98};
CONFIG changeItm = INIT;
using namespace KScreen;
OutputConfig::OutputConfig(QWidget *parent) :
    UkccFrame(parent),
    mOutput(nullptr)
{
    this->setContainer(true);
}

OutputConfig::OutputConfig(const KScreen::OutputPtr &output, QWidget *parent) :
    UkccFrame(parent)
{
    this->setContainer(true);
    setOutput(output);
}

OutputConfig::~OutputConfig()
{
}

void OutputConfig::setTitle(const QString &title)
{
    mTitle->setText(title);
}

void OutputConfig::initUi()
{
    GetConfigOperation op;
    op.exec();
    const ConfigPtr config = op.config();
    initConfig(config);

    setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);

    QVBoxLayout *vbox = new QVBoxLayout(this);
    vbox->setMargin(0);
    vbox->setSpacing(1);

    // 分辨率下拉框
    mResolution = new ResolutionSlider(mOutput, this);

    QLabel *resLabel = new QLabel(this);
    //~ contents_path /Display/resolution
    resLabel->setText(tr("resolution"));
    resLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
    resLabel->setFixedWidth(108);

    QHBoxLayout *resLayout = new QHBoxLayout();
    resLayout->setContentsMargins(16, 0, 16, 0);
    resLayout->addWidget(resLabel);
    resLayout->addWidget(mResolution);
    resLayout->setSpacing(16);

    UkccFrame *resFrame = new UkccFrame(this, UkccFrame::BorderRadiusStyle::None, true);
    resFrame->setLayout(resLayout);
    vbox->addWidget(resFrame);

    connect(mResolution, &ResolutionSlider::resolutionChanged,
            this, [=](QSize size, bool emitFlag){
                slotResolutionChanged(size, emitFlag);
                scaleChanged(size);
                UkccCommon::buriedSettings(QString("display"), QString("mResolution"), QString("select"), Utils::sizeToString(size));
            });

    // 方向下拉框
    mRotation = new QComboBox(this);

    QLabel *rotateLabel = new QLabel(this);
    //~ contents_path /Display/orientation
    rotateLabel->setText(tr("orientation"));
    rotateLabel->setFixedWidth(108);

    QHBoxLayout *rotateLayout = new QHBoxLayout();
    rotateLayout->setContentsMargins(16, 0, 16, 0);
    rotateLayout->setSpacing(16);
    rotateLayout->addWidget(rotateLabel);

    rotateLayout->addWidget(mRotation);

    mRotateFrame = new UkccFrame(this, UkccFrame::BorderRadiusStyle::None, true);
    mRotateFrame->setLayout(rotateLayout);

    mRotation->addItem(tr("arrow-up"), KScreen::Output::None);
    mRotation->addItem(tr("90° arrow-right"), KScreen::Output::Right);
    mRotation->addItem(tr("90° arrow-left"), KScreen::Output::Left);
    mRotation->addItem(tr("arrow-down"), KScreen::Output::Inverted);
    connect(mRotation, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
            this, &OutputConfig::slotRotationChanged);
    mRotation->setCurrentIndex(mRotation->findData(mOutput->rotation()));

    vbox->addWidget(mRotateFrame);
    // 自动旋转
    mAutoRotation = new KSwitchButton(this);

    QLabel *autoRotateLabel = new QLabel(this);
    //~ contents_path /Display/auto rotation
    autoRotateLabel->setText(tr("auto rotation"));
    autoRotateLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
    autoRotateLabel->setFixedWidth(108);

    QHBoxLayout *autoRotateLayout = new QHBoxLayout();
    autoRotateLayout->setContentsMargins(16, 0, 16, 0);
    autoRotateLayout->setSpacing(16);
    autoRotateLayout->addWidget(autoRotateLabel);
    autoRotateLayout->addStretch();
    autoRotateLayout->addWidget(mAutoRotation);

    mAutoRotationFrame = new UkccFrame(this, UkccFrame::BorderRadiusStyle::None, true);
    mAutoRotationFrame->setLayout(autoRotateLayout);

    vbox->addWidget(mAutoRotationFrame);
    mAutoRotationFrame->setVisible(false);

    mStatusManager = new QDBusInterface("com.kylin.statusmanager.interface",
                                        "/",
                                        "com.kylin.statusmanager.interface",
                                        QDBusConnection::sessionBus(), this);

    if (mStatusManager->isValid()) {
        // 仅平板模式下，且硬件支持自动旋转才显示
        QDBusReply<bool> isSupportedAuto = mStatusManager->call("is_supported_autorotation");
        QDBusReply<bool> tabletmode = mStatusManager->call("get_current_tabletmode");
        qDebug() << "tabletmode = " << tabletmode << "isSupportedAuto = " << isSupportedAuto;
        if (tabletmode && isSupportedAuto) {
            mAutoRotationFrame->setVisible(true);
        }
        connect(mStatusManager, SIGNAL(mode_change_signal(bool)), this, SLOT(tabletChanged(bool)));

        QDBusReply<bool> isAutoRotation = mStatusManager->call("get_auto_rotation");
        mAutoRotation->setChecked(isAutoRotation);
        mRotation->setEnabled(!isAutoRotation);
        connect(mStatusManager, SIGNAL(auto_rotation_change_signal(bool)), this, SLOT(rotationDbusSlot(bool)));
        connect(mStatusManager, SIGNAL(rotations_change_signal(QString)), this, SLOT(rotationDirectionSlot(QString)));
    }

    connect(mAutoRotation, &KSwitchButton::stateChanged, this, [=](bool checked){
        mRotation->setEnabled(!checked);
        if (mStatusManager->isValid()) {
            mStatusManager->call("set_auto_rotation", checked, "ukcc", "set_auto_rotation");
        }
    });

    // 刷新率下拉框
    mRefreshRate = new QComboBox(this);

    QLabel *freshLabel = new QLabel(this);
    //~ contents_path /Display/frequency
    freshLabel->setText(tr("frequency"));
    freshLabel->setFixedWidth(108);

    QHBoxLayout *freshLayout = new QHBoxLayout();
    freshLayout->setContentsMargins(16, 0, 16, 0);
    freshLayout->setSpacing(16);
    freshLayout->addWidget(freshLabel);
    freshLayout->addWidget(mRefreshRate);

    UkccFrame *freshFrame = new UkccFrame(this, UkccFrame::BorderRadiusStyle::None, true);
    freshFrame->setLayout(freshLayout);
    vbox->addWidget(freshFrame);

    slotResolutionChanged(mResolution->currentResolution(), true);
    connect(mRefreshRate, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
            this, &OutputConfig::slotRefreshRateChanged);

    initConnection();

    if (!(UkccCommon::isOpenkylin() && UkccCommon::isWayland()))
        return;
    // 缩放率下拉框

    UkccFrame *scaleFrame = new UkccFrame(this, UkccFrame::BorderRadiusStyle::None, true);

    QHBoxLayout *scaleLayout = new QHBoxLayout(scaleFrame);
    scaleLayout->setContentsMargins(16, 0, 16, 0);
    scaleLayout->setSpacing(16);

    mScaleCombox = new QComboBox(this);
    mScaleCombox->setObjectName("scaleCombox");

    QLabel *scaleLabel = new QLabel(this);
    //~ contents_path /Display/screen zoom
    scaleLabel->setText(tr("screen zoom"));
    scaleLabel->setFixedWidth(108);

    scaleLayout->addWidget(scaleLabel);
    scaleLayout->addWidget(mScaleCombox);

    vbox->addWidget(scaleFrame);
    connect(mScaleCombox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
            this, [=](int index){
        slotScaleChanged(index);
    });

    connect(mOutput.data(), &KScreen::Output::scaleChanged,
               this, &OutputConfig::initScaleItem);

    mScaleCombox->setEnabled(mOutput->isEnabled());
    initScaleItem();
}

double OutputConfig::getScreenScale()
{
    double scale = 1.0;
    if (QGSettings::isSchemaInstalled(SCALE_SCHEMAS)) {
        if (mDpiSettings->keys().contains("scalingFactor")) {
            scale = mDpiSettings->get(SCALE_KEY).toDouble();
        }
    }
    return scale;
}

void OutputConfig::initConnection()
{
    connect(mOutput.data(), &KScreen::Output::isConnectedChanged,
            this, [=]() {
        if (!mOutput->isConnected()) {
            setVisible(false);
        }
    });
    connect(mOutput.data(), &KScreen::Output::isEnabledChanged,
            this, [=]() {
        emit enabledChanged();
    });

    connect(mOutput.data(), &KScreen::Output::rotationChanged,
            this, [=]() {
        const int index = mRotation->findData(mOutput->rotation());
        mRotation->blockSignals(true);
        mRotation->setCurrentIndex(index);
        mRotation->blockSignals(false);
    });
    //监听，否则无法处理修改分辨率/刷新率未保存
    connect(mOutput.data(), &KScreen::Output::currentModeIdChanged, this, [=]() {
        if (!mIsRestore || !mOutput->currentMode()) {
            mIsRestore = true;
            if (!mSetRestore) {
                return;
            }
        }
        mSetRestore = false;
        //分辨率改变时，触发该信号重新加载刷新率，用于修改分辨率之后但未保存
        if (mResolution->currentResolution() != mOutput->currentMode()->size()) {
            mResolution->setResolution(mOutput->currentMode()->size());
            Q_EMIT scaleChanged(mOutput->currentMode()->size());
            slotResolutionChanged(mOutput->currentMode()->size(), false);
        } else {
            //分辨率未修改，刷新率修改,用于修改刷新率之后但未保存
            for (int i = 0; i < mRefreshRate->count(); i++) {
                if (mRefreshRate->count() == 1 || \
                        refreshRateToText(mOutput->currentMode()->refreshRate()) == mRefreshRate->itemText(i)) {
                    mRefreshRate->blockSignals(true);
                    mRefreshRate->setCurrentIndex(i);
                    mRefreshRate->blockSignals(false);
                    break;
                }
            }
        }
    });

    connect(mOutput.data(), &KScreen::Output::isEnabledChanged,
            this, [=](){
        slotEnableWidget();
    });
}

QString OutputConfig::scaleToString(double scale)
{
    return QString::number(scale * 100) + "%";
}

void OutputConfig::hideComponent()
{
    QDBusInterface ifc("com.kylin.screen.rotation",
                       "/",
                       "com.kylin.screen.rotation.interface",
                       QDBusConnection::systemBus());

    bool isShowRotation = true;
#ifdef KY_SDK_SYSINFO
    isShowRotation = !(QString(QLatin1String(kdk_system_get_hostCloudPlatform())) == "huawei");
#endif
    if (ifc.isValid()) {
        QDBusReply<QString> res = ifc.call("GetCurrentScreenStatus");
        isShowRotation = (isShowRotation && res.value().isEmpty());
    }
    mRotateFrame->setVisible(isShowRotation);
}

void OutputConfig::setOutput(const KScreen::OutputPtr &output)
{
    mOutput = output;
    initUi();
    hideComponent();
}

KScreen::OutputPtr OutputConfig::output() const
{
    return mOutput;
}

//只修改刷新率时，不应该运行此函数
void OutputConfig::slotResolutionChanged(const QSize &size, bool emitFlag)
{
    // Ignore disconnected outputs
    if (!size.isValid()) {
        return;
    }
    bool mIsModeInit = false;
    QString modeID;
    KScreen::ModePtr selectMode;
    KScreen::ModePtr currentMode = mOutput->currentMode();
    QList<KScreen::ModePtr> modes;
    Q_FOREACH (const KScreen::ModePtr &mode, mOutput->modes()) {
        //初始化时,currentMode可能为空(比如刚插上屏幕)
        if (!currentMode || (currentMode && currentMode->size() == size)) {
            if (currentMode) {
                selectMode = currentMode;
            }
            mIsModeInit = true;
        }
        if (mode->size() == size) {
            if (!mIsModeInit || !currentMode) {
                selectMode = mode;
            }
            modes << mode;
        }
    }
    //非初始化，则设置选中(用户设置)的mode为该分辨率下刷新率最大的mode
    if (!mIsModeInit) {
        selectMode = findBestMode(selectMode->size());
    }
    modeID = selectMode->id();
    mRefreshRate->blockSignals(true);
    mRefreshRate->clear();
    mRefreshRate->blockSignals(false);

    for (int i = 0, total = modes.count(); i < total; ++i) {
        const KScreen::ModePtr mode = modes.at(i);
        QString comText  = refreshRateToText(mode->refreshRate());
        int     comIndex = 0;
        bool alreadyExisted = false;
        for (int j = 0; j < mRefreshRate->count(); ++j) {
            if (comText == mRefreshRate->itemText(j)) {
                alreadyExisted = true;
                break;
            }
        }

        if (alreadyExisted == false) {   //不添加已经存在的项
            if (mRefreshRate->count() > 0) {
                for (int r = 0; r < mRefreshRate->count(); ++r) {
                    if (comText.compare(mRefreshRate->itemText(r)) < 0) {
                        comIndex = r + 1;
                    } else {
                        break;
                    }
                }
            }
            mRefreshRate->blockSignals(true);
            mRefreshRate->insertItem(comIndex, comText, mode->id());
            mRefreshRate->blockSignals(false);
        }

        //mode是选中的mode，则设置
        if (mode == selectMode && mRefreshRate->count() > 0) {
            mRefreshRate->blockSignals(true);
            mRefreshRate->setCurrentIndex(comIndex);
            mRefreshRate->blockSignals(false);
        }
    }

    if (mRefreshRate->count() == 0) {
        mRefreshRate->blockSignals(true);
        mRefreshRate->addItem(tr("auto"), -1);
        mRefreshRate->blockSignals(false);
    } else {
        if (-1 == mRefreshRate->currentIndex()) {
            modeID = mRefreshRate->itemData(0).toString();
            // 避免选择50hz以下刷新率为空
            mRefreshRate->blockSignals(true);
            mRefreshRate->setCurrentIndex(0);
            mRefreshRate->blockSignals(false);
        }
    }

    mOutput->setCurrentModeId(modeID);

    if (!mIsModeInit) {
        mIsRestore = false;
        if (emitFlag) {
            changeItm = RESOLUTION;
            Q_EMIT changed();
        }
    }
}

void OutputConfig::slotRotationChanged(int index)
{
    KScreen::Output::Rotation rotation
        = static_cast<KScreen::Output::Rotation>(mRotation->itemData(index).toInt());
    mOutput->blockSignals(true);
    mOutput->setRotation(rotation);
    mOutput->blockSignals(false);
    mOutput.data()->blockSignals(true);

    changeItm = ORIENTATION;
    Q_EMIT toSetScreenPos();//要在save之前修正坐标
    Q_EMIT changed();
    UkccCommon::buriedSettings(QString("display"), QString("mRotation"), QString("select"), QString::number(index));
    //widget.cpp中收到changed信号会延迟200+1000ms,因此这里相应延迟，避免接收到信号改变导致方向多次修改。
    QTimer::singleShot(1400, this, [=](){
        mOutput.data()->blockSignals(false);
    });
}

void OutputConfig::slotScaleChanged(int index)
{
    qreal kscreenScale = mScaleCombox->itemData(index).toDouble();
    disconnect(mOutput.data(), &KScreen::Output::scaleChanged,
            this, &OutputConfig::initScaleItem);
    mOutput->setScale(kscreenScale);
    connect(mOutput.data(), &KScreen::Output::scaleChanged,
            this, &OutputConfig::initScaleItem);
    changeItm = SCALE;
    Q_EMIT changed();
}

void OutputConfig::rotationDbusSlot(bool autoRotation)
{
    mRotation->setEnabled(!autoRotation);
    mAutoRotation->blockSignals(true);
    mAutoRotation->setChecked(autoRotation);
    mAutoRotation->blockSignals(false);
}

void OutputConfig::tabletChanged(bool tabletMode)
{
    QDBusReply<bool> isSupportedAuto = mStatusManager->call("is_supported_autorotation");
    qDebug() << "tabletmode = " << tabletMode << "isSupportedAuto = " << isSupportedAuto;
    if (tabletMode && isSupportedAuto) {
        mAutoRotationFrame->setVisible(true);
    } else {
        mAutoRotationFrame->setVisible(false);
    }
}

void OutputConfig::rotationDirectionSlot(QString rotationDirection)
{
    mRotation->blockSignals(true);
    mRotation->setCurrentIndex(mRotation->findData(mOutput->rotation()));
    mRotation->blockSignals(false);
}

void OutputConfig::slotRefreshRateChanged(int index)
{
    QString modeId;
    modeId = mRefreshRate->itemData(index).toString();
    qDebug() << "(slotRefreshRateChanged)modeId is:" << modeId << endl;
    mOutput->blockSignals(true);
    mIsRestore = false;
    mOutput->setCurrentModeId(modeId);
    mOutput->blockSignals(false);
    changeItm = FREQUENCY;
    Q_EMIT changed();
    UkccCommon::buriedSettings(QString("display"), QString("mRefreshRate"), QString("select"), mRefreshRate->currentText());
}

void OutputConfig::slotEnableWidget()
{
    bool isEnable = mOutput.data()->isEnabled();
    mResolution->setEnabled(isEnable);
    mRotation->setEnabled(isEnable);
    mRefreshRate->setEnabled(isEnable);
}

void OutputConfig::setShowScaleOption(bool showScaleOption)
{
    mShowScaleOption = showScaleOption;
    if (mOutput) {
        initUi();
    }
}

bool OutputConfig::showScaleOption() const
{
    return mShowScaleOption;
}

// 拿取配置
void OutputConfig::initConfig(const KScreen::ConfigPtr &config)
{
    mConfig = config;
}

void OutputConfig::initScaleItem()
{
    if (!UkccCommon::isOpenkylin() && !UkccCommon::isWayland())
        return;
    mScaleCombox->blockSignals(true);
    if (!mOutput->currentMode())
        return;
    QSize scalesize = mOutput->currentMode()->size();
    mScaleCombox->clear();
    mScaleCombox->addItem("100%", 1.0);
    if (scalesize.width() > 1024 ) {
        mScaleCombox->addItem("125%", 1.25);
    }
    if (scalesize.width() == 1920 ) {
        mScaleCombox->addItem("150%", 1.5);
    }
    if (scalesize.width() > 1920) {
        mScaleCombox->addItem("150%", 1.5);
        mScaleCombox->addItem("175%", 1.75);
    }
    if (scalesize.width() >= 2160) {
        mScaleCombox->addItem("200%", 2.0);
    }
    if (scalesize.width() > 2560) {
        mScaleCombox->addItem("225%", 2.25);
    }
    if (scalesize.width() > 3072) {
        mScaleCombox->addItem("250%", 2.5);
    }
    if (scalesize.width() > 3840) {
        mScaleCombox->addItem("275%", 2.75);
    }

    if (mScaleCombox->findData(mOutput->scale()) == -1) {
        mOutput->setScale(1);
    }

    mScaleCombox->setCurrentText(QString::number(mOutput->scale() * 100) + "%");
    mScaleCombox->blockSignals(false);
}

QString OutputConfig::refreshRateToText(float refreshRate)
{
    QRegExp rx;
    rx.setPattern("(\\.){0,1}0+$");
    return tr("%1 Hz").arg((QString::number(refreshRate,'f',2)).replace(rx,""));
}

double OutputConfig::getGlobalData(const OutputPtr &output)
{
    QString hash = mConfig->connectedOutputsHash();

    QString scaleDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)
            %QStringLiteral("/kscreen/scale/");
    QFile file(scaleDir % hash);
    if (!file.open(QIODevice::ReadOnly)) {
        qDebug() << "Failed to open file" << file.fileName();
        return 0;
    }

    QByteArray readBy=file.readAll();
    QJsonParseError error;
    QJsonDocument readDoc=QJsonDocument::fromJson(readBy,&error);
    QJsonArray obj=readDoc.array();

    for (int i = 0 ; i < obj.size(); i++) {
        QJsonObject faObj= obj[i].toObject();
        if (faObj["id"].toString() == output->hashMd5()) {
            return faObj["scale"].toDouble();
        }

    }
    return 0;
}

void OutputConfig::setRestore()
{
    mSetRestore = true;
}

KScreen::ModePtr OutputConfig::findBestMode(const QSize &size)
{
    KScreen::ModePtr m_mode;
    float refreshRate = 0;
    Q_FOREACH (const KScreen::ModePtr &mode, mOutput->modes()) {
        if (mode->size() == size && mode->refreshRate() > refreshRate) {
            refreshRate = mode->refreshRate();
            m_mode = mode;
        }
    }
    return m_mode;
}
