/*
 * Copyright 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 (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 <https://www.gnu.org/licenses/>.
 */

#include "appinfo.h"
#include <QCryptographicHash>
#include <QDateTime>
#include <KWindowSystem>
#include <windowmanager/windowmanager.h>
#include "common.h"

namespace appinfo {

AppInfo::AppInfo(QObject *parent) : QObject(parent)
{

}

AppInfo::AppInfo(const QString &id,
                 const QString &desktopFile,
                 AppType appType,
                 QObject *parent)
    : QObject(parent)
    , m_appId(id)
    , m_desktopFile(desktopFile)
    , m_appType(appType)
{

}

AppInfo::AppInfo(const appinfo::AppInfo &other)
    : AppInfo(other.parent())
{
    this->m_instances = other.m_instances;
    this->m_appId = other.m_appId;
    this->m_desktopFile = other.m_desktopFile;
    this->m_appType = other.m_appType;
}

bool AppInfo::operator==(const appinfo::AppInfo &other)
{
    return this->m_instances == other.m_instances &&
            this->m_appId == other.m_appId &&
            this->m_desktopFile == other.m_desktopFile &&
            this->m_appType == other.m_appType;
}

AppInfo AppInfo::operator=(const AppInfo &other)
{
    this->m_instances = other.m_instances;
    this->m_appId = other.m_appId;
    this->m_desktopFile = other.m_desktopFile;
    this->m_appType = other.m_appType;
    return *this;
}

QString AppInfo::appId() const
{
    return m_appId;
}

QString AppInfo::desktopFile() const
{
    return m_desktopFile;
}

AppType AppInfo::appType() const
{
    return m_appType;
}

QString AppInfo::cgroupName(const QString &instName) const
{
    for (auto const &instance : qAsConst(m_instances)) {
        if (instance.instName == instName) {
            return instance.cgroupName;
        }
    }
    return QString();
}

QStringList AppInfo::cgroupNames(Policy::AppStatus status) const
{
    QStringList cgroupNames;
    for (auto const &instance : qAsConst(m_instances)) {
        if (instance.appStatus == status) {
            cgroupNames.push_back(instance.cgroupName);
        }
    }
    return cgroupNames;
}

void AppInfo::setInstanceStatus(const QString &name, Policy::AppStatus status)
{
    for (auto &instance : m_instances) {
        if (instance.instName == name || instance.cgroupName == name) {
            instance.appStatus = status;
            return;
        }
    }
}

int AppInfo::instanceStatus(const QString &name)
{
    for (auto &instance : m_instances) {
        if (instance.instName == name || instance.cgroupName == name) {
            return instance.appStatus;
        }
    }
    return -1;
}

void AppInfo::setInstanceRunningStatus(const QString &name, RunningStatus status)
{
    for (auto &instance : m_instances) {
        if (instance.instName == name || instance.cgroupName == name) {
            instance.appRunningStaus = status;
            return;
        }
    }
}

void AppInfo::setInstanceAppType(const QString &name, AppType type)
{
    for (auto &instance : m_instances) {
        if (instance.instName == name || instance.cgroupName == name) {
            instance.appType = type;
            return;
        }
    }
}

QString AppInfo::mprisDbus(const QString &instName) const
{
    for (auto const &instance : qAsConst(m_instances)) {
        if (instance.instName == instName) {
            return instance.mprisDbus;
        }
    }
    return QString();
}

void AppInfo::setMprisDbus(const QString &instName, const QString &mprisDBusName)
{
    for (auto &instance : m_instances) {
        if (instance.instName == instName) {
            instance.mprisDbus = mprisDBusName;
            return;
        }
    }
}

quint64 AppInfo::instanceTimerId(const QString &instName) const
{
    for (auto &instance : m_instances) {
        if (instance.instName == instName) {
            return instance.timerId ;
        }
    }
    return 0;
}

void AppInfo::setInstanceTimerId(const QString &instName, quint64 timerId)
{
    for (auto &instance : m_instances) {
        if (instance.instName == instName) {
            instance.timerId = timerId;
            return;
        }
    }
}

bool AppInfo::appInstanceIsHidden(const QString &instName) const
{
    if (instName.isEmpty()) {
        return false;
    }

    for (auto const &inst : m_instances) {
        if (inst.instName != instName) {
            continue;
        }

        for (auto const &wid : inst.wids) {
            if (Policy::isWaylandPlatform()) {
                auto windowInfo = kdk::WindowManager::getwindowInfo(wid);
                return windowInfo.isMinimized();
            }
            KWindowInfo windowInfo(wid, NET::WMState | NET::XAWMState);
            if (!windowInfo.hasState(NET::Hidden)) {
                return false;
            }
        }

        return true;
    }

    return true;
}

QList<appinfo::AppInstance> AppInfo::instances() const
{
    return m_instances;
}

void AppInfo::appendAppInstance(const AppInstance &inst)
{
    m_instances.append(inst);
}

void AppInfo::removeAppInstance(const QString &inst)
{
    if (inst.isEmpty()) {
        qWarning() << current_func << "id or inst is inValid!" << inst;
        return;
    }
    for (int i=0; i<m_instances.count(); ++i) {
        if (m_instances.at(i).instName == inst) {
            m_instances.removeAt(i);
            return;
        }
    }
}

void AppInfo::appendAppWid(const QString &inst, const quint32 &wid)
{
    if (inst.isEmpty() || wid <= 0) {
        qWarning() << current_func << "id、inst or wid is inValid!" << inst << wid;
        return;
    }

    for (auto &instance : m_instances) {
        if (instance.instName == inst) {
            if (instance.wids.contains(wid)) {
                continue;
            }
            instance.wids.push_back(wid);
            qDebug() << current_func << inst << instance.wids;
            return;
        }
    }
}

QList<quint32> AppInfo::appInstanceWids(const QString &inst) const
{
    if (inst.isEmpty()) {
        return QList<quint32>();
    }

    for (auto const &instance : m_instances) {
        if (instance.instName == inst) {
            return instance.wids;
        }
    }
    return QList<quint32>();
}

void AppInfo::appendPid(const QString &inst, const int &pid)
{
    if (inst.isEmpty() || pid <= 0) {
        qWarning() << current_func << "inst or wid is inValid!" << inst << pid;
        return;
    }

    for (auto &instance : m_instances) {
        if (instance.instName == inst) {
            if (instance.pids.contains(pid)) {
                continue;
            }
            instance.pids.push_back(pid);
            qDebug() << current_func << inst <<  instance.pids;
            return;
        }
    }
}

void AppInfo::removePid(const QString &inst, const int &pid)
{
    if (inst.isEmpty() || pid <= 0) {
        qWarning() << current_func << "inst or pid is inValid!" << inst << pid;
        return;
    }

    for (auto &instance : m_instances) {
        if (instance.instName == inst) {
            instance.pids.removeOne(pid);
            return;
        }
    }
}

QList<int> AppInfo::pids(const QString &inst) const
{
    if (inst.isEmpty()) {
        qWarning() << current_func << "inst is inValid!" << inst;
        return QList<int>();
    }

    for (auto const &instance : m_instances) {
        if (instance.instName == inst) {
            return instance.pids;
        }
    }
    return QList<int>();
}

void AppInfo::removeAppWid(const QString &inst, const quint32 &wid)
{
    if (inst.isEmpty() || wid <= 0) {
        qWarning() << current_func << "inst or wid is inValid!" << inst << wid;
        return;
    }
    for (auto &instance : m_instances) {
        if (instance.instName == inst) {
            instance.wids.removeOne(wid);
            return;
        }
    }
}

}// namespace appinfo
