/* common/kmfile.cpp
 *
 * Copyright (C) 2023 OpenKylin, Inc
 */

#include "KMFile.h"

#include <glib.h>

class KMFile::Private
{
public:
    Private(GFile *file);
    ~Private();

    GFile *m_file = nullptr;
    GError *m_error = nullptr;
};

KMFile::Private::Private(GFile *file)
{
    m_file = file;
}

KMFile::Private::~Private()
{
    if (m_file)
    {
        g_object_unref(m_file);
    }
}

KMFile::KMFile()
{
}

KMFile::KMFile(const std::string &filename)
{
    GFile *file = g_file_new_for_path(filename.c_str());

    if (file)
    {
        d = std::make_shared<Private>(file);
    }
}

// 不进行深拷贝
KMFile::KMFile(const KMFile &other)
{
    d = other.d;
}

KMFile::KMFile(KMFile &&other)
{
    d = other.d;
}

KMFile::KMFile(GFile *file)
{
    if (file)
    {
        d = std::make_shared<Private>(file);
    }
}

KMFile::~KMFile()
{
}

// 不进行深拷贝
KMFile &KMFile::operator=(const KMFile &other)
{
    if (this != &other && d->m_file != other.d->m_file)
    {
        d = other.d;
    }

    return *this;
}

KMFile &KMFile::operator=(KMFile &&other)
{
    d = other.d;

    return *this;
}

KMFile &KMFile::operator=(GFile *file)
{
    if (file)
    {
        if (d)
        {
            if (d->m_file == file)
            {
                // do nothing
            }
            else if (d->m_file)
            {
                g_object_unref(d->m_file);
                d->m_file = file;
            }
        }
        else
        {
            d = std::make_shared<Private>(file);
        }
    }
    else
    {
        d.reset();
    }

    return *this;
}

GFile *KMFile::get()
{
    if (d)
    {
        return d->m_file;
    }
    else
    {
        return nullptr;
    }
}

std::string KMFile::errorMessage() const
{
    if (d && d->m_error)
    {
        return d->m_error->message;
    }
    else
    {
        return "";
    }
}

int KMFile::errorCode() const
{
    if (d && d->m_error)
    {
        return d->m_error->code;
    }
    else
    {
        return -1;
    }
}

bool KMFile::queryExists()
{
    if (d && d->m_file)
    {
        return g_file_query_exists(d->m_file, nullptr);
    }
    else
    {
        return false;
    }
}

/*
将文件的内容加载到内存中。数据总是以零结尾，但这不包括在结果长度中。
*/
bool KMFile::loadContents(std::string &contents)
{
    // 当不再需要时，应使用g_free（）释放返回的内容。
    g_autofree char *tmp_contents = NULL;
    gsize length = 0;
    bool ret = g_file_load_contents(d->m_file, nullptr, &tmp_contents, &length, nullptr, &d->m_error);
    if (ret)
    {
        contents = tmp_contents;
    }

    return ret;
}

/*
获取所请求的有关指定文件的信息。结果是一个KMFileInfo对象，它包含键值属性（例如文件的类型或大小）。

attributes值是一个字符串，用于指定应收集的文件属性。如果无法从文件中读取特定请求的属性，这就不是错误——只是不会设置它。
属性应该是以逗号分隔的属性列表或属性通配符。通配符“*”表示所有属性，像“standard::*”这样的通配符表示标准名称空间中的所有属性。
一个示例属性查询是“standard::*，owner::user”。标准属性可按定义使用，如G_FILE_ATTRIBUTE_STANDARD_NAME。

如果cancellable不为NULL，则可以通过从另一个线程触发可取消对象来取消操作。如果操作被取消，将返回错误G_IO_ERROR_CANCELLED。

对于符号链接，通常会返回关于符号链接目标的信息，而不是关于符号链接本身的信息。但是，如果在标志中传递G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS，则将返回有关符号链接本身的信息。
此外，对于指向不存在文件的符号链接，将返回有关符号链接本身的信息。

如果文件不存在，将返回G_IO_ERROR_NOT_FOUND错误。其他错误也是可能的，这取决于文件所在的文件系统类型。
*/
KMFileInfo KMFile::queryInfo(const std::string &attributes, GFileQueryInfoFlags flags)
{
    if (d)
    {
        return g_file_query_info(d->m_file, attributes.c_str(), flags, nullptr, &d->m_error);
    }
    else
    {
        return KMFileInfo(true);
    }
}

/*
获取所请求的有关目录中文件的信息。结果是一个GFileEnumerator对象，它将为目录中的所有文件提供GFileInfo对象。

attributes值是一个字符串，用于指定应收集的文件属性。如果无法从文件中读取特定请求的属性，这就不是错误——只是不会设置它。
属性应该是以逗号分隔的属性列表或属性通配符。通配符“*”表示所有属性，像“standard::*”这样的通配符表示标准名称空间中的所有属性。
一个示例属性查询是“standard::*，owner::user”。标准属性可按定义使用，如G_FILE_ATTRIBUTE_STANDARD_NAME。

如果cancellable不为NULL，则可以通过从另一个线程触发可取消对象来取消操作。如果操作被取消，将返回错误G_IO_ERROR_CANCELLED。

如果文件不存在，将返回G_IO_ERROR_NOT_FOUND错误。如果文件不是目录，将返回G_IO_ERROR_NOT_DIRECOTRY错误。其他错误也是可能的。
*/
KMFileEnumerator KMFile::children(const char *attributes, GFileQueryInfoFlags flags)
{
    if (d)
    {
        return g_file_enumerate_children(d->m_file, attributes, flags, nullptr, &d->m_error);
    }
    else
    {
        return KMFileEnumerator();
    }
}

/*
获取basename等于name的文件的子级。

请注意，具有该特定名称的文件可能不存在，但您仍然可以有一个指向它的GFile。例如，您可以使用它来创建该文件。

此调用不阻止I/O。
*/
KMFile KMFile::getChild(const std::string &name)
{
    if (d)
    {
        return g_file_get_child(d->m_file, name.c_str());
    }
    else
    {
        return KMFile();
    }
}

/*
    返回m_file对应的文件路径
*/
std::string KMFile::getPath()
{
    if (d)
    {
        g_autofree char *path = g_file_get_path(d->m_file);
        if (path)
        {
            return path;
        }
    }

    return "";
}
