/*
 * 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 "controlpanel.h"
#include "outputconfig.h"
#include "unifiedoutputconfig.h"
#include "utils.h"

#include <QVBoxLayout>
#include <QDebug>
#include <QLabel>
#include <QDBusInterface>
#include <KF5/KScreen/kscreen/config.h>

QSize mScaleSize = QSize();

ControlPanel::ControlPanel(QWidget *parent)
    : UkccFrame(parent),
    mUnifiedOutputCfg(nullptr)
{
    this->setContainer(true);
    setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
    mLayout = new QVBoxLayout(this);
    mLayout->setMargin(0);

    isWayland();
}

ControlPanel::~ControlPanel()
{
}

void ControlPanel::setConfig(const KScreen::ConfigPtr &config)
{
    qDeleteAll(mOutputConfigs);
    mOutputConfigs.clear();
    delete mUnifiedOutputCfg;
    mUnifiedOutputCfg = nullptr;

    if (mConfig) {
        mConfig->disconnect(this);
    }

    mConfig = config;
    connect(mConfig.data(), &KScreen::Config::outputAdded,
            this, [=](const KScreen::OutputPtr &output) {
        addOutput(output, false);
    });
    connect(mConfig.data(), &KScreen::Config::outputRemoved,
            this, &ControlPanel::removeOutput);

    for (const KScreen::OutputPtr &output : mConfig->outputs()) {
        addOutput(output, false);
    }
}

void ControlPanel::addOutput(const KScreen::OutputPtr &output, bool connectChanged)
{
    if (!connectChanged) {
        connect(output.data(), &KScreen::Output::isConnectedChanged,
                    this, &ControlPanel::slotOutputConnectedChanged);
    }

    if (!output->isConnected() || (output->size() == QSize(-1, -1))) return;

    OutputConfig *outputCfg = new OutputConfig(this);
    outputCfg->setVisible(false);
    outputCfg->setShowScaleOption(mConfig->supportedFeatures().testFlag(KScreen::Config::Feature::PerOutputScaling));

    outputCfg->setOutput(output);
    connect(outputCfg, &OutputConfig::changed,
            this, &ControlPanel::changed);

    connect(outputCfg, &OutputConfig::scaleChanged,
            this, &ControlPanel::scaleChanged);

    connect(outputCfg, &OutputConfig::enabledChanged,
            this, &ControlPanel::enabledChanged);

    connect(outputCfg, &OutputConfig::toSetScreenPos,
            this, [=](){
        Q_EMIT this->toSetScreenPos(output);
    });

    mLayout->addWidget(outputCfg);

    mOutputConfigs << outputCfg;

    if (mIsWayland) {
        activateOutput(mCurrentOutput);
    }
}

void ControlPanel::removeOutput(int outputId)
{
    if (mUnifiedOutputCfg) {
        mUnifiedOutputCfg->deleteLater(); //避免内存泄露，插拔时会一直new，所以这里delete掉
        mUnifiedOutputCfg = nullptr;
    }
    for (OutputConfig *outputCfg : mOutputConfigs) {
        if (!outputCfg || !outputCfg->output()) {
            continue;
        }
        if (outputCfg->output()->id() == outputId) {
            mOutputConfigs.removeOne(outputCfg);
            outputCfg->deleteLater();
            outputCfg = nullptr;
        } else {
            if (outputCfg->output()->isConnected()) {
                outputCfg->setVisible(true);
            } else {
                outputCfg->setVisible(false);
            }
        }
    }
}

void ControlPanel::activateOutput(const KScreen::OutputPtr &output)
{
    // Ignore activateOutput when in unified mode
    //避免镜像下拔掉所有屏幕再插上导致不显示显示器内容
    if (mUnifiedOutputCfg && mUnifiedOutputCfg->isVisible()) {
        return;
    }

    mCurrentOutput = output;

    Q_FOREACH (OutputConfig *cfg, mOutputConfigs) {
        cfg->setVisible(cfg->output()->id() == output->id());
    }
}

void ControlPanel::activateOutputNoParam()
{
    // Ignore activateOutput when in unified mode
    if (mUnifiedOutputCfg) {
        return;
    }

    Q_FOREACH (OutputConfig *cfg, mOutputConfigs) {
        cfg->setVisible(cfg->output()->id() == 66);
    }
}

void ControlPanel::changescalemax(const KScreen::OutputPtr &output)
{
    QSize sizescale = QSize();
    Q_FOREACH (const KScreen::ModePtr &mode, output->modes()) {
        if (sizescale.width() <= mode->size().width()) {
            sizescale = mode->size();
        }
    }
    if (mScaleSize == QSize() || mScaleSize.width() > sizescale.width()) {
        mScaleSize = sizescale;
    }
}

void ControlPanel::isWayland()
{
    QString sessionType = getenv("XDG_SESSION_TYPE");

    if (!sessionType.compare(kSession, Qt::CaseSensitive)) {
        mIsWayland = true;
    } else {
        mIsWayland = false;
    }
}

void ControlPanel::setUnifiedOutput(const KScreen::OutputPtr &output)
{
    if (output.isNull()) {
        mUnifiedOutputCfg->deleteLater();
        mUnifiedOutputCfg = nullptr;
    } else {
        if (mUnifiedOutputCfg) {
            mUnifiedOutputCfg->deleteLater();
            mUnifiedOutputCfg = nullptr;
        }
        mUnifiedOutputCfg = new UnifiedOutputConfig(mConfig, this);
        mUnifiedOutputCfg->setOutput(output);
        mUnifiedOutputCfg->setVisible(true);
        mLayout->insertWidget(mLayout->count() - 2, mUnifiedOutputCfg);
        connect(mUnifiedOutputCfg, &UnifiedOutputConfig::changed,
                this, &ControlPanel::changed);
        connect(mUnifiedOutputCfg, &UnifiedOutputConfig::scaleChanged,
                this, &ControlPanel::scaleChanged);
    }
    Q_FOREACH (OutputConfig *config, mOutputConfigs) {
        // 隐藏下面控制
        config->setVisible(false);
    }
}

void ControlPanel::slotOutputConnectedChanged()
{
    const KScreen::OutputPtr output(qobject_cast<KScreen::Output *>(sender()), [](void *){
    });

    if (output->isConnected()) {
        addOutput(output, true);
    } else {
        removeOutput(output->id());
    }
}

void ControlPanel::setRestore()
{
    for (OutputConfig *outputCfg : mOutputConfigs) {
        if (!outputCfg || !outputCfg->output()) {
            continue;
        }
        if (outputCfg->isVisible()) {
            outputCfg->setRestore();
        }
    }
    if (mUnifiedOutputCfg && mUnifiedOutputCfg->isVisible()) {
        mUnifiedOutputCfg->setRestore();
    }
}
