/*
 * Copyright 2021 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 2 of the License, 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, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "updatedbus.h"
#include "connection.h"
#include <QMutexLocker>
#define PROGRAM_NAME "control-upgrade"
#define PATH_MAX_LEN 1024
#define PID_STRING_LEN 64

UpdateDbus* UpdateDbus::updateMutual = nullptr;
using namespace std;

UpdateDbus* UpdateDbus::getInstance(QObject *parent)
{
    static QMutex mutex;
    if(nullptr == updateMutual)
    {
        QMutexLocker locker(&mutex);
        updateMutual = new UpdateDbus(parent);
    }
    return updateMutual;
}

UpdateDbus::UpdateDbus(QObject *parent)
    :QObject(parent)
{
    qRegisterMetaType<AppMsg>("AppMsg"); //注册信号槽类型
    qRegisterMetaType<AppAllMsg>("AppAllMsg"); //注册信号槽类型
    interface = new QDBusInterface(KYLIN_UPDATE_MANAGER_SERVICE,
                                   KYLIN_UPDATE_MANAGER_PATH,
                                   KYLIN_UPDATE_MANAGER_INTERFACE,
                                   QDBusConnection::systemBus());
    QDBusConnection::systemBus().connect(QString("cn.kylinos.KylinUpdateManager"), QString("/cn/kylinos/KylinUpdateManager"),
                                         QString("cn.kylinos.KylinUpdateManager"),
                                         QString("kum_apt_signal"), this, SLOT(getAptSignal(QString, QMap<QString, QVariant>)));

    QDBusConnection::systemBus().connect(QString("cn.kylinos.KylinUpdateManager"), QString("/cn/kylinos/KylinUpdateManager"),
                                         QString("cn.kylinos.KylinUpdateManager"),
                                         QString("important_app_message_signal"), this, SLOT(getAppMessageSignal(QMap<QString, QVariant>, QStringList, QStringList, QStringList, QStringList, QString, bool)));

    QDBusConnection::systemBus().connect(QString("cn.kylinos.KylinUpdateManager"), QString("/cn/kylinos/KylinUpdateManager"),
                                         QString("cn.kylinos.KylinUpdateManager"),
                                         QString("get_message_finished_signal"), this, SLOT(slotFinishGetMessage(QString)));

    QDBusConnection::systemBus().connect(QString("cn.kylinos.KylinUpdateManager"), QString("/cn/kylinos/KylinUpdateManager"),
                                         QString("cn.kylinos.KylinUpdateManager"),
                                         QString("copy_finish"), this, SLOT(slotCopyFinished(QString)));

//    cleanUpdateList();
    setImportantStatus(true);

}
void UpdateDbus::disconnectDbusSignal()
{
    QDBusConnection::systemBus().disconnect(QString("cn.kylinos.KylinUpdateManager"), QString("/cn/kylinos/KylinUpdateManager"),
                                         QString("cn.kylinos.KylinUpdateManager"),
                                         QString("kum_apt_signal"), this, SLOT(getAptSignal(QString, QMap<QString, QVariant>)));

    QDBusConnection::systemBus().disconnect(QString("cn.kylinos.KylinUpdateManager"), QString("/cn/kylinos/KylinUpdateManager"),
                                         QString("cn.kylinos.KylinUpdateManager"),
                                         QString("important_app_message_signal"), this, SLOT(getAppMessageSignal(QMap<QString, QVariant>, QStringList, QStringList, QStringList, QStringList, QString, bool)));

    QDBusConnection::systemBus().disconnect(QString("cn.kylinos.KylinUpdateManager"), QString("/cn/kylinos/KylinUpdateManager"),
                                         QString("cn.kylinos.KylinUpdateManager"),
                                         QString("get_message_finished_signal"), this, SLOT(slotFinishGetMessage(QString)));

    QDBusConnection::systemBus().disconnect(QString("cn.kylinos.KylinUpdateManager"), QString("/cn/kylinos/KylinUpdateManager"),
                                         QString("cn.kylinos.KylinUpdateManager"),
                                         QString("copy_finish"), this, SLOT(slotCopyFinished(QString)));
}
void UpdateDbus::SetDownloadLimit(QString value,bool whetherlimit)
{
    interface->call("set_downloadspeed_max",value,whetherlimit);
}

int UpdateDbus::GetDownloadLimit(void)
{
    QDBusPendingReply<int> reply = interface->call("get_downloadspeed_limit_value");
    if (!reply.isValid())
    {
        qDebug()<<"error getting download speed limit value";
        return -1;
    }
    if (reply.argumentAt(0)==true)
    {
        return reply.argumentAt(1).toInt();
    }
    else
    {
        return -2;
    }
}

void UpdateDbus::onRequestSendDesktopNotify(QString message)
{
    QDBusInterface iface("org.freedesktop.Notifications",
                         "/org/freedesktop/Notifications",
                         "org.freedesktop.Notifications",
                         QDBusConnection::sessionBus());
    if (!notifyMsg.compare(message)) {
        return ;
    }
    notifyMsg = message;
    QList<QVariant> args;
    args<<(tr("System-Upgrade"))
       <<((unsigned int) 0)
      <<("ukui-control-center")
     <<tr("ukui-control-center-update") //显示的是什么类型的信息  控制面板-更新提示
    <<message //显示的具体信息
    <<QStringList()
    <<QVariantMap()
    <<(int)-1;
    iface.callWithArgumentList(QDBus::AutoDetect,"Notify",args);
}

bool UpdateDbus::fileLock()
{
    int uid = getuid();
//    struct passwd *pwd = getpwuid(uid);
    QString nameuid = QString("%1\n").arg(uid);
    QByteArray str2char1 = nameuid.toLatin1(); // must
    char* charnameuid = str2char1.data();
    qDebug()<<"文件锁打开 用户ID：" << charnameuid;

    //判断是否有lock目录，没有则创建
    QDir dir("/tmp/lock/");
    if(! dir.exists()) {
        dir.mkdir("/tmp/lock/");//只创建一级子目录，即必须保证上级目录存在
        chmod("/tmp/lock/",0777);
    }
    //预设运行程序名称
    char name_string[25] = {"ukui-control-center\n"};
    //预设操作类型
    char operationType[14] = {"install\n"};
    //预设系统当前时间
    QDateTime time = QDateTime::currentDateTime();                     //获取系统现在的时间
    QString str = time.toString("yyyy-MM-dd hh:mm:ss");                //设置显示格式
    QByteArray str2char2 = str.toLatin1(); // must
    char* nowtime = str2char2.data();

    umask(0000);
    //O_TRUNC 为先清空，再写入
    int fd = open(lockPath.toUtf8().data(), O_RDWR | O_CREAT | O_TRUNC,0666);
    if (fd < 0) {
        qDebug()<<"文件锁打开异常";
        return false;
    } else {
        write(fd, charnameuid, strlen(charnameuid));
        write(fd, name_string, strlen(name_string));
        write(fd, operationType, strlen(operationType));
        write(fd, nowtime, strlen(nowtime));
    }
    return flock(fd, LOCK_EX | LOCK_NB);
}

void UpdateDbus::fileUnLock()
{
    QDir dir("/tmp/lock/");
    if(! dir.exists()) {
        dir.mkdir("/tmp/lock/");//只创建一级子目录，即必须保证上级目录存在
        chmod("/tmp/lock/",0777);
    }
    umask(0000);
    int fd = open(lockPath.toUtf8().data(), O_RDWR | O_CREAT,0666);
    if (fd < 0) {
        qDebug()<<"解锁时文件锁打开异常";
        return;
    }
    flock(fd, LOCK_UN);
    system("rm /tmp/lock/kylin-update.lock");
}

void UpdateDbus::slotFinishGetMessage(QString num)
{
    int inum = num.toInt();
    emit sendFinishGetMsgSignal(inum);
}


//拷贝软件包到安装目录，拷贝之前需要判断是否存在archives目录
void UpdateDbus::copyFinsh(QStringList srcPath, QString appName)
{
    QDir dir(QString("/var/cache/apt/archives/"));
    if(!dir.exists())
    {
        makeDirs(QString("/var/cache/apt/archives/"));
    }
    replyStr = interface->call("copy_file_to_install",srcPath,appName);
    qDebug() << "拷贝软件包到安装目录，调用接口copy_file_to_install";
}

bool UpdateDbus::makeDirs(QString path)
{
    // 有参数的情况下  传参调用dbus接口并保存返回值
    replyBool = interface->call("makedirs", path);

    // 将reply.value()作为返回值
    if (replyBool.isValid()) {
        qDebug() << "makeDirs" << replyBool.value();
        return replyBool.value();
    }
    else{
        qDebug() << QString("调用失败： makeDirs");
        return false;
    }
}

// setImportantStatus
void UpdateDbus::setImportantStatus(bool status)
{
    interface->asyncCall("set_important_status", status);
    qDebug() <<"更新管理器-设置状态值" <<"setImportantStatus:"<<status;
}

//安装和升级
bool UpdateDbus::installAndUpgrade(QString pkgName)
{
    // 有参数的情况下  传参调用dbus接口并保存返回值
    interface->asyncCall("install_and_upgrade",pkgName);

    // 将reply.value()作为返回值
    return true;
}



void UpdateDbus::modifyConf(QString path, QString group, QString key, QString value)
{
    replyStr = interface->call("modify_conf",path,group,key,value);
    qDebug() << QString("调用 modify_conf接口");

}

void UpdateDbus::cleanUpdateList()
{
    qDebug() << "更新管理器清除列表：cleanUpdateList";
    interface->asyncCall("clear_install_list");
}

//初始化cache
void UpdateDbus::init_cache()
{

    interface->call("init_cache");
    qDebug() << " call init_cache";
}

void UpdateDbus::getInameAndCnameList(QString arg)
{

    qDebug()<<"getsignal";
    qDebug()<<arg;
    //调用远程的value方法
    QDBusReply<QStringList> reply = interface1->call("getImportant");
    if (reply.isValid()) {
        inameList = reply.value();
        qDebug() << inameList;
//        qDebug() <<  value;
    } else {
        qDebug() << "value method 调用失败!";
    }
//    emit sendImportant();

}

void UpdateDbus::getAppMessageSignal(QMap<QString, QVariant> map, QStringList urlList, QStringList nameList,QStringList fullnameList,QStringList sizeList, QString allSize, bool dependState)
{
    QVariant dateQVariant;
    AppAllMsg appAllMsg;
    QVariantMap::Iterator it;
    for (it = map.begin(); it != map.end(); ++it) {
        if (it.key() == "appname")
        {
            dateQVariant = it.value();
            appAllMsg.name = dateQVariant.toString();
        }
        if(it.key() == "current_version")
        {
            dateQVariant = it.value();
            appAllMsg.version = dateQVariant.toString();
        }
        if(it.key() == "source_version")
        {
            dateQVariant = it.value();
            appAllMsg.availableVersion = dateQVariant.toString();
        }
        if(it.key() == "size")
        {
            dateQVariant = it.value();
            appAllMsg.packageSize = dateQVariant.toString().toLong();
        }
        if(it.key() == "description")
        {
            dateQVariant = it.value();
            appAllMsg.longDescription = dateQVariant.toString();
        }
    }
    if(urlList.length() != 0)
    {
        for(int i = 0; i < urlList.length(); i++)
        {
            UrlMsg msg;
            msg.url = urlList.at(i);
            msg.name = nameList.at(i);
            msg.fullname = fullnameList.at(i);
            QString size = sizeList.at(i);
            msg.size = size.toLong();
            appAllMsg.msg.depList.append(msg);
        }
    }
    appAllMsg.msg.allSize = allSize.toLong();
    appAllMsg.msg.getDepends = dependState;
//    qDebug() << "获取信息" << appAllMsg.name << appAllMsg.longDescription;
    emit sendAppMessageSignal(appAllMsg);
}

void UpdateDbus::insertInstallStates(QString item,QString info)
{
    interface->asyncCall("insert_install_state",item,info);
}

QStringList UpdateDbus::checkInstallOrUpgrade(QStringList list)
{
    replyStrList = interface->call("check_installed_or_upgrade",list);
    if (replyStrList.isValid()) {
        return replyStrList.value();
    }
    else{
        QStringList error;
        qDebug() << QString("check_installed_or_upgrade接口调用失败！");
        return error;
    }
}

void UpdateDbus::getAppMessage(QStringList list)
{
    interface->asyncCall("get_app_message",list);
    qDebug() << "调用 get_app_message";
}


void UpdateDbus::getAptSignal(QString arg, QMap<QString, QVariant> map)
{

    QString aptStatus;
    QString aptAppName;
    QString errorMessage;
    float aptPercent = 0;

    QVariant dateQVariant;
    aptStatus = arg;

    qDebug() << "安装状态" << arg;

    QVariantMap::Iterator it;
    for (it = map.begin(); it != map.end(); ++it) {
        if (it.key() == "apt_appname")
        {
            dateQVariant = it.value();
            aptAppName = dateQVariant.toString();
        }
        if(it.key() == "apt_percent")
        {
            dateQVariant = it.value();
            aptPercent = dateQVariant.toFloat();
        }
        if(it.key() == "error_message")
        {
            dateQVariant = it.value();
            errorMessage = dateQVariant.toString();
        }
    }

    qDebug() << "aptAppName:" << aptAppName;
    qDebug() << "aptPercent:" << aptPercent;

    emit transferAptProgress(aptStatus,aptAppName,aptPercent,errorMessage);

}

void UpdateDbus::slotCopyFinished(QString appName)
{
    emit copyFinish(appName);
}


UpdateDbus::~UpdateDbus()
{
//    qDebug() << "update quit------>";
    cleanUpdateList();
    setImportantStatus(false);
    fileUnLock();
}
