/* -*- Mode: C++; indent-tabs-mode: nil; tab-width: 4 -*-
 * -*- coding: utf-8 -*-
 *
 * 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 of the License, or
 * 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 "gamma-manager-thread.h"

GmWorkThread::GmWorkThread(QObject *parent)
{
    m_rtTemperature = COLOR_TEMPERATURE_DEFAULT;
    m_lastTemperature = COLOR_TEMPERATURE_DEFAULT;
    m_targetTemp = COLOR_TEMPERATURE_DEFAULT;
    m_pSetTimer = new QTimer(this);
    m_pGmHelper = new GmHelper(this);
    m_pGmHelper->initOutput();
#if 0
    m_pSetTimer->setSingleShot(false);
    m_pSetTimer->setTimerType(Qt::PreciseTimer);
    connect(m_pSetTimer, SIGNAL(timeout()), this, SLOT(doSetTimer()), Qt::QueuedConnection);
#endif

    QDBusConnection::sessionBus().connect(QString(),
                                          QString(DBUS_XRANDR_PATH),
                                          DBUS_XRANDR_INTERFACE,
                                          "screenAdded", this, SLOT(doAddedScreen(QString)));

    QDBusConnection::sessionBus().connect(QString(),
                                          QString(DBUS_XRANDR_PATH),
                                          DBUS_XRANDR_INTERFACE,
                                          "screenRemoved", this, SLOT(doRemovedScreen(QString)));

    QDBusConnection::sessionBus().connect(QString(),
                                          QString(DBUS_XRANDR_PATH),
                                          DBUS_XRANDR_INTERFACE,
                                          "screenStateChanged", this, SLOT(doScreenStateChanged(QString,int)));
}

GmWorkThread::~GmWorkThread()
{
    if (m_pSetTimer) {
        delete m_pSetTimer;
        m_pSetTimer = nullptr;
    }
}

void GmWorkThread::setAllOutputsBrightness(const double brightness)
{
    m_pGmHelper->setAllOutputsBrightness(brightness);
    USD_LOG(LOG_DEBUG,"update brightness...:%d",brightness);
}

void GmWorkThread::setBrightness(const QString outputName, const double brightness)
{
    m_pGmHelper->setBrightness(outputName, brightness);
}

bool GmWorkThread::setPrimaryBrightness(const double brightness)
{
    int ret = false;
    ret = m_pGmHelper->setPrimaryBrightness(brightness);
    if (ret) {
        this->run();
    }

    return ret;
}

int GmWorkThread::getPrimaryBrightness()
{
    return m_pGmHelper->getPrimaryBrightness();
}

void GmWorkThread::setTemperature(const int temperature)
{
    m_targetTemp = temperature;
    USD_LOG(LOG_DEBUG, "update setTemperature...:%d", m_targetTemp);
}

int GmWorkThread::getTemperature()
{
    return m_targetTemp;
}

void GmWorkThread::stopWork()
{
    isExit = true;
}

OutputGammaInfoList GmWorkThread::getAllOutputGammaInfo()
{
   return m_pGmHelper->getAllOutputsInfo();
}

void GmWorkThread::run()
{
    double frac;
    int errorTimes = 0;
    int brightSetCount = 0;//已设置背光的显示器（含无需设置），其最终值需要与显示器总数相等
    int tempSetCount = 0;
    static int runTimes = 0;
    runTimes = 0;
    QList<OutputInfo>& outputList = m_pGmHelper->getOutputInfo();

    for (int var = 0; var < outputList.count(); var++) {
        if (outputList[var].rtBrightness == outputList[var].targetBrightness) {
            brightSetCount++;
        }
    }

    do {
        errorTimes = 0;
        tempSetCount = 0;
        if (isExit){
            break;
        }
        QTime timerStart = QTime::currentTime();
        frac = (runTimes * 50.f) / (1000.f*USD_NIGHT_LIGHT_SMOOTH_SMEAR);//调整值从大逐步变小以达到渐变效果。
        if (qAbs(m_rtTemperature - m_targetTemp) < 10.f) {
            m_rtTemperature = m_targetTemp;
        } else {
            m_rtTemperature = (m_targetTemp - m_lastTemperature) * frac + m_lastTemperature;
        }

        if (m_rtTemperature < 0) {
            m_rtTemperature = m_targetTemp;
        }

        for (int var = 0; var < outputList.count(); var++) {
            if (qAbs(outputList[var].rtBrightness - outputList[var].targetBrightness) < 3 && outputList[var].rtBrightness != outputList[var].targetBrightness) {
                outputList[var].rtBrightness = outputList[var].targetBrightness;
                outputList[var].lastBrightness = outputList[var].rtBrightness;
                brightSetCount++;
                screenBrightnessChangedSignal(outputList[var].name, outputList[var].rtBrightness);
            } else if (outputList[var].rtBrightness != outputList[var].targetBrightness){
                outputList[var].rtBrightness = (outputList[var].targetBrightness - outputList[var].lastBrightness) * (frac*2) + outputList[var].lastBrightness;
                outputList[var].lastBrightness = outputList[var].rtBrightness;
//                USD_LOG(LOG_DEBUG,"%s ---> %0.4f ---> %0.4f",outputList[var].name.toLatin1().data(), outputList[var].rtBrightness, outputList[var].targetBrightness);
                screenBrightnessChangedSignal(outputList[var].name, outputList[var].rtBrightness);
            }

            if(outputList[var].rtTemp == m_targetTemp || outputList[var].connectState != RR_Connected) {
                tempSetCount++;
            }
        }

        do{
             msleep(10);
             errorTimes++;
        } while (!m_pGmHelper->setGammaWithTemp(m_rtTemperature) && errorTimes < 30);
        m_lastTemperature = m_rtTemperature;
        runTimes++;
    } while (m_targetTemp != m_rtTemperature || brightSetCount != outputList.count() || tempSetCount != outputList.count());

    for (int var = 0; var < outputList.count(); var++) {
        if (outputList[var].isPrimary) {
            m_pGmHelper->sendPrimaryChanged(outputList[var].targetBrightness,
                                            DBUS_GC_BRIGHTNESS_SIGNAL_PRIMARYCHANGED_END);

            break;
        }
    }
}

void GmWorkThread::doAddedScreen(QString outputName)
{
    USD_LOG(LOG_DEBUG,"output:%s added", outputName.toLatin1().data());
}

void GmWorkThread::doRemovedScreen(QString outputName)
{
    USD_LOG(LOG_DEBUG,"output:%s removed", outputName.toLatin1().data());
}


/**
 * @brief GmWorkThread::doScreenStateChanged
 * @param outputName
 * @param state
 *
 */
void GmWorkThread::doScreenStateChanged(QString outputName, int state)
{
    QList<OutputInfo>& outputList = m_pGmHelper->getOutputInfo();

    for (int var = 0; var < outputList.count(); var++) {
        if (!outputList[var].name.compare(outputName,Qt::CaseInsensitive) || outputName == "all") {
            if (state || (outputName=="" && outputList[var].connectState == RR_Connected)) {//just set gamma when output enable.
                outputList[var].targetTemp = m_targetTemp;
            }
        }
    }

    USD_LOG(LOG_DEBUG,"output:%s state:%d,output", outputName.toLatin1().data(), state);
    if (this->isRunning()) {
        return;
    }

    if (outputName == "all") {
        QTimer::singleShot(1*1000, [=](){
           this->run(); //#205251, reset it
        });
    }

    this->run();

}

int GmWorkThread::enablePrimarySignal(int state)
{
    m_pGmHelper->enablePrimarySignal(state);
}

int GmWorkThread::screenBrightnessChangedSignal(QString screenName, uint brightness)
{
    /*
    if (DBUS_GC_BRIGHTNESS_SIGNAL_PRIMARYCHANGED_END != signalName && (brightness == lastBrightness || m_sendPrimaryBrightnessSignal == false)) {
        USD_LOG(LOG_DEBUG,"skip send signal.");
        return;
    }

    lastBrightness = brightness;
    */
    QDBusMessage notifySignal =
            QDBusMessage::createSignal(DBUS_GC_BRIGHTNESS_PATH, DBUS_GC_BRIGHTNESS_INTERFACE, DBUS_GM_SCREENCHANGED);
    notifySignal.setArguments({QVariant::fromValue((QString)screenName), QVariant::fromValue((uint)brightness)});
    QDBusConnection::sessionBus().send(notifySignal);
}

