/*
    Copyright (C) 2012  Dan Vratil <dvratil@redhat.com>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library 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
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include "qmlscreen.h"
#include "qmloutputcomponent.h"
#include "qmloutput.h"

#include <KF5/KScreen/kscreen/output.h>
#include <KF5/KScreen/kscreen/config.h>

#include <QTimer>
#include <sys/socket.h>
#include <QtMath>

Q_DECLARE_METATYPE(KScreen::OutputPtr)

QMLScreen::QMLScreen(QQuickItem *parent) :
    QQuickItem(parent)
{
    connect(this, &QMLScreen::widthChanged, this, &QMLScreen::viewSizeChanged);
    connect(this, &QMLScreen::heightChanged, this, &QMLScreen::viewSizeChanged);
}

QMLScreen::~QMLScreen()
{
    qDeleteAll(m_outputMap);
    m_outputMap.clear();
}

KScreen::ConfigPtr QMLScreen::config() const
{
    return m_config;
}

void QMLScreen::setConfig(const KScreen::ConfigPtr &config)
{
    qDeleteAll(m_outputMap);
    m_outputMap.clear();
    m_manuallyMovedOutputs.clear();
    m_connectedOutputsCount = 0;
    m_enabledOutputsCount = 0;

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

    m_config = config;
    connect(m_config.data(), &KScreen::Config::outputAdded, this, [this](const KScreen::OutputPtr &output) {
        m_pendingOutputs.append(output);
        QTimer::singleShot(1000, this, [=] {
            if (!m_pendingOutputs.contains(output)) {
                return;
            }
            m_pendingOutputs.removeAll(output);
            addOutput(output);
            m_isPosChanged = true;
            updateOutputsPlacement();
            m_isPosChanged = false;
            setScreenPos(primaryOutput(), false);
        });
    });
    connect(m_config.data(), &KScreen::Config::outputRemoved, this, [this](int outputId) {
        QList<KScreen::OutputPtr> tmpPendingOutputs = m_pendingOutputs;
        for (KScreen::OutputPtr output : tmpPendingOutputs) {
            if (output->id() == outputId) {
                m_pendingOutputs.removeAll(output);
            }
        }
        removeOutput(outputId);
    });

    for (const KScreen::OutputPtr &output : m_config->outputs()) {
        addOutput(output);
    }
    m_isPosChanged = true;
    updateOutputsPlacement();
    m_isPosChanged = false;
}

void QMLScreen::addOutput(const KScreen::OutputPtr &output)
{
    QMLOutputComponent comp(qmlEngine(this), this);

    QMLOutput *qmloutput = comp.createForOutput(output);
    if (!qmloutput) {
        qWarning() << "Failed to create QMLOutput";
        return;
    }
    qmloutput->setObjectName("ukui-control-center_display_output_" + output->name());
    m_outputMap.insert(output, qmloutput);

    qmloutput->setParentItem(this);
    qmloutput->setZ(m_outputMap.count());

    connect(output.data(), &KScreen::Output::isConnectedChanged,
            this, &QMLScreen::outputConnectedChanged);
    connect(output.data(), &KScreen::Output::isEnabledChanged,
            this, &QMLScreen::outputEnabledChanged);
    connect(output.data(), &KScreen::Output::posChanged,
            this, &QMLScreen::outputPositionChanged);
    connect(qmloutput, &QMLOutput::yChanged,
            [this, qmloutput]() {
        qmlOutputMoved(qmloutput);
    });
    connect(qmloutput, &QMLOutput::xChanged,
            [this, qmloutput]() {
        qmlOutputMoved(qmloutput);
    });

    // 在这里点击上面小屏幕
    connect(qmloutput, SIGNAL(clicked()),
            this, SLOT(setActiveOutput()));

    connect(qmloutput, SIGNAL(clicked()),
                this, SLOT(getClickedPos()));

    connect(qmloutput, SIGNAL(mouseReleased(bool)),
            this, SLOT(setScreenPos(bool)));

    connect(qmloutput, SIGNAL(rotationChanged(bool)),
            this, SLOT(setScreenPos(bool)));

    connect(qmloutput, SIGNAL(widthChanged(bool)),
            this, SLOT(setScreenPos(bool)));

    connect(qmloutput, SIGNAL(heightChanged(bool)),
            this, SLOT(setScreenPos(bool)));

    connect(qmloutput, &QMLOutput::visibleChanged, this, [=]() {
        visibleChangedSlot();
    });

    qmloutput->updateRootProperties();
    if (isCloneMode()) {
        qmloutput->setVisible(false);
        qDebug() << Q_FUNC_INFO << "clone mode set output invisble" << qmloutput->output()->name();
    }
    viewSizeChanged();
}

void QMLScreen::visibleChangedSlot() {
    qDebug() << "output visibleChanged";
    m_isPosChanged = true;
    updateOutputsPlacement();
    m_isPosChanged = false;

    viewSizeChanged();
}

void QMLScreen::removeOutput(int outputId)
{
    viewSizeChanged();
    for (const KScreen::OutputPtr &output : m_outputMap.keys()) {
        if (output->id() == outputId) {
            QMLOutput *qmlOutput = m_outputMap.take(output);
            qmlOutput->setParentItem(nullptr);
            qmlOutput->setParent(nullptr);
            // TODO:bug51346
            // qmlOutput->deleteLater();
            return;
        }
    }
}

int QMLScreen::connectedOutputsCount() const
{
    return m_connectedOutputsCount;
}

int QMLScreen::enabledOutputsCount() const
{
    return m_enabledOutputsCount;
}

QMLOutput *QMLScreen::primaryOutput() const
{
    Q_FOREACH (QMLOutput *qmlOutput, m_outputMap) {
        if (qmlOutput->output()->isPrimary()) {
            return qmlOutput;
        }
    }

    return nullptr;
}

QList<QMLOutput *> QMLScreen::outputs() const
{
    return m_outputMap.values();
}

void QMLScreen::getClickedPos()
{
    leftPosList.clear();
    // 应该判断所有显示器的坐标是否变化，移动A屏幕可能只会改变A与B屏幕的相对位置，A本身的屏幕位置可能不变(比如(0, 1680))
    Q_FOREACH (QMLOutput *qmlOutput, m_outputMap) {
        if (qmlOutput->output()->isConnected()) {
            qmlOutput->setIsClicked(true);
            qDebug() << Q_FUNC_INFO << " setclick " << qmlOutput->output()->name() << qmlOutput->isClicked();
            leftPosList.append(qmlOutput->position().x());
            leftPosList.append(qmlOutput->position().y());
        }
    }
}

void QMLScreen::setActiveOutput(QMLOutput *output)
{
    Q_FOREACH (QMLOutput *qmlOutput, m_outputMap) {
        if (qmlOutput->z() > output->z()) {
            qmlOutput->setZ(qmlOutput->z() - 1);
        }
    }

    output->setZ(m_outputMap.count());
    // 中屏幕
    output->setFocus(true);
    Q_EMIT focusedOutputChanged(output);
}

void QMLScreen::setScreenCenterPos()
{
    // 组成最大矩形四个边的位置，分别对应左上(1)，右下(2)的xy坐标值
    qreal localX1 = -1, localX2 = -1, localY1 = -1, localY2 = -1;
    qreal mX1 = 0, mY1 = 0, mX2 = 0, mY2 = 0; // 矩形中点坐标
    qreal moveX = 0, moveY = 0;// 移动的值
    bool firstFlag = true;
    Q_FOREACH (QMLOutput *qmlOutput, m_outputMap) {
        if (qmlOutput->output()->isConnected() && qmlOutput->isVisible()) {
            if (firstFlag == true || localX1 > qmlOutput->x()) {
                localX1 = qmlOutput->x();
            }
            if (firstFlag == true || localX2 < qmlOutput->x() + qmlOutput->width()) {
                localX2 = qmlOutput->x() + qmlOutput->width();
            }
            if (firstFlag == true || localY1 > qmlOutput->y()) {
                localY1 = qmlOutput->y();
            }
            if (firstFlag == true || localY2 < qmlOutput->y() + qmlOutput->height()) {
                localY2 = qmlOutput->y() + qmlOutput->height();
            }
            firstFlag = false;
        }
    }

    mX1 = localX1 + (localX2-localX1)/2;
    mY1 = localY1 + (localY2-localY1)/2;

    mX2 = width()/2;
    mY2 = height()/2;

    moveX = mX2 - mX1;
    moveY = mY2 - mY1;

    Q_FOREACH (QMLOutput *qmlOutput, m_outputMap) {
          if (qmlOutput->output()->isConnected() && qmlOutput->isVisible()) {
            qDebug() << Q_FUNC_INFO << " before set position " << qmlOutput->output()->name() << qmlOutput->position() << qmlOutput->size();
            qmlOutput->blockSignals(true);
            qmlOutput->setX(qmlOutput->x() + moveX);
            qmlOutput->setY(qmlOutput->y() + moveY);
            qmlOutput->blockSignals(false);
            qDebug() << Q_FUNC_INFO << " after set position " << qmlOutput->output()->name() << qmlOutput->position() << qmlOutput->size();
        }
    }
}

bool QMLScreen::isUpDownOutput(QMLOutput *curOutput, QMLOutput *preOutput) {
    if (curOutput == nullptr || preOutput == nullptr) {
        return false;
    }
    float width = (std::max(curOutput->x() + curOutput->width(), preOutput->x() + preOutput->width()) -
                   std::min(curOutput->x(), preOutput->x()) -
                   fabs(curOutput->x() - preOutput->x()) -
                   fabs((curOutput->x() + curOutput->width()) - (preOutput->x() + preOutput->width())));
    float height = (std::max(curOutput->y() + curOutput->height(), preOutput->y() + preOutput->height()) -
                    std::min(curOutput->y(), preOutput->y()) -
                    fabs(curOutput->y() - preOutput->y()) -
                    fabs((curOutput->y() + curOutput->height()) - (preOutput->y() + preOutput->height())));
    qDebug() << Q_FUNC_INFO << curOutput->output()->name() << preOutput->output()->name() << "width:" << width << " height:" << height;
    if (width > height) {
        return true;
    }
    return false;
}

void QMLScreen::setScreenPos(QMLOutput *output, bool isReleased)
{
    qDebug() << Q_FUNC_INFO << "setScreenPos isRelease" << isReleased;
    // 镜像模式下跳过屏幕旋转处理
    if (!output || output->isCloneMode()) {
        qDebug() << Q_FUNC_INFO << "isCloneMode setScreenCenterPos";
        setScreenCenterPos();
        return;
    }
    output->setSize(QSizeF(output->property("saveWidth").toDouble(), output->property("saveHeight").toDouble()));
    float x1 = 0, y1 = 0;
    float width1 = 0, height1 = 0;
    float x2 = 0, y2 = 0;
    float width2 = 0, height2 = 0;

    x1 = output->x();
    y1 = output->y();
    width1 = output->width();
    height1 = output->height();

    int connectedScreen = 0;

    QMLOutput *other = NULL;
    Q_FOREACH (QMLOutput *qmlOutput, m_outputMap) {
        if (qmlOutput->output()->isConnected()) {
            qDebug() << Q_FUNC_INFO << " start " << qmlOutput->output()->name() << qmlOutput->position() << qmlOutput->size() << qmlOutput->outputX() << qmlOutput->outputY() << UtilsOfDisplay::sizeToString(qmlOutput->output()->size());
            connectedScreen++;
        }
        if (qmlOutput != output && qmlOutput->output()->isConnected()) {
            qmlOutput->setSize(QSizeF(qmlOutput->property("saveWidth").toDouble(), qmlOutput->property("saveHeight").toDouble()));
            other = qmlOutput;
            x2 = other->x();
            y2 = other->y();
            width2 = other->width();
            height2 = other->height();
        }
    }

    // 坐标为负的情况，bug#76350
    if (x1 < 0 || y1 < 0 || x2 < 0 || y2 < 0) {
        if (isReleased)
            setScreenCenterPos();
        return;
    }

    if (connectedScreen < 2) {
        setScreenCenterPos();
        return;
    }

    QVector<QMLOutput *> qmlOutputVec;
    qmlOutputVec.reserve(m_outputMap.size());
    Q_FOREACH (QMLOutput *qmlOutput, m_outputMap) {
        if (!qmlOutput->output()->isConnected()) {
            continue;
        }
        qmlOutputVec.append(qmlOutput);
    }
    // x point sort
    std::sort(qmlOutputVec.begin(), qmlOutputVec.end(),
              [&qmlOutputVec](const QMLOutput* lOutput, const QMLOutput* rOutput){
        if (lOutput->x() != rOutput->x()) {
            return lOutput->x() < rOutput->x();
        }
        if (lOutput->width() != rOutput->width()) {
            return lOutput->width() < rOutput->width();
        }
        if (lOutput->y() != rOutput->y()) {
            return lOutput->y() < rOutput->y();
        }
        if (lOutput->height() != rOutput->height()) {
            return lOutput->height() < rOutput->height();
        }
        return false;
    });

    bool outputVecSizeBeyondTwo = (qmlOutputVec.size() > 2);
    if (outputVecSizeBeyondTwo) {
        qmlOutputVec.append(qmlOutputVec.at(0)); // convert list to a circle
    }

    QMLOutput* preQmlOutput = nullptr;
    Q_FOREACH (QMLOutput *qmlOutput, qmlOutputVec) {
        if (!qmlOutput->output()->isConnected()) {
            continue;
        }
        if (preQmlOutput == nullptr) {
            preQmlOutput = qmlOutput;
            continue;
        }

        bool swapOutputFlag = false;
        int outputVecLastIndex = (qmlOutputVec.size() - 1);
        if (outputVecSizeBeyondTwo && (qmlOutput == qmlOutputVec.at(outputVecLastIndex))) {
            QMLOutput* tmpOutput = qmlOutput;
            qmlOutput = preQmlOutput;
            preQmlOutput = tmpOutput;
            swapOutputFlag = true;
        }

        if (!swapOutputFlag) {
            if (qmlOutput->x() + qmlOutput->width() < preQmlOutput->x()) {
                qmlOutput->setX(preQmlOutput->x() - qmlOutput->width());
            }
            if (qmlOutput->y() > preQmlOutput->y() + preQmlOutput->height()) {
                qmlOutput->setY(preQmlOutput->y() + preQmlOutput->height());
            }
            if (qmlOutput->x() > preQmlOutput->x() + preQmlOutput->width()) {
                qmlOutput->setX(preQmlOutput->x() + preQmlOutput->width());
            }
            if (qmlOutput->y() + qmlOutput->height() < preQmlOutput->y()) {
                qmlOutput->setY(preQmlOutput->y() - qmlOutput->height());
            }
        }
        // 矩形是否相交
        float maxX = float(std::max(qmlOutput->x(), preQmlOutput->x()));
        float minX = float(std::min(qmlOutput->x() + qmlOutput->width(), preQmlOutput->x() + preQmlOutput->width()));
        float maxY = float(std::max(qmlOutput->y(), preQmlOutput->y()));
        float minY = float(std::min(qmlOutput->y() + qmlOutput->height(), preQmlOutput->y() + preQmlOutput->height()));
        qDebug() << Q_FUNC_INFO << " before set position " << qmlOutput->output()->name() << qmlOutput->position() << qmlOutput->size();
        qDebug() << Q_FUNC_INFO << " before set position " << preQmlOutput->output()->name() << preQmlOutput->position() << preQmlOutput->size();
        float newMaxX = QString::number(maxX, 'f', 3).toFloat();
        float newMinX = QString::number(minX, 'f', 3).toFloat();
        float newMaxY = QString::number(maxY, 'f', 3).toFloat();
        float newMinY = QString::number(minY, 'f', 3).toFloat();
        qDebug() << Q_FUNC_INFO << " maxX:" << maxX << " minX:" << minX << " maxY:" << maxY << " minY:" << minY;
        qDebug() << Q_FUNC_INFO << " newMaxX:" << newMaxX << " newMinX:" << newMinX << " newMaxY:" << newMaxY << " newMinY:" << newMinY;
        if (newMaxX < newMinX && newMaxY < newMinY) {
            if (swapOutputFlag) {
                qmlOutput->setX(preQmlOutput->x() + preQmlOutput->width());
            } else {
                if (qmlOutput->output()->name() == output->output()->name() ||
                        preQmlOutput->output()->name() == output->output()->name()) {
                    if (isUpDownOutput(qmlOutput, preQmlOutput)) {
                        // up
                        if ((qmlOutput->y() + height() > preQmlOutput->y()) &&
                                (qmlOutput->y() < preQmlOutput->y())) {
                            qmlOutput->setY(preQmlOutput->y() - qmlOutput->height());
                            preQmlOutput = qmlOutput;
                            continue;
                        // down
                        } else if ((qmlOutput->y() < preQmlOutput->y()  + preQmlOutput->height()) &&
                                   (qmlOutput->y() + qmlOutput->height() > preQmlOutput->y() + preQmlOutput->height())) {
                            qmlOutput->setY(preQmlOutput->y() + preQmlOutput->height());
                            preQmlOutput = qmlOutput;
                            continue;
                        }
                    }
                }
                // left
                if ((qmlOutput->x() + qmlOutput->width() > preQmlOutput->x()) && (qmlOutput->x() < preQmlOutput->x())) {
                    qmlOutput->setX(preQmlOutput->x() - qmlOutput->width());
                // contains
                } else if ((qmlOutput->x() < preQmlOutput->x() + preQmlOutput->width()) &&
                           (qmlOutput->x() + qmlOutput->width() > preQmlOutput->x() + preQmlOutput->width())) {
                    qmlOutput->setX(preQmlOutput->x() + preQmlOutput->width());
                // right
                } else if ((qmlOutput->x() + qmlOutput->width() > preQmlOutput->x()) && (qmlOutput->x() > preQmlOutput->x())) {
                    qmlOutput->setX(preQmlOutput->x() + preQmlOutput->width());
                // up
                } else if ((qmlOutput->y() + height() > preQmlOutput->y()) &&
                    (qmlOutput->y() < preQmlOutput->y())) {
                    qmlOutput->setY(preQmlOutput->y() - qmlOutput->height());
                // down
                } else if ((qmlOutput->y() < preQmlOutput->y()  + preQmlOutput->height()) &&
                           (qmlOutput->y() + qmlOutput->height() > preQmlOutput->y() + preQmlOutput->height())) {
                    qmlOutput->setY(preQmlOutput->y() + preQmlOutput->height());
                }
            }
        }
        preQmlOutput = qmlOutput;
    }

    if (outputVecSizeBeyondTwo) {
        qmlOutputVec.erase(qmlOutputVec.end() - 1); // erase repeat output
    }

    QMLOutput* nextQmlOutput = nullptr;
    int curOutputIdx = 0;
    int interval = 2;
    Q_FOREACH (QMLOutput *qmlOutput, qmlOutputVec) {
        if (!qmlOutput->output()->isConnected()) {
            continue;
        }
        qDebug() << Q_FUNC_INFO << " after set position" << qmlOutput->output()->name() << qmlOutput->position() << qmlOutput->size();
        int nextOutputIdx = curOutputIdx + interval;
        curOutputIdx++;
        if (nextOutputIdx > qmlOutputVec.size() - 1) {
            continue;
        }
        nextQmlOutput = qmlOutputVec.at(nextOutputIdx);
        // compare with interval output
        if ((std::max(qmlOutput->x(), nextQmlOutput->x()) < std::min(qmlOutput->x() + qmlOutput->width(), nextQmlOutput->x() + nextQmlOutput->width())) &&
                std::max(qmlOutput->y(), nextQmlOutput->y()) < std::min(qmlOutput->y() + qmlOutput->height(), nextQmlOutput->y() + nextQmlOutput->height())) {
            qmlOutput->setY(nextQmlOutput->y() + nextQmlOutput->height());
        }
    }

    setScreenCenterPos();
    rightPosList.clear();
    Q_FOREACH (QMLOutput *qmlOutput, m_outputMap) {
        if (qmlOutput->output()->isConnected()) {
            rightPosList.append(qmlOutput->position().x());
            rightPosList.append(qmlOutput->position().y());
        }
    }
    if (isReleased) {
        if (leftPosList.size() != rightPosList.size()) {
            Q_EMIT released();
        } else {
            for (int i = 0; i < leftPosList.size(); ++i) {
                if (qFabs(leftPosList.at(i) - rightPosList.at(i)) > 0.001) {
                    Q_EMIT released();
                    break;
                }
            }
        }
    }
}

void QMLScreen::setActiveOutputByCombox(int screenId)
{
    QHash<KScreen::OutputPtr, QMLOutput *>::const_iterator it = m_outputMap.constBegin();
    while (it != m_outputMap.constEnd()) {
        if (screenId == it.key()->id()) {
            setActiveOutput(it.value());
            return;
        }
        it++;
    }
}

QSize QMLScreen::maxScreenSize() const
{
    return m_config->screen()->maxSize();
}

float QMLScreen::outputScale() const
{
    return m_outputScale;
}

void QMLScreen::outputConnectedChanged()
{
    int connectedCount = 0;

    Q_FOREACH (const KScreen::OutputPtr &output, m_outputMap.keys()) {
        if (output->isConnected()) {
            ++connectedCount;
        }
    }

    if (connectedCount != m_connectedOutputsCount) {
        m_connectedOutputsCount = connectedCount;
        Q_EMIT connectedOutputsCountChanged();
        // bug#180957
        //updateOutputsPlacement();
    }
}

void QMLScreen::outputEnabledChanged()
{
    const KScreen::OutputPtr output(qobject_cast<KScreen::Output *>(sender()), [](void *){
        });
    /*if (output->isEnabled()) {   // bug#206036
        // bug#68442
        QTimer::singleShot(500, this,[=]{
            updateOutputsPlacement();
        });
    }*/
    int enabledCount = 0;

    Q_FOREACH (const KScreen::OutputPtr &output, m_outputMap.keys()) {
        if (output->isEnabled()) {
            ++enabledCount;
        }
    }

    if (enabledCount == m_enabledOutputsCount) {
        m_enabledOutputsCount = enabledCount;
        Q_EMIT enabledOutputsCountChanged();
    }
}

void QMLScreen::setCloseOutput(bool status) {
    m_closeOutput = status;
    qDebug() << Q_FUNC_INFO << "set close output" << m_closeOutput;
}

void QMLScreen::setExtendMode(bool status) {
    m_extendMode = status;
    qDebug() << Q_FUNC_INFO << "set extend mode" << m_extendMode;
}

void QMLScreen::setIsPosChanged(bool status) {
    m_isPosChanged = status;
    qDebug() << Q_FUNC_INFO << "set is pos changed" << m_isPosChanged;
}

void QMLScreen::outputPositionChanged()
{
    KScreen::Output *output = qobject_cast<KScreen::Output *>(sender());
    if (output == nullptr) {
         qDebug() << Q_FUNC_INFO << " output is null";
         return;
    }
    static const int OUTPUT_POS_CHANGE_COUNT = 2;
    if (m_closeOutput) {
        qDebug() << Q_FUNC_INFO << "close output no need change output position" << output->name();
        ++m_closeOutputCount;
        if (m_closeOutputCount == OUTPUT_POS_CHANGE_COUNT) {
            m_closeOutputCount = 0;
            m_closeOutput = false;
        }
        return;
    }
    bool isClicked = true;
    bool resetClicked = false;
    for (QMLOutput *mOutput: outputs()) {
        if (mOutput->output()->name() == output->name()) {
            isClicked = mOutput->isClicked();
            if (!isClicked) {
                break;
            }
            ++m_outputPosChangedCount;
            if (m_outputPosChangedCount == OUTPUT_POS_CHANGE_COUNT) {
                m_outputPosChangedCount = 0;
                resetClicked = true;
            }
            qDebug() << Q_FUNC_INFO << mOutput->output()->name() << " isClicked " << mOutput->isClicked() << m_outputPosChangedCount;
            break;
        }
    }
    if (resetClicked) {
        for (QMLOutput *mOutput: outputs()) {
            if (mOutput->output()->isConnected()) {
                mOutput->setIsClicked(false);
                qDebug() << Q_FUNC_INFO << mOutput->output()->name() << " resetClicked " << mOutput->isClicked();
            }
        }
    }
    if (isClicked) {
        qDebug() << Q_FUNC_INFO << " output isClicked, skip";
        return;
    }

    m_isPosChanged = true;
    qDebug() << Q_FUNC_INFO << isClicked << resetClicked;
    updateOutputsPlacement(isClicked);
    m_isPosChanged = false;
}

void QMLScreen::qmlOutputMoved(QMLOutput *qmlOutput)
{
    qDebug() << Q_FUNC_INFO << "qmlOutputMoved output" << qmlOutput->output()->name() << qmlOutput->size();
    if (qmlOutput->isCloneMode()) {
        qDebug() << Q_FUNC_INFO << "output is cloneMode, return" << qmlOutput->output()->name();
        return;
    }

    if (qFabs(qmlOutput->property("saveWidth").toDouble() - qmlOutput->width()) > 0.001 || qFabs(qmlOutput->property("saveHeight").toDouble() - qmlOutput->height()) > 0.001) {
        qDebug() << Q_FUNC_INFO << "output no need change, return" << qmlOutput->output()->name();
        return;
    }

    if (!m_manuallyMovedOutputs.contains(qmlOutput)) {
        m_manuallyMovedOutputs.append(qmlOutput);
        qDebug() << Q_FUNC_INFO << "qmlOutputMoved m_manuallyMovedOutputs add" << qmlOutput->output()->name() << qmlOutput->position() << qmlOutput->size() << m_manuallyMovedOutputs.size();
    }

    updateCornerOutputs();

    Q_FOREACH (QMLOutput *m_qmlOutput, m_outputMap) {
        if (!m_qmlOutput->output()->isConnected() || !m_qmlOutput->output()->isEnabled()) {
            continue;
        }
        qDebug() << Q_FUNC_INFO << "output info" << m_qmlOutput->output()->name() << qmlOutput->position() << qmlOutput->size() << UtilsOfDisplay::sizeToString(qmlOutput->output()->size());
        int x = qRound((m_qmlOutput->x() - leftX) / outputScale());
        int y = qRound((m_qmlOutput->y() - topY) / outputScale());
        m_qmlOutput->outputPtr()->blockSignals(true);
        qDebug() << Q_FUNC_INFO << "before setOutputX setOutputY" << m_qmlOutput->output()->name() << m_qmlOutput->outputX() << m_qmlOutput->outputY();
        m_qmlOutput->setOutputX(x);
        m_qmlOutput->setOutputY(y);
        qDebug() << Q_FUNC_INFO << "after setOutputX setOutputY" << m_qmlOutput->output()->name() << m_qmlOutput->outputX() << m_qmlOutput->outputY();
        m_qmlOutput->outputPtr()->blockSignals(false);
    }
}

void QMLScreen::viewSizeChanged()
{
    //TO fix bug#85240
//    updateOutputsPlacement();
    QTimer::singleShot(0,this,[=]{
        setScreenCenterPos();
    });
}

void QMLScreen::updateCornerOutputs()
{
    leftX = -1.0;
    topY = -1.0;
    Q_FOREACH (QMLOutput *output, m_outputMap) {
        if (!output->output()->isConnected() || !output->output()->isEnabled()) {
            continue;
        }

        if (leftX < 0 || leftX > output->x()) {
            leftX = output->x();
        }

        if (topY < 0 || topY > output->y()) {
            topY = output->y();
        }
        qDebug() << Q_FUNC_INFO << "updateCornerOutputs leftX" << leftX << "topY" << topY << "output" << output->output()->name() << output->position();
    }
}

void QMLScreen::setOutputScale(float scale)
{
    if (qFuzzyCompare(scale, m_outputScale))
        return;
    m_outputScale = scale;
    emit outputScaleChanged();
}

bool QMLScreen::isCloneMode()
{
    KScreen::OutputPtr output = m_config->primaryOutput();
    if (!output) {
        return false;
    }
    if (m_config->connectedOutputs().count() >= 2) {
        foreach (KScreen::OutputPtr secOutput, m_config->connectedOutputs()) {
            //不能用size为(-1,-1)进行判断，刚插拔时分辨率可能会是(-1,-1)
            if (secOutput->pos() != output->pos() || !secOutput->isEnabled()) {
                return false;
            }
        }
    } else {
        return false;
    }
    return true;
}

// 画坐标
void QMLScreen::updateOutputsPlacement(bool isClicked)
{
    qDebug() << Q_FUNC_INFO << isCloneMode() << isClicked;
    if (width() <= 0 || (!m_extendMode && isCloneMode()))
        return;

    QSizeF initialActiveScreenSize;

    Q_FOREACH (QQuickItem *item, childItems()) {
        QMLOutput *qmlOutput = qobject_cast<QMLOutput *>(item);
        if (!qmlOutput->output()->isConnected() || !qmlOutput->output()->isEnabled()) {
            continue;
        }

        if (qmlOutput->outputX() + qmlOutput->currentOutputWidth()
            > initialActiveScreenSize.width()) {
            initialActiveScreenSize.setWidth(qmlOutput->outputX()
                                             + qmlOutput->currentOutputWidth());
        }
        if (qmlOutput->outputY() + qmlOutput->currentOutputHeight()
            > initialActiveScreenSize.height()) {
            initialActiveScreenSize.setHeight(
                qmlOutput->outputY() + qmlOutput->currentOutputHeight());
        }
    }

    auto initialScale = outputScale();

    auto scale = initialScale;
    qreal lastX = -1.0;
    do {
        auto activeScreenSize = initialActiveScreenSize * scale;

        const QPointF offset((width() - activeScreenSize.width()) / 2.0,
                             (height() - activeScreenSize.height()) / 2.0);

        lastX = -1.0;
        qreal lastY = -1.0;
        Q_FOREACH (QQuickItem *item, childItems()) {
            QMLOutput *qmlOutput = qobject_cast<QMLOutput *>(item);
            if (!qmlOutput->output()->isConnected()
                    || (isClicked && m_manuallyMovedOutputs.contains(qmlOutput))) {
                qDebug() << Q_FUNC_INFO << " continue " << qmlOutput->output()->name() << qmlOutput->position() << !qmlOutput->output()->isConnected()
                         << !qmlOutput->output()->isEnabled() << m_manuallyMovedOutputs.contains(qmlOutput) << isClicked;
                continue;
            }

            qmlOutput->blockSignals(true);
            qmlOutput->setPosition(QPointF(offset.x() + (qmlOutput->outputX() * scale),
                                           offset.y() + (qmlOutput->outputY() * scale)));
            qDebug() << Q_FUNC_INFO << " set position" << qmlOutput->output()->name() << qmlOutput->position() << qmlOutput->size()
                     << isClicked << m_manuallyMovedOutputs.contains(qmlOutput);
            lastX = qMax(lastX,
                         qmlOutput->position().x() + qmlOutput->width() / initialScale * scale);
            lastY = qMax(lastY, qmlOutput->position().y());
            qmlOutput->blockSignals(false);
        }
        qDebug() << Q_FUNC_INFO << "isposChanged:" << m_isPosChanged << "m_manuallyMovedOutputs.size:" << m_manuallyMovedOutputs.size();
        if (!m_isPosChanged) {
            Q_FOREACH (QQuickItem *item, childItems()) {
                QMLOutput *qmlOutput = qobject_cast<QMLOutput *>(item);
                if (qmlOutput->output()->isConnected()
                    && !m_manuallyMovedOutputs.contains(qmlOutput)) {
                    qmlOutput->blockSignals(true);
                    qDebug() << Q_FUNC_INFO << "not in manuallyMovedOutputs, before set position" << qmlOutput->output()->name() << qmlOutput->position() << qmlOutput->size();
                    qmlOutput->setPosition(QPointF(lastX, lastY));
                    qDebug() << Q_FUNC_INFO << "not in manuallyMovedOutputs, after set position" << qmlOutput->output()->name() << qmlOutput->position() << qmlOutput->size();
                    lastX += qmlOutput->width() / initialScale * scale;
                    qmlOutput->blockSignals(false);
                }
            }
        }
        // calculate the scale dynamically, so all screens fit to the dialog
        if (lastX > width()) {
            scale *= 0.8;
        }
    } while (lastX > width());

    // Use a timer to avoid binding loop on width()
    QTimer::singleShot(0, this, [scale, this] {
        setOutputScale(scale);
    });
}
