/*
    SPDX-FileCopyrightText: 2020 Alexander Trufanov <trufanovan@gmail.com>
    SPDX-License-Identifier: GPL-2.0-or-later
*/

#include "nodeoperations.h"

#include <QThread>

namespace kt
{
FNode *NodeOperations::getChild(FNode *root, const QString &name, bool is_dir)
{
    FNode *child = root->first_child;
    while (child && (child->name != name || child->is_dir != is_dir)) {
        child = child->next;
    }
    return child;
}

FNode *NodeOperations::addChild(FNode *root, const QString &name, bool is_dir)
{
    FNode *n = new FNode();
    n->parent = root;
    n->name = name;
    n->is_dir = is_dir;
    if (!root->first_child) {
        root->first_child = n;
    } else {
        FNode *last_child = root->first_child;
        while (last_child->next)
            last_child = last_child->next;
        last_child->next = n;
        n->prev = last_child;
    }
    return n;
}

void NodeOperations::removeNode(FNode *n)
{
    while (n->first_child) {
        removeNode(n->first_child);
    }

    if (n->parent) {
        if (n->parent->first_child == n) {
            n->parent->first_child = n->next;
        }
    }

    if (n->prev) {
        n->prev->next = n->next;
    }

    if (n->next) {
        n->next->prev = n->prev;
    }

    free(n);
}

FNode *NodeOperations::makePath(FNode *root, const QString &fname, bool is_dir)
{
    int idx = fname.indexOf(QLatin1Char('/'));
    FNode *existing;

    if (idx == -1) {
        existing = getChild(root, fname, is_dir);
        if (existing)
            return existing;
        return addChild(root, fname, is_dir);
    } else {
        existing = getChild(root, fname.left(idx), true);
        if (!existing)
            existing = addChild(root, fname.left(idx), true);
        return makePath(existing, fname.right(fname.size() - 1 - idx), is_dir);
    }
}

FNode *NodeOperations::findChild(FNode *root, const QString &fname, bool is_dir)
{
    int idx = fname.indexOf(QLatin1Char('/'));
    if (idx == -1) {
        return getChild(root, fname, is_dir);
    } else {
        FNode *n = getChild(root, fname.left(idx), true);
        if (n)
            n = findChild(n, fname.right(fname.size() - 1 - idx), is_dir);
        return n;
    }
}

void NodeOperations::fillFromDir(FNode *root, const QDir &dir)
{
    if (QThread::currentThread()->isInterruptionRequested()) {
        return;
    }

    // QStringLists must be const to suppress "warning: c++11 range-loop might detach Qt container"
    const QStringList sl_f = dir.entryList(QDir::NoDotAndDotDot | QDir::Files | QDir::Hidden);
    for (const QString &s : sl_f) {
        addChild(root, s, false);
    }

    const QStringList sl_d = dir.entryList(QDir::NoDotAndDotDot | QDir::Dirs | QDir::Hidden);
    QDir next_dir;
    for (const QString &s : sl_d) {
        FNode *d = addChild(root, s, true);
        next_dir.setPath(dir.path() + QLatin1String("/") + s);
        fillFromDir(d, next_dir);
    }
}

void NodeOperations::subtractTreesOnFiles(FNode *tree1, FNode *tree2)
{
    if (QThread::currentThread()->isInterruptionRequested()) {
        return;
    }

    FNode *c = tree2->first_child;
    while (c) {
        FNode *f = getChild(tree1, c->name, c->is_dir);
        if (f) {
            if (c->is_dir)
                subtractTreesOnFiles(f, c);
            else
                removeNode(f);
        }
        c = c->next;
    }
}

void NodeOperations::pruneEmptyFolders(FNode *start_folder)
{
    FNode *c = start_folder->first_child;
    while (c) {
        if (c->is_dir)
            pruneEmptyFolders(c);
        c = c->next;
    }

    if (!start_folder->first_child) {
        removeNode(start_folder);
    }
}

void NodeOperations::pruneEmptyFolders(FNode *tree1, FNode *tree2)
{
    if (QThread::currentThread()->isInterruptionRequested()) {
        return;
    }

    FNode *c = tree2->first_child;
    while (c) {
        if (c->is_dir) {
            FNode *f = getChild(tree1, c->name, c->is_dir);
            if (f) {
                pruneEmptyFolders(f, c);
            }
        }
        c = c->next;
    }

    if (!tree2->first_child) {
        pruneEmptyFolders(tree1);
    }
}

void NodeOperations::printTree(FNode *root, const QString &path, QSet<QString> &set)
{
    if (QThread::currentThread()->isInterruptionRequested()) {
        return;
    }

    QString new_path;
    if (!root->name.isEmpty()) {
        new_path = path + QLatin1String("/") + root->name;
        set += new_path;
    }

    FNode *c = root->first_child;

    while (c) {
        if (c->is_dir) {
            printTree(c, new_path, set);
        } else {
            set += new_path + QLatin1String("/") + c->name;
        }
        c = c->next;
    }
}

void NodeOperations::printTree(FNode *root, QSet<QString> &set)
{
    QString path;
    printTree(root, path, set);
}

}
