/*
 * Copyright (C) 2018 Tianjin KYLIN Information Technology 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 "interface.h"
#include <QDebug>
#include <QDBusConnection>
#include <QDBusMessage>
#include <QDBusInterface>
#include <QTimer>
#include <unistd.h>
#include <QDBusPendingReply>
#include <QGSettings>
#include <QDBusReply>
#include <signal.h>
#include <QFileInfo>
#include <unistd.h>
#include <fcntl.h>

Interface::Interface(QObject *parent)
    : QObject(parent),
      m_timerCount(0),
      m_timer(nullptr)
{
    lockState = false;
    slpState = false;
    m_logind = new LogindIntegration(this);
    connect(m_logind, &LogindIntegration::requestLock, this,
        [this]() {
            this->onShowScreensaver();
        }
    );

    connect(m_logind, &LogindIntegration::requestUnlock, this,
        [this]() {
//        	process.terminate();
//		因为fullbackgroundwidget.cpp会监听这个信号，因此这个信号没必要监听了
//        	process.kill();
	}
    );

    connect(&process, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
        [=](int exitCode, QProcess::ExitStatus exitStatus){
            qInfo()<<"ukui-screensaver-dialog is exited,exitCode is "<<exitCode;
            if(exitStatus == QProcess::CrashExit)
                qWarning()<<process.errorString();

            emitLockState(false);
    });

    QDBusInterface *iface = new QDBusInterface("org.freedesktop.login1",
                                               "/org/freedesktop/login1",
                                               "org.freedesktop.login1.Manager",
                                               QDBusConnection::systemBus(),
                                               this);
    connect(iface, SIGNAL(PrepareForSleep(bool)), this, SLOT(onPrepareForSleep(bool)));

    // 监听一些会话状态
    m_sessionWatcher = new SessionWatcher(this);
    connect(m_sessionWatcher, &SessionWatcher::sessionIdle,
                         this, &Interface::onSessionIdleReceived);
//    connect(m_sessionWatcher, &SessionWatcher::sessionLockIdle,
//                         this, &Interface::Lock);
    connect(m_sessionWatcher, &SessionWatcher::sessionIdle,
            this, [=](){
        QDBusMessage message;
        message = QDBusMessage::createSignal(SS_DBUS_PATH,
                                             SS_DBUS_INTERFACE,
                                             "SecondRunParam");
        message<<"SessionIdle";
        QDBusConnection::sessionBus().send(message);
    });
//    connect(m_sessionWatcher, &SessionWatcher::sessionLockIdle,
//                         this, [=](){
//        QDBusMessage message;
//        message = QDBusMessage::createSignal(SS_DBUS_PATH,
//                                             SS_DBUS_INTERFACE,
//                                             "SessionLockIdle");
//        QDBusConnection::sessionBus().send(message);
//    });
    connect(m_sessionWatcher, &SessionWatcher::sessionIdleExit,
                         this, &Interface::onSessionIdleExit);
    connect(m_sessionWatcher, &SessionWatcher::lidStateChanged,
                         this, &Interface::onLidStateChaned);

    // 监听关屏信号
    QDBusConnection::sessionBus().connect(QString(),
                                           "/",
                                           "ukui.power.manager",
                                           "TurnOffDisplay",
                                           this,
                                           SLOT(onScreenClosed(bool)));
    inhibit();
}

bool Interface::GetSlpState()
{
    return ((process.state() != QProcess::NotRunning) && slpState);
}

bool Interface::GetLockState()
{
    if(process.state() != QProcess::NotRunning)
        return lockState;
    else
	return checkScreenDialogRunning(); 
}

bool Interface::GetBlankState()
{
    return blankState;
}

void Interface::SetLockState()
{
    lockState = true;
}

void Interface::emitLockState(bool val)
{
    qDebug()<<"emitLockState state = "<<val;
    if (QString(qgetenv("XDG_SESSION_TYPE")) == "wayland") {
        QDBusInterface interface("org.kde.KWin",
                             "/kglobalaccel",
                             "org.kde.KGlobalAccel",
                             QDBusConnection::sessionBus());
        interface.call("blockGlobalShortcuts",val);
    }
    QDBusMessage message;
    if(val){
    	message = QDBusMessage::createSignal(SS_DBUS_PATH,
                                             SS_DBUS_INTERFACE,
                                             "lock");
    }else{
         message = QDBusMessage::createSignal(SS_DBUS_PATH,
                                             SS_DBUS_INTERFACE,
                                             "unlock");
    }
    QDBusConnection::sessionBus().send(message);
}

void Interface::UnLock()
{
    if(process.state() != QProcess::NotRunning)
        process.kill();
}

void Interface::Lock()
{
    if(process.state() != QProcess::NotRunning) {
        return ;
    }
    qDebug() << "Lock requested";
    lockState = false;
    QString cmd = QString("/usr/bin/ukui-screensaver-dialog --lock");
    qDebug() << cmd;

    process.start(cmd);
    emitLockState(true);
}

void Interface::onSessionIdleReceived()
{
    qDebug()<<"onSessionIdleReceived--------";
    if (!checkStatus(SESSION_STATUS_SCREENSAVER)) {
        qDebug()<<"m_nStatus:"<<m_nStatus<<" can't do screensaver!";
        return ;
    }
    if(process.state() != QProcess::NotRunning)
        return ;

    qDebug() << "emit SessionIdle";
    lockState = false;
    QString cmd = QString("/usr/bin/ukui-screensaver-dialog --session-idle");
    qDebug() << cmd;
    process.start(cmd);
    emitLockState(true);
}

void Interface::onShowBlankScreensaver()
{
    if(process.state() != QProcess::NotRunning) {
        QDBusMessage message;
        message = QDBusMessage::createSignal(SS_DBUS_PATH,
                                             SS_DBUS_INTERFACE,
                                             "SecondRunParam");
        message<<"SleepLock";
        QDBusConnection::sessionBus().send(message);
        return ;
    }

    qDebug() << "lock and show screensaver";
    lockState = false;
    slpState = false;
    QString cmd = QString("/usr/bin/ukui-screensaver-dialog --blank");
    qDebug() << cmd;

    process.start(cmd);
    emitLockState(true);
}

void Interface::onShowScreensaver()
{
    if(process.state() != QProcess::NotRunning)
        return ;

    slpState = true;
    qDebug() << "lock and show screensaver";
    lockState = false;
    QString cmd = QString("/usr/bin/ukui-screensaver-dialog --lock-screensaver");
    qDebug() << cmd;

    process.start(cmd);
    emitLockState(true);
}

bool Interface::checkExistChild()
{
    char cmd[128] = {0};
    char str[4];
    FILE *fp;
    int num;

    sprintf(cmd, "ps -aux | grep ukui-screensaver-dialog | grep %s | grep -v grep | wc -l", getenv("USER"));

    fp = popen(cmd, "r");
    if(fgets(str, sizeof(str)-1, fp) == NULL)
        qDebug() << "fgets: " << strerror(errno);
    pclose(fp);

    num = atoi(str);

    qDebug() << (num > 0 ? "exist dialog running" : "");

    return num > 0;
}

void Interface::onNameLost(const QString &serviceName)
{
    if(serviceName == "cn.kylinos.ScreenSaver")
        exit(0);
}

bool Interface::checkScreenDialogRunning()
{
    int fd = -1;
    struct flock lock;

    const QString PID_DIR = QString("/var/run/user/%1").arg(QString::number(getuid()));
    QString env = qgetenv("DISPLAY");
    const QString PID_FILE = PID_DIR + QString("/ukui-screensaver%1.pid").arg(env);

    QFileInfo fileInfo(PID_FILE);
    if(!fileInfo.exists()) {
        return false;
    }
    if((fd = open(PID_FILE.toLocal8Bit().data(),
                   O_RDWR, 0666)) == -1){
        return false;
    }

    memset(&lock, 0, sizeof(struct flock));
    lock.l_type = F_WRLCK;
    lock.l_whence = SEEK_SET;

    if(fcntl(fd, F_GETLK, &lock) < 0) {
	close(fd);
        return false;
    } else {
        if (lock.l_type == F_UNLCK) {
	    close(fd);
            return false;
        } else {
	    close(fd);
            return true;
        }
    }
}

void Interface::onPrepareForSleep(bool sleep)
{
    qDebug()<<"onPrepareForSleep:"<<sleep<<"--------";
    if(sleep)
    {
        if (!checkStatus(SESSION_STATUS_SLEEPED)) {
            uninhibit();
            return;
        }
	
        if (checkScreenDialogRunning()) {
            uninhibit();
            return;
        }

        if(GetLockState()){
            uninhibit();
            return;
        }

        if(GetSlpState()){
            uninhibit();
            return;
        }

	blankState = true;
    	this->onShowBlankScreensaver();

        if(!m_timer){
            m_timer = new QTimer(this);
            connect(m_timer, &QTimer::timeout, this, [&]{
                m_timerCount+=1;

                if(GetLockState() || m_timerCount>20){
                    m_timer->stop();
                    m_timerCount = 0;
                    uninhibit();
                }
            });
        }
        m_timer->start(200);
    }
    else
    {
        m_nStatus &= ~SESSION_STATUS_SLEEPED;
	blankState = false;
        inhibit();
    }
}

void Interface::onSessionIdleExit()
{
    qDebug()<<"onSessionIdleExit--------";
    m_nStatus &= ~SESSION_STATUS_SCREENSAVER;
    m_nStatus &= ~SESSION_STATUS_SCREENCLOSE;
    stopDelayLockScreen();
}

void Interface::onScreenClosed(bool state)
{
    qDebug()<<"onScreenClosed:"<<state<<"------"<<m_isNewBlankInterface;
    if (m_isNewBlankInterface)
        return;
    if (state) {
        if (!checkStatus(SESSION_STATUS_SCREENCLOSE)) {
            qDebug()<<"m_nStatus:"<<m_nStatus<<" can't do screenclose lock!";
            return ;
        }
        if(process.state() != QProcess::NotRunning) {
            QDBusMessage message;
            message = QDBusMessage::createSignal(SS_DBUS_PATH,
                                                 SS_DBUS_INTERFACE,
                                                 "SecondRunParam");
            message<<"CloseLock";
            QDBusConnection::sessionBus().send(message);
            return ;
        }
        delayLockScreen();
    } else {
        stopDelayLockScreen();
    }
}

void Interface::onLidStateChaned(bool isClosed)
{
    if (!isClosed) {
        if (m_sessionWatcher->isLidCloseWithBlank()) {
            m_nStatus &= ~SESSION_STATUS_SCREENCLOSE;
        }
        stopDelayLockScreen();
    }
}

void Interface::ShowScreensaver()
{
    if(process.state() != QProcess::NotRunning)
        return ;

    qDebug() << "lock and show screensaver";
    lockState = false;
    QString cmd = QString("/usr/bin/ukui-screensaver-dialog --screensaver");
    qDebug() << cmd;

    process.start(cmd);
    emitLockState(true);
}

void Interface::inhibit()
{
    if (m_inhibitFileDescriptor.isValid()) {
        return;
    }

    QDBusMessage message = QDBusMessage::createMethodCall("org.freedesktop.login1",
                                                          "/org/freedesktop/login1",
                                                          "org.freedesktop.login1.Manager",
                                                          QStringLiteral("Inhibit"));
    message.setArguments(QVariantList({QStringLiteral("sleep"),
                                       "Screen Locker Backend",
                                       "Ensuring that the screen gets locked before going to sleep",
                                       QStringLiteral("delay")}));
    QDBusPendingReply<QDBusUnixFileDescriptor> reply = QDBusConnection::systemBus().call(message);
    if (!reply.isValid()) {
        return;
    }
    reply.value().swap(m_inhibitFileDescriptor);
}

void Interface::uninhibit()
{
    if (!m_inhibitFileDescriptor.isValid()) {
        return;
    }

     m_inhibitFileDescriptor = QDBusUnixFileDescriptor();
}

bool Interface::checkStatus(int nStatus)
{
    if (!m_sessionWatcher) {
        return false;
    }
    switch (nStatus) {
    case SESSION_STATUS_SLEEPED:
    {
        m_nStatus |= nStatus;
        if (!m_sessionWatcher->isSleepActivationEnable()) {
            return false;
        }
    }
        break;
    case SESSION_STATUS_SCREENCLOSE:
    {
        m_nStatus |= nStatus;
        if ((m_nStatus&SESSION_STATUS_SLEEPED) || !m_sessionWatcher->isCloseActivationEnable()) {
            return false;
        }
        if (((m_sessionWatcher->closeActivationDelay() == m_sessionWatcher->sleepActivationDelay())
             && m_sessionWatcher->sleepActivationDelay() != -1) && !m_sessionWatcher->isSleepActivationEnable()) {
            qDebug()<<"Sleep same with Close and Sleep disable!";
            return false;
        }
    }
        break;
    case SESSION_STATUS_SCREENSAVER:
    {
        m_nStatus |= nStatus;
        if ((m_nStatus&SESSION_STATUS_SLEEPED) || (m_nStatus&SESSION_STATUS_SCREENCLOSE)) {
            return false;
        }
        if (((m_sessionWatcher->idledelay()*60 == m_sessionWatcher->sleepActivationDelay())
             && m_sessionWatcher->sleepActivationDelay() != -1) && !m_sessionWatcher->isSleepActivationEnable()) {
            qDebug()<<"Sleep same with idle and Sleep disable!";
            return false;
        }
        if (((m_sessionWatcher->idledelay()*60 == m_sessionWatcher->closeActivationDelay())
             && m_sessionWatcher->closeActivationDelay() != -1)/* && !m_sessionWatcher->isCloseActivationEnable()*/) {
            qDebug()<<"Close same with idle do nothing!";
            return false;
        }
    }
        break;
    default:
        return false;
    }
    return true;
}

bool Interface::LockByBlank(int nType)   // 0:idle delay close;1:lid close;2:lowpower close
{
    qDebug()<<"LockByBlank:"<<nType;
    bool isNeedLock = true;
    m_isNewBlankInterface = true;
    if (!checkStatus(SESSION_STATUS_SCREENCLOSE)) {
        qDebug()<<"m_nStatus:"<<m_nStatus<<" can't do screenclose lock!";
        if (nType == 0) {
            int closeDisplayTime = m_sessionWatcher->closeActivationDelay();
            int nIdleDelay = m_sessionWatcher->idledelay();
            bool isCloseActive = m_sessionWatcher->isCloseActivationEnable();
            qDebug()<<"DelayState:"<<closeDisplayTime<<nIdleDelay<<isCloseActive;
            if (-1 == closeDisplayTime || !isCloseActive)
                return false;
            if (-1 != nIdleDelay && closeDisplayTime == nIdleDelay*60) { // 屏保与关屏时间一致
                if (!m_sessionWatcher->isLockEnable()) {   // 仅启动屏保
                    isNeedLock = false;
                }
            } else {
                return false;
            }
        } else {
            qDebug()<<"Don't need blank lock:"<<nType;
            return false;
        }
    }
    if(process.state() != QProcess::NotRunning) {
        if (isNeedLock) {
            QDBusMessage message;
            message = QDBusMessage::createSignal(SS_DBUS_PATH,
                                                 SS_DBUS_INTERFACE,
                                                 "SecondRunParam");
            message<<"CloseLock";
            QDBusConnection::sessionBus().send(message);
            return true;
        } else {
            return false;
        }
    } else {
        qDebug() << "LockByBlank ---";
        lockState = false;
        slpState = false;
        if (m_sessionWatcher->locktimeout() != -1 && isNeedLock) {
            delayLockScreen();
            return false;
        } else {
            QString cmd = QString("/usr/bin/ukui-screensaver-dialog --blank --delay %1 --has-lock %2").arg(2000).arg(isNeedLock?1:0);
            qDebug() << cmd;
            process.start(cmd);
            emitLockState(true);
        }
        return true;
    }
}

void Interface::delayLockScreen()
{
    if (!m_timerLock) {
        m_timerLock = new QTimer(this);
        connect(m_timerLock, &QTimer::timeout, this, &Interface::onLockScreenTimeout);
    }
    qDebug()<<"LockTimeout:"<<m_sessionWatcher->locktimeout();
    if (m_sessionWatcher->locktimeout() != -1) {
        stopDelayLockScreen();
        m_timerLock->start(m_sessionWatcher->locktimeout()*1000);
    }
}

void Interface::stopDelayLockScreen()
{
    if (m_timerLock && m_timerLock->isActive()) {
        m_timerLock->stop();
    }
}

void Interface::onLockScreenTimeout()
{
    Lock();
    m_timerLock->stop();
}
