/* -*- 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.h"
#include "gamma-color-info.h"
//struct ColorInfo {
//    QString arg;
//    QDBusVariant out;
//};

//QDBusArgument &operator<<(QDBusArgument &argument, const ColorInfo &mystruct)
//{
//    argument.beginStructure();
//    argument << mystruct.arg << mystruct.out;
//    argument.endStructure();
//    return argument;
//}

//const QDBusArgument &operator>>(const QDBusArgument &argument, ColorInfo &mystruct)
//{
//    argument.beginStructure();
//    argument >> mystruct.arg >> mystruct.out;
//    argument.endStructure();
//    return argument;
//}

//Q_DECLARE_METATYPE(ColorInfo)
GammaManager *GammaManager::m_gammaManager = nullptr;
/*
 *
 * 目前拆分四个类：
 * 管理类：在合适的时机（开始运行、gsettings改变时）调用GmThread进行设置，GmLocation获取地理位置
 * 经纬度获取类：GmLocation，用于获取经纬度
 * 渐变类：GmApply，用户将计算后gamma值应用
 * 没有联网时，全天生效.
 * 缺省参数关闭。
 * 异常处理机制：
 * -地址链接失败，
 * --重复三次后检查网络状态，重复6次后更换地址
 * -json解析失败，
 * --重复10次后检查网络状态更换地址
*/
GammaManager::GammaManager():
    m_pColorSettings(nullptr),
    m_darkModeChangedBySelf(false),
    m_cachedTemperature(6500)
{
    m_pCheckTimer     = new QTimer(this);

    m_pGmLocation    = new GmLocation(this);
    m_pGmThread      = new GmWorkThread(this);

    m_pukuiGtkConfig = new UkuiGtkConfig(this);
    m_pColorSettings = new QGSettings(USD_COLOR_SCHEMA);
    m_pQtSettings    = new QGSettings(QT_THEME_SCHEMA);
    m_pGtkSettings   = new QGSettings(GTK_THEME_SCHEMA);

    m_pGmDbus        = new GmDbus(this);
    m_pGmAdaptor     = new GmAdaptor(m_pGmDbus);
    m_pclockNotifier = new USD::ClockSkewNotifier(this);

    m_pCheckTimer->setTimerType(Qt::PreciseTimer);

    QDBusConnection sessionBus = QDBusConnection::sessionBus();
    if (sessionBus.registerService(DBUS_GM_NAME)) {
        sessionBus.registerObject(DBUS_GM_PATH,
                                  m_pGmDbus,
                                  QDBusConnection::ExportAllContents);
        USD_LOG(LOG_DEBUG, "register gamma manager dbus success");
    } else {
        USD_LOG(LOG_ERR, "register dbus error");
    }

    m_inDark = m_pColorSettings->get(COLOR_KEY_AUTO_THEME).toBool();
    connect(m_pclockNotifier, SIGNAL(clockSkewed(QString)), SLOT(gammaRecheck(QString)));
}

GammaManager::~GammaManager()
{
    m_pCheckTimer->stop();
    if (m_pColorSettings) {
        delete m_pColorSettings;
        m_pColorSettings = nullptr;
    }

    if (m_pGmLocation) {
        delete m_pGmLocation;
        m_pGmLocation = nullptr;
    }

    if (m_pQtSettings) {
        delete m_pQtSettings;
        m_pQtSettings = nullptr;
    }

    if (m_pGtkSettings) {
        delete m_pGtkSettings;
        m_pGtkSettings = nullptr;
    }

    if (m_pCheckTimer) {
        delete m_pCheckTimer;
        m_pCheckTimer = nullptr;
    }

    if (m_pGmThread) {
        delete m_pGmThread;
        m_pGmThread = nullptr;
    }

    if (m_pukuiGtkConfig) {
        delete m_pukuiGtkConfig;
        m_pukuiGtkConfig = nullptr;
    }
}

GammaManager *GammaManager::GammaManagerNew()
{
    if (m_gammaManager == nullptr) {
        m_gammaManager = new GammaManager();
    }
    return m_gammaManager;
}

bool GammaManager::Start()
{
    //wayland转化到x时需要禁用kwin的色温管理

    if (UsdBaseClass::isEdu())
    {
        if (!m_pColorSettings->get(HAD_SET_EDU).toBool()) {
            m_pColorSettings->set(COLOR_KEY_TEMPERATURE, 5150);
            m_pColorSettings->set(COLOR_KEY_ALLDAY, true);
            m_pColorSettings->set(COLOR_KEY_AUTOMATIC, false);
            m_pColorSettings->set(COLOR_KEY_TEMPERATURE, 5150);
            m_pColorSettings->set(HAD_SET_EDU,true);
             USD_LOG(LOG_DEBUG,"--edu first  start--");
        }
        USD_LOG(LOG_DEBUG,"--Color check end--");
    }

    if (!m_pColorSettings->get(HAD_READ_KWIN).toBool()){
        if (false == ReadKwinColorTempConfig()) {
            USD_LOG(LOG_ERR,"--Kwin Color check over--");
        }
    }

    m_pGmLocation->setGsettings(m_pColorSettings);
    m_pGmLocation->start();

    connect(m_pQtSettings, SIGNAL(changed(QString)), this, SLOT(doQtSettingsChanged(QString)), Qt::DirectConnection);
    connect(m_pColorSettings, SIGNAL(changed(QString)), this, SLOT(doColorSettingsChanged(QString)), Qt::DirectConnection);
    connect(m_pCheckTimer, SIGNAL(timeout()), this, SLOT(doCheckTimeout()), Qt::DirectConnection);

    connect(m_pGmDbus, SIGNAL(screenBrightnessChanged(QString, int)), this, SLOT(doScreenBrightnessChanged(QString,int)), Qt::DirectConnection);
    connect(m_pGmDbus, SIGNAL(allScreenBrightnessChanged(QString, int)), this, SLOT(doScreenBrightnessChanged(QString,int)), Qt::DirectConnection);

    doCheckTimeout();
    m_pCheckTimer->setSingleShot(false);
    m_pCheckTimer->start(60000);
    m_pukuiGtkConfig->connectGsettingSignal();
    USD_LOG(LOG_DEBUG,"start in x.....");
    return true;
}

void GammaManager::Stop()
{
    m_pGmThread->stopWork();
    m_pGmThread->exit();
    m_pGmThread->wait();
    USD_LOG(LOG_DEBUG,"stop.....");
}

void GammaManager::setLocationIp(QStringList addresses)
{
    m_pGmLocation->setIpAddresses(addresses);
}

/*Active:1,使能，0禁用。
 *0:全天，1跟随日出日落，2自定义
 *Mode:1 自定义
 *Mode:2--EveningBeginFixed（17:55:01）---跟随日出日落
 *Mode:3--全天
*/
bool GammaManager::ReadKwinColorTempConfig()
{
    QHash<QString, QVariant> nightConfig;
    QVector<ColorInfo> nightColor;
    if (m_pColorSettings->keys().contains(HAD_READ_KWIN)) {
        if (m_pColorSettings->get(HAD_READ_KWIN).toBool() == true) {
            USD_LOG(LOG_DEBUG,"Kwin had read over..");
            return false;
        }
    } else {
        USD_LOG(LOG_DEBUG,"can't find key:%s", HAD_READ_KWIN);
        return false;
    }

    QDBusInterface colorIft("org.ukui.KWin",
                            "/ColorCorrect",
                            "org.ukui.kwin.ColorCorrect",
                            QDBusConnection::sessionBus());

    QDBusMessage result = colorIft.call("nightColorInfo");
    if (result.type() != QDBusMessage::ReplyMessage) {
        USD_LOG(LOG_ERR,"call error...");
        return false;
    }

    const QDBusArgument &dbusArgs = result.arguments().at(0).value<QDBusArgument>().asVariant().value<QDBusArgument>();

    dbusArgs.beginArray();
    while (!dbusArgs.atEnd()) {
        ColorInfo color;
        dbusArgs >> color;
        nightColor.push_back(color);
    }
    dbusArgs.endArray();

    for (ColorInfo it : nightColor) {
        nightConfig.insert(it.arg, it.out.variant());
    }

    m_pColorSettings->set(COLOR_KEY_TEMPERATURE, nightConfig[KWIN_NIGHT_TEMP].toInt());
    m_pColorSettings->set(COLOR_KEY_ENABLED,nightConfig[KWIN_COLOR_ACTIVE].toBool());

    if (3 == nightConfig[KWIN_COLOR_MODE].toInt()) {
        m_pColorSettings->set(COLOR_KEY_ALLDAY, true);
    } else if (2 == nightConfig[KWIN_COLOR_MODE].toInt() && nightConfig[KWIN_COLOR_START].toString() == "17:55:01"){
        m_pColorSettings->set(COLOR_KEY_AUTOMATIC, true);
    } else {
        QTime startTime = QTime::fromString(nightConfig[KWIN_COLOR_START].toString(),"hh:mm:ss");
        QTime endTime = QTime::fromString(nightConfig[KWIN_COLOR_END].toString(),"hh:mm:ss");

        m_pColorSettings->set(COLOR_KEY_FROM, hourMinuteToDouble(startTime.hour(), startTime.minute()));
        m_pColorSettings->set(COLOR_KEY_TO, hourMinuteToDouble(endTime.hour(), endTime.minute()));
    }

    USD_LOG_SHOW_PARAM1(nightConfig[KWIN_COLOR_ACTIVE].toBool());
    USD_LOG_SHOW_PARAM1(nightConfig[KWIN_COLOR_MODE].toInt());
    USD_LOG_SHOW_PARAMS(nightConfig[KWIN_COLOR_START].toString().toLatin1().data());
    USD_LOG_SHOW_PARAMS(nightConfig[KWIN_COLOR_END].toString().toLatin1().data());

    m_pColorSettings->set(HAD_READ_KWIN,true);
    nightConfig[KWIN_COLOR_ACTIVE] = false;
    colorIft.call("setNightColorConfig", nightConfig);

    nightConfig[KWIN_NIGHT_TEMP] = nightConfig[KWIN_CURRENT_TEMP];
    nightConfig[KWIN_COLOR_ACTIVE] = false;
    colorIft.call("setNightColorConfig", nightConfig);
    return true;
}
OutputGammaInfo GammaManager::getScreensInfo()
{
    OutputGammaInfo hdmi;
    OutputGammaInfo vga;
    hdmi.OutputName = "hdmi";
    hdmi.Gamma = 6500;
    hdmi.Brignthess = 100;

    vga.OutputName = "vga";
    vga.Gamma = 6000;
    vga.Brignthess = 80;
    OutputGammaInfoList varRet;

    return hdmi;
}

OutputGammaInfoList GammaManager::getScreensInfoList()
{
    OutputGammaInfoList infoList;
    infoList = m_pGmThread->getAllOutputGammaInfo();
    return infoList;
}


void GammaManager::doColorSettingsChanged(QString key)
{
    USD_LOG(LOG_DEBUG,"change key:%s",key.toLatin1().data());
    checkEyeCareMode(key);
    if (isDarkMode(key)) {
        USD_LOG(LOG_DEBUG,"return...");
        return;
    }

//    if (key == COLOR_KEY_LAST_COORDINATES) {
//        QVariant qVar;
//        QVariantList qVarList;

//        qVar = m_pColorSettings->get(key);
//        qVarList = qVar.value<QVariantList>();
//        USD_LOG(LOG_DEBUG,"key:%s(%0.4f,%0.4f)",key.toLatin1().data(), qVarList[0].toDouble(), qVarList[1].toDouble());
//    }

    gammaRecheck(key);
}

void GammaManager::doCheckTimeout()
{
    checkEyeCareMode(EYE_CARE_MOEDE);
    gammaRecheck("");
}

void GammaManager::doScreenBrightnessChanged(QString outputName, int outputBrightness)
{
    m_pGmThread->setBrightness(outputName,outputBrightness);
    if (!m_pGmThread->isRunning()) {
        m_pGmThread->start();
    }
    USD_LOG(LOG_DEBUG,"set %s to %d", outputName.toLatin1().data(), outputBrightness);
}



bool GammaManager::isDarkMode(QString key)
{
    bool darkMode = m_pColorSettings->get(COLOR_KEY_DARK_MODE).toBool();
    bool ret;
    if (key.contains("-dm") || key == COLOR_KEY_REAL_TIME_TEMPERATURE) {
        return true;
    }

    //外部修改，则直接退出夜间模式。
    if (key == COLOR_KEY_ALLDAY || key == COLOR_KEY_ENABLED) {
        bool isAllDay = m_pColorSettings->get(COLOR_KEY_ALLDAY).toBool();
        bool isEnable = getNightColorState();

        if (darkMode && false == (isAllDay & isEnable)) {
            setDarkMode(false);
            USD_LOG(LOG_DEBUG,"exit dark mode..%d %d",isAllDay, isEnable);
            return false;
        } else  if (isAllDay && isEnable && darkMode == false) {
            if (m_pQtSettings->get(QT_THEME_KEY).toString() == "ukui-dark") {
                setDarkMode(true);
                USD_LOG(LOG_DEBUG,"enter dark mode..");
                return false;
            }
        }
    } else if (key == COLOR_KEY_AUTOMATIC) {
        ret = m_pColorSettings->get(key).toBool();
        if (darkMode && true == ret) {
            setDarkMode(false);
            USD_LOG(LOG_DEBUG,"exit dark mode..");
            return false;
        }
    } else if (key == COLOR_KEY_AUTO_THEME) {
        ret = m_pColorSettings->get(key).toBool();
        if (darkMode && true == ret) {
            setDarkMode(false);
            USD_LOG(LOG_DEBUG,"exit dark mode..");
            return false;
        }
    }

    if (key == COLOR_KEY_DARK_MODE) {
        int inDark = m_pColorSettings->get(key).toBool();

        if (inDark == m_inDark) {
            return true;
        }

        if (inDark) {//进入夜间模式
            m_pColorSettings->blockSignals(true);
            m_pColorSettings->set(COLOR_KEY_ALLDAY_DM, m_pColorSettings->get(COLOR_KEY_ALLDAY).toBool());
            m_pColorSettings->set(COLOR_KEY_ENABLED_DM, getNightColorState());
            m_pColorSettings->set(COLOR_KEY_AUTOMATIC_DM, m_pColorSettings->get(COLOR_KEY_AUTOMATIC).toBool());
            m_pColorSettings->set(COLOR_KEY_STYLE_NAME_DM, m_pQtSettings->get(QT_THEME_KEY).toString());
            m_pColorSettings->set(COLOR_KEY_AUTO_THEME_DM, m_pColorSettings->get(COLOR_KEY_AUTO_THEME).toString());//四个任意一个改变则退出夜间模式。
            m_pColorSettings->set(COLOR_KEY_AUTOMATIC, false);
            m_pColorSettings->set(COLOR_KEY_AUTO_THEME, false);
            m_pQtSettings->set(QT_THEME_KEY, "ukui-dark");
            m_pGtkSettings->set(GTK_THEME_KEY, "ukui-black");
            if (!m_pColorSettings->get(COLOR_KEY_ALLDAY).toBool() &&
                    !m_pColorSettings->get(COLOR_KEY_ENABLED).toBool()) {
                m_pColorSettings->set(COLOR_KEY_ALLDAY, true);
                USD_LOG(LOG_DEBUG,"...A");
            }

            m_pColorSettings->blockSignals(false);
            if(!m_pColorSettings->get(COLOR_KEY_ALLDAY).toBool()) {
                m_pColorSettings->set(COLOR_KEY_ALLDAY, true);
                                USD_LOG(LOG_DEBUG,"enable allday");
            }

            if(!m_pColorSettings->get(COLOR_KEY_ENABLED).toBool()) {
                m_pColorSettings->set(COLOR_KEY_ENABLED, true);
                                USD_LOG(LOG_DEBUG,"enable color");
            }

            m_pColorSettings->apply();

            USD_LOG(LOG_DEBUG, "enter dark mode");

        } else {//退出夜间模式1
            m_pColorSettings->delay();

            if (m_pColorSettings->get(COLOR_KEY_ALLDAY_DM).toBool() !=
                    m_pColorSettings->get(COLOR_KEY_ALLDAY).toBool()) {
                m_pColorSettings->set(COLOR_KEY_ALLDAY, m_pColorSettings->get(COLOR_KEY_ALLDAY_DM).toBool());
            }

            if (m_pColorSettings->get(COLOR_KEY_ENABLED_DM).toBool() !=
                    m_pColorSettings->get(COLOR_KEY_ENABLED).toBool()) {
                m_pColorSettings->set(COLOR_KEY_ENABLED, m_pColorSettings->get(COLOR_KEY_ENABLED_DM).toBool());
            }

            if (m_pColorSettings->get(COLOR_KEY_AUTOMATIC_DM).toBool() != m_pColorSettings->get(COLOR_KEY_AUTOMATIC).toBool()) {
                 m_pColorSettings->set(COLOR_KEY_AUTOMATIC, m_pColorSettings->get(COLOR_KEY_AUTOMATIC_DM).toBool());
            }

            if (m_pColorSettings->get(COLOR_KEY_AUTO_THEME_DM).toBool() != m_pColorSettings->get(COLOR_KEY_AUTO_THEME).toBool()) {
                m_pColorSettings->set(COLOR_KEY_AUTO_THEME, m_pColorSettings->get(COLOR_KEY_AUTO_THEME_DM).toBool());
            }

            if (false == m_pColorSettings->get(COLOR_KEY_AUTO_THEME).toBool()) {
                if (m_pColorSettings->get(COLOR_KEY_STYLE_NAME_DM).toString() == "ukui-default") {
                    m_pQtSettings->set(QT_THEME_KEY, "ukui-default");
                    m_pGtkSettings->set(GTK_THEME_KEY, "ukui-white");
                } else if(m_pColorSettings->get(COLOR_KEY_STYLE_NAME_DM).toString() == "ukui-light"){
                    m_pQtSettings->set(QT_THEME_KEY, "ukui-light");
                    m_pGtkSettings->set(GTK_THEME_KEY, "ukui-white");
                } else if(!m_pColorSettings->get(COLOR_KEY_ALLDAY_DM).toBool() ||  !m_pColorSettings->get(COLOR_KEY_ENABLED_DM).toBool())  {
                    m_pQtSettings->set(QT_THEME_KEY, "ukui-dark");
                    m_pGtkSettings->set(GTK_THEME_KEY, "ukui-black");
                } else {
                    m_pQtSettings->set(QT_THEME_KEY, "ukui-light");
                    m_pGtkSettings->set(GTK_THEME_KEY, "ukui-white");
                }
            }
            m_pColorSettings->apply();
            USD_LOG(LOG_DEBUG, "exit dark mode");
        }

        m_inDark = inDark;
        return true;
    }

    return false;
}

void GammaManager::setDarkMode(bool darkMode)
{
    m_pColorSettings->blockSignals(true);
    m_pColorSettings->set(COLOR_KEY_DARK_MODE,darkMode);
    m_pColorSettings->blockSignals(false);
    m_pColorSettings->apply();
}

void GammaManager::checkEyeCareMode(QString key)
{
     bool isColorEnable = getNightColorState();
     bool isEyeCareEnable = m_pColorSettings->get(EYE_CARE_MOEDE).toBool();
     USD_LOG_SHOW_PARAM1(isEyeCareEnable);
     USD_LOG_SHOW_PARAM1(isColorEnable);
    if (key == COLOR_KEY_ENABLED && isColorEnable) {
        m_pColorSettings->blockSignals(true);
        m_pColorSettings->set(EYE_CARE_MOEDE,false);
    } else if (key == EYE_CARE_MOEDE && isEyeCareEnable) {
        m_pColorSettings->blockSignals(true);
        m_pColorSettings->set(COLOR_KEY_ENABLED,false);
    }

    if (key == COLOR_KEY_ENABLED || key == EYE_CARE_MOEDE) {
        m_pColorSettings->apply();
        m_pColorSettings->blockSignals(false);
    }
}

bool GammaManager::isFracDayBetween(double value, double start, double end)
{
    if (end <= start) {
        end += 24;
    }

    if (value < start && value < end) {
        value += 24;
    }

   return value >= start && value < end;
}

void GammaManager::gammaRecheck(QString key)
{
    double fracDay;
    double scheduleFrom = -1.f;
    double scheduleTo = -1.f;
    double smear = USD_NIGHT_LIGHT_POLL_SMEAR; /* hours */
    int interpolateStart = 0;

    int temperature;
    uint tempSmeared;
    QTime rtTime = QTime::currentTime();

    fracDay = getFracTimeFromDt(rtTime);
    //跟随日出日落
    if (m_pColorSettings->get(COLOR_KEY_AUTOMATIC).toBool()) {
        scheduleFrom = m_pColorSettings->get(COLOR_KEY_AUTOMATIC_FROM).toDouble();
        scheduleTo = m_pColorSettings->get(COLOR_KEY_AUTOMATIC_TO).toDouble();
        if (scheduleFrom < 0.f || scheduleTo < 0.f) {
            scheduleFrom = m_pColorSettings->get(COLOR_KEY_FROM).toDouble();
            scheduleTo = m_pColorSettings->get(COLOR_KEY_TO).toDouble();
        }
    } else {
        scheduleFrom = m_pColorSettings->get(COLOR_KEY_FROM).toDouble();
        scheduleTo = m_pColorSettings->get(COLOR_KEY_TO).toDouble();
    }


    //自动主题
    if (m_pColorSettings->get(COLOR_KEY_AUTO_THEME).toBool()) {
        if (isFracDayBetween(fracDay, scheduleFrom, scheduleTo)) {
            m_pGtkSettings->set(GTK_THEME_KEY, "ukui-black");
            m_pQtSettings->set(QT_THEME_KEY, "ukui-dark");
        } else {
            m_pGtkSettings->set(GTK_THEME_KEY, "ukui-white");
            m_pQtSettings->set(QT_THEME_KEY, "ukui-light");
        }

        if(key == COLOR_KEY_AUTO_THEME) {
            return;
        }
    }

    //合法性检测
    temperature = m_pColorSettings->get(COLOR_KEY_TEMPERATURE).toUInt();
    USD_LOG_SHOW_PARAM1(temperature);
    if(temperature < 1100 || temperature > COLOR_TEMPERATURE_DEFAULT) {
        USD_LOG(LOG_ERR, "temperature had error value:%d", temperature);
        return;
    }


    if (!m_pColorSettings->get(EYE_CARE_MOEDE).toBool() && !getNightColorState()) {
        setTemperature(COLOR_TEMPERATURE_DEFAULT);
        return;
    }

    //色温使能
    if (getNightColorState()) {
        interpolateStart = COLOR_TEMPERATURE_DEFAULT;
    }

    if (m_pColorSettings->get(EYE_CARE_MOEDE).toBool()) {
        getEyeCareDate(temperature, interpolateStart, scheduleFrom, scheduleTo);
    }

    //全天生效
    if (m_pColorSettings->get(COLOR_KEY_ALLDAY).toBool() && getNightColorState()) {
        setTemperature(temperature);
        return;
    }

    /* lower smearing period to be smaller than the time between start/stop */
    smear = qMin (smear,
                (qMin (qAbs ((scheduleTo - scheduleFrom)),
                     (24 - qAbs ((scheduleTo - scheduleFrom))))));

    USD_LOG(LOG_DEBUG,"fracDay:%.2f, %.2f %.2f", fracDay,scheduleFrom - smear, scheduleTo);
    if (!isFracDayBetween (fracDay,
                           scheduleFrom - smear,
                           scheduleTo)) {
        setTemperature(interpolateStart);
        USD_LOG(LOG_DEBUG,"in smeared...");
        return;
    }

    /* smear the temperature for a short duration before the set limits
    *
    *   |----------------------| = from->to
    * |-|                        = smear down
    *                        |-| = smear up
    *
    * \                        /
    *  \                      /
    *   \--------------------/
    */

    if (smear < 0.01) {
        /* Don't try to smear for extremely short or zero periods */
        tempSmeared = temperature;
        USD_LOG(LOG_DEBUG,"can't smeared...");
    } else if (isFracDayBetween(fracDay,
                                           scheduleFrom - smear,
                                           scheduleFrom)) {
        double factor = 1.f - ((fracDay - (scheduleFrom - smear)) / smear);
        tempSmeared = linearInterpolate (interpolateStart,
                                          temperature, factor);
        USD_LOG(LOG_DEBUG,"interpolateStart:%d temperature:%d factor:%f,frac_day:%f,schedule_from:%f",interpolateStart, temperature, factor,
                fracDay,scheduleFrom);
    } else if (isFracDayBetween (fracDay,
                                           scheduleTo - smear,
                                           scheduleTo)) {
        double factor = (fracDay - (scheduleTo - smear)) / smear;
        tempSmeared = linearInterpolate (interpolateStart,
                                          temperature, factor);
        USD_LOG(LOG_DEBUG,"interpolateStart:%d temperature:%d factor:%f,frac_day:%f,schedule_from:%f",interpolateStart, temperature, factor,
                fracDay,scheduleTo);
    } else {
        tempSmeared = temperature;
    }
    USD_LOG_SHOW_PARAM2(tempSmeared, (interpolateStart-tempSmeared));
    setTemperature(tempSmeared);
}

int GammaManager::setTemperature(const uint value)
{
    if (m_pGmThread->getTemperature() == value) {
        USD_LOG(LOG_DEBUG,"same value!!!");
        return 0;
    }

    m_pGmThread->setTemperature(value);
    if (!m_pGmThread->isRunning()) {
        m_pGmThread->start();
    }

    return 0;
}

void GammaManager::getEyeCareDate(int& temperature, int& interpolateStart, double& scheduleFrom, double& scheduleTo)
{
    double autoScheduleFrom,autoScheduleTo,fracDay;

    QTime rtTime = QTime::currentTime();
    fracDay = getFracTimeFromDt(rtTime);

    autoScheduleFrom = m_pColorSettings->get(COLOR_KEY_AUTOMATIC_FROM).toDouble();
    autoScheduleTo = m_pColorSettings->get(COLOR_KEY_AUTOMATIC_TO).toDouble();
    QVariant qVar = m_pColorSettings->get(COLOR_KEY_LAST_COORDINATES);
    QVariantList qVarList =  qVar.value<QVariantList>();

    if(!(qVarList[1].toDouble() > -180 && qVarList[1].toDouble() < 180 &&
            qVarList[0].toDouble() > -90 && qVarList[0].toDouble() < 90)) {
        autoScheduleFrom = m_pColorSettings->get(COLOR_KEY_FROM).toDouble();
        autoScheduleTo = m_pColorSettings->get(COLOR_KEY_TO).toDouble();
        USD_LOG(LOG_DEBUG," latitude and longitude error use user config");
    }

    /* smear the temperature for a short duration before the set limits
    *
    *  ---|                                         |------ = to->from(EYE_CARE_VALUE_DAY)
    *     |-|                                     |-| = smear down
    *       |---|                             |---| = from->1, 3->to  evening and dawn (EYE_CARE_VALUE_EVENING_DAWN)
    *           |-|                         |-| = smear down
    *             |-------------------------| =  late at night  (EYE_CARE_VALUE_LATE_NIGHT)
    */

    // evening
    if (isFracDayBetween (fracDay,
                           autoScheduleFrom,
                           DEEP_NIGHT_START)) {
        temperature = m_pColorSettings->get(EYE_CARE_VALUE_EVENING_DAWN).toInt();
        scheduleFrom = autoScheduleFrom;
        scheduleTo = DEEP_NIGHT_START;
        interpolateStart = m_pColorSettings->get(EYE_CARE_VALUE_LATE_NIGHT).toInt();
        USD_LOG(LOG_DEBUG,"in evening");
        return;
    }

    //dawn
    if (isFracDayBetween (fracDay,
                          DEEP_NIGHT_END,
                          autoScheduleTo)) {
        temperature =  m_pColorSettings->get(EYE_CARE_VALUE_EVENING_DAWN).toInt();
        scheduleFrom = DEEP_NIGHT_END;
        scheduleTo = autoScheduleTo;
        interpolateStart = m_pColorSettings->get(EYE_CARE_VALUE_DAY).toInt();
        USD_LOG(LOG_DEBUG,"in dawn");
        return;
    }

    // late at night
    if (isFracDayBetween (fracDay,
                          DEEP_NIGHT_START,
                          DEEP_NIGHT_END)) {
        temperature =  m_pColorSettings->get(EYE_CARE_VALUE_LATE_NIGHT).toInt();
        scheduleFrom = DEEP_NIGHT_START;
        scheduleTo = DEEP_NIGHT_END;
        interpolateStart = m_pColorSettings->get(EYE_CARE_VALUE_EVENING_DAWN).toInt();
        USD_LOG(LOG_DEBUG,"in late at night");
        return;
    }

    // all night
    if (isFracDayBetween (fracDay,
                           autoScheduleTo,
                           autoScheduleFrom)) {
        temperature =  m_pColorSettings->get(EYE_CARE_VALUE_DAY).toInt();
        scheduleFrom = autoScheduleTo;
        scheduleTo = autoScheduleFrom;
        interpolateStart =  m_pColorSettings->get(EYE_CARE_VALUE_EVENING_DAWN).toInt();;
        USD_LOG(LOG_DEBUG,"in day");
        return;
    }
}
/*WT* why we can't recv the value*/
bool GammaManager::getNightColorState()
{
    QGSettings settings(USD_COLOR_SCHEMA);
    return settings.get(COLOR_KEY_ENABLED).toBool();
}

int GammaManager::enablePrimarySignal(int state)
{
    m_pGmThread->enablePrimarySignal(state);
}

void GammaManager::doQtSettingsChanged(QString key)
{
    if (key == QT_THEME_KEY) {
        bool isAllDay = m_pColorSettings->get(COLOR_KEY_ALLDAY).toBool();
        bool isEnable = getNightColorState();

        if (m_pQtSettings->get(key).toString() != "ukui-dark") {
            if (m_pColorSettings->get(COLOR_KEY_DARK_MODE).toBool()) {
                m_pColorSettings->set(COLOR_KEY_STYLE_NAME_DM, m_pQtSettings->get(QT_THEME_KEY).toString());
                setDarkMode(false);
                USD_LOG(LOG_DEBUG,"exit dark mode..");
                m_pQtSettings->apply();
            }
        }

        QString theme = m_pQtSettings->get(QT_THEME_KEY).toString();
        if (isAllDay && isEnable && theme == "ukui-dark") {
            setDarkMode(true);
            USD_LOG(LOG_DEBUG,"enter dark mode..");
        }
    }
}

void GammaManager::setBrightness(const uint value)
{
    USD_LOG(LOG_DEBUG, "set brightness:%d", value);
    m_pGmThread->setAllOutputsBrightness(value);
    if (!m_pGmThread->isRunning()) {
        m_pGmThread->start();
    }
}

double GammaManager::getFracTimeFromDt(QTime dt)
{
    return dt.hour() + (double) dt.minute() / 60 + (double) dt.second() / 3600;
}

double GammaManager::linearInterpolate(double val1, double val2, double factor)
{
    USD_CHECK_RETURN (factor >= 0.f, -1.f);
    USD_CHECK_RETURN (factor <= 1.f, -1.f);
    return ((val1 - val2) * factor) + val2;
}

double GammaManager::hourMinuteToDouble(int hour, int minute)
{
    double value = (double)minute/60;
    return (double) hour + value;
}

int GammaManager::setPrimaryBrightness(int brightness)
{
    return m_pGmThread->setPrimaryBrightness(brightness);
}

int GammaManager::getPrimaryBrightness()
{
    return m_pGmThread->getPrimaryBrightness();
}


