#include "datadirectory.h"
#include "src/po/translation.h"
#include "src/frame/configuration.h"
#include "src/view/execdialog.h"
#include "src/view/modaldialog.h"
#include "src/view/mainwindow.h"

#include <QObject>
#include <QDir>
#include <QCoreApplication>
#include <QDebug>
#include <QScopedPointer>
#include <windowmanager/windowmanager.h>

bool DataDirectory::g_bFilterHidden = false;
bool DataDirectory::g_bFilterBroken = false;//是否执行损坏连接文件过滤
bool DataDirectory::g_bRepleace = false;//是否执行连接文件替换

void DataDirectory::setFilterHidden(bool filter)
{
    DataDirectory::g_bFilterHidden = filter;
}

void DataDirectory::setFilterBrokenSymbolLinks(bool filter)
{
    DataDirectory::g_bFilterBroken = filter;
}

void DataDirectory::setRepleaceSymbolLinks(bool filter)
{
    DataDirectory::g_bRepleace = filter;
}

DataDirectory::DataDirectory(QString path, QObject *parent, bool fromDisk) :
    DataFile(path, parent, fromDisk)
{
    DataDirectory *dir;
    m_oChildren.clear();
    m_bFilterBroken = DataDirectory::g_bFilterHidden;
    m_bFilterHidden = DataDirectory::g_bFilterBroken;
    m_bRepleace = DataDirectory::g_bRepleace;
    m_bIsDirectory = true;
    m_oHashChildren.clear();
    m_u64Files = 0;
    m_u64Dirs = 0;
    m_u64NewDataSize = 0;


    dir = qobject_cast<DataDirectory *>(parent);
    if (dir) m_bIsRoot = false;
    else m_bIsRoot = true;

    if (!m_bIsRoot)
    {
        m_bFilterHidden = dir->filterHidden();
        qDebug()<<__LINE__<<__func__<<m_bFilterHidden;
        m_bFilterBroken = dir->filterBrokenSymbolLinks();
        m_bRepleace = dir->repleaceSymbolLinks();
    }

    if (m_bIsRoot){
        m_u64Size = 0;
    }
    if (".." == m_oDisplayName)
    {
        m_oIcon = QIcon::fromTheme("go-up-symbolic");
        m_oSource = _("Return to parent level.");
    }

    if (!m_bIsRoot && m_oSource == m_oDestination)
    {
        m_oIcon = dir->icon();
        m_oType = _("NewFolder");
        m_u32Level = dir->level() + 1;
        m_oSource.clear();
    }
#if 1
    //为每个非根文件夹中加一个“..”文件。
    if (!m_bIsRoot && ".." != m_oDisplayName)
    {
        path.append("/..");
        dir = new DataDirectory(path, this);
    }
#endif
    if (!m_bIsRoot) child(this, true);

}

DataFile *DataDirectory::at(int row)
{
    if (row < 0) return nullptr;
    if (row < m_oChildren.size())
        return m_oChildren.at(row);
    return nullptr;
}

int DataDirectory::of(DataFile *item)
{
    return m_oChildren.indexOf(item);
}

int DataDirectory::of(DataDirectory *dir)
{
    DataFile *item;
    item = static_cast<DataFile *>(dir);
    return of(item);
}


//移除文件
bool DataDirectory::take(DataFile *file)
{
    int idx;

    idx = m_oChildren.indexOf(file);
    if (-1 == idx){
        qDebug()<<__LINE__<<__func__<<idx;
        return false;
    }
    m_oChildren.takeAt(idx);
    //qDebug()<<__LINE__<<__func__<<idx;
    m_oHashChildren.remove(file->display());
    //qDebug()<<__LINE__<<__func__<<file->display();
    return true;
}

void DataDirectory::append(DataFile *d)
{
    int suffix;
    bool renamed;
    DataFile *old;
    QString newName;
    QFileInfo f;

    suffix = 0;
    old = m_oHashChildren.value(d->display());
    if (old)
    {
        debug(_("Duplicate child %1").arg(d->display()));
        switch (CFG->get("DataSetting", "Duplicate Data", 0).toInt())
        {
        case 0:
            do
            {
                QScopedPointer<InputChange> p(new InputChange(MainWindow::Instance()));
                //renamed = EXEC->input(newName, _("Rename"), _("Add new name for %1").arg(d->display()), d->display());
                renamed = p->input(newName, _("Rename"), _("File name duplicate, need to rename:"), d->display());
                if (!renamed) break;
                if (newName.trimmed().isEmpty())
                {
                    EXEC->error(_("Add File(s)") + " - " + _("Empty Name"),
                                _("Cannot named with empty."));
                    continue;
                }
                if (m_oHashChildren.value(newName))
                {
                    EXEC->error(_("Add File(s)") + " - " + _("Duplicate Name"),
                                _("There is data named %1").arg(newName));
                    continue;
                }
                if (newName.length() > 103)
                {
                    EXEC->error(_("Add File(s)") + " - " + _("Name Too Long"),
                                _("Cannot add named length more than 103 unicode charsets."));
                    continue;
                }
                if (renamed) break;
            } while (true);
            if (renamed) { d->rename(newName); break; }
        case 1: // add suffix
            suffix = 0;
            f.setFile(d->destination());
            do
            {
                if (f.suffix().isEmpty())
                {
                    newName = QString("%1_%2").arg(f.baseName()).arg(suffix++);
                    if (newName.length() > NAME_LENGTH)
                    {
                        newName = f.fileName().mid(0, NAME_LENGTH - QString("_%1").arg(suffix).length());
                        newName.append(QString("_%2").arg(suffix));
                    }
                }
                else
                {
                    newName = QString("%1_%2.%3").arg(f.baseName()).arg(suffix++).arg(f.suffix());
                    if (newName.length() > NAME_LENGTH)
                    {
                        newName = f.fileName().mid(0, NAME_LENGTH - QString("_%1.%2").arg(suffix).arg(f.suffix()).length());
                        newName.append(QString("_%1.%2").arg(suffix).arg(f.suffix()).length());
                    }
                }
            } while (m_oHashChildren.value(newName));
            d->rename(newName);
            break;
        default:
            if (!old->isDisk()) delete old;
            break;
        }
    }
    m_oHashChildren.insert(d->display(), d);
    m_oChildren << d;
    debug(_("Add child %1").arg(d->display()));
    return;
}

int DataDirectory::count() const
{
    if (m_bIsRoot) return m_oChildren.count();
    if (m_oChildren.count() == 1 && ".." == m_oChildren.first()->display()) return 0;
    return m_oChildren.count() - 1;
}

bool DataDirectory::canClean() const
{
    int i;
    if (0 == m_oChildren.size()) return false;

    for (i = m_bIsRoot ? 0 : 1; i < m_oChildren.size(); ++i)
    {
        if (!m_oChildren.at(i)->isDisk()) break;
    }
    if (i < m_oChildren.size()) return true;
    return false;
}

bool DataDirectory::duplicate(QString name) const
{
    if (m_oHashChildren.value(name)) return true;
    return false;
}

void DataDirectory::rename(QString name, bool add)
{
    DataDirectory *parent;
    QDir dir;

    if (m_bIsRoot)
    {
        dir.setPath(destination());
        dir.removeRecursively();
        m_oSource = name;
        m_oDestination = name;
        update(m_oDestination);
    }
    else
    {
        parent = static_cast<DataDirectory *>(this->parent());
        if (nullptr == parent) return;
        if (!add)
            parent->updateChild(m_oDisplayName, name);
        m_oDisplayName = name;
        m_oDestination = parent->destination() + "/" + m_oDisplayName;
    }
}

void DataDirectory::updateChild(QString old, QString n)
{
    if (old == n) return;
    if (!m_oHashChildren.value(old)) return;
    m_oHashChildren.insert(n, m_oHashChildren.value(old));
    m_oHashChildren.remove(old);
}

void DataDirectory::clear()
{
    for (int i = m_oChildren.size() - 1; i > -1 && m_oChildren.size(); --i)
    {
        if (m_oChildren.at(i)->isDisk() ||
                ".." == (m_oChildren.at(i))->display())
            continue;
        //删除DataFile指针会造成闪退
        delete m_oChildren.at(i);
    }
}

void DataDirectory::clean()
{
    for (int i = m_oChildren.size() - 1; i > -1 && m_oChildren.size(); --i)
    {
        //删除DataFile指针会造成闪退
        delete m_oChildren.at(i);
    }
}

void DataDirectory::copy()
{
    QDir dir;
    int i;

    if (".." == m_oDisplayName) return;
    if (m_bIsDisk) return;

    dir.setPath(m_oDestination);
    if (!dir.exists()) dir.mkpath(m_oDestination);
    copied(m_oSource, m_oDestination);
    debug(_("Copied dir %1 to %2").arg(m_oSource).arg(m_oDestination));
    for (i = 0; i < m_oChildren.size(); ++i)
    {
        QCoreApplication::processEvents();
        m_oChildren.at(i)->copy();
    }
}

void DataDirectory::execHidden(bool b)
{
    m_bFilterHidden = b;
    qDebug()<<"DataDirectory"<<__func__<<__LINE__<<m_bFilterHidden;
    hidden(b);
}

void DataDirectory::execBroken(bool b)
{
    m_bFilterBroken = b;
    qDebug()<<"DataDirectory"<<__func__<<__LINE__<<m_bFilterBroken;
    broken(b);
}

void DataDirectory::execRepleace(bool b)
{
    m_bRepleace = b;
    qDebug()<<"DataDirectory"<<__func__<<__LINE__<<m_bRepleace;
    repleace(b);
}

void DataDirectory::turnViewFilterFileToHidden()
{
    if (m_oChildren.size() == 0){
        m_bFilterHidden = false;
        execHidden(false);
        qDebug()<<"DataDirectory"<<__func__<<__LINE__<<m_bFilterHidden;
    }
    for (int i = 0; i < m_oChildren.size(); ++i)
    {
        if (m_oChildren.at(i)->attrHidden())
        {
            m_bFilterHidden = true;
            qDebug()<<"DataDirectory"<<__func__<<__LINE__<<m_bFilterHidden;
            return;
        }
    }
    m_bFilterHidden = false;
    execHidden(false);
    qDebug()<<"DataDirectory"<<__func__<<__LINE__<<m_bFilterHidden;
}

void DataDirectory::turnViewFilterFileToBrokenSymbolLinks()
{
    if (m_oChildren.size() == 0) m_bFilterBroken = false;
    for (int i = 0; i < m_oChildren.size(); ++i)
    {
        if (m_oChildren.at(i)->attrSymbolLink())
        {
            m_bFilterBroken = true;
            return;
        }
    }
    m_bFilterBroken = false;
}

void DataDirectory::turnViewFilterFileToRepleaceSymbolLinks()
{
    if (m_oChildren.size() == 0) m_bRepleace = false;
    for (int i = 0; i < m_oChildren.size(); ++i)
    {
        if (m_oChildren.at(i)->attrSymbolLink() && m_oChildren.at(i)->linkTarget())
        {
            m_bRepleace = true;
            return;
        }
    }
    m_bRepleace = false;
}

void *DataDirectory::findChildByDisplay(QString name, bool child)
{
    void *ret;
    DataDirectory *dir;
    DataFile *file;

    ret = m_oHashChildren.value(name);
    if (ret) return ret;
    if (child) return ret; // if child is true, means only find in self chidren
    foreach( file, m_oChildren )
    {
        if (file->isDirectory())
        {
            dir = qobject_cast<DataDirectory *>(file);
            ret = dir->findChildByDisplay(name);
            if (ret) return ret;
        }
    }
    return nullptr;
}

/***************************************************
 * 计算最外层（根目录）下有多少非光盘文件，即新添加文件
 * ************************************************/
int DataDirectory::CountOneLevelNotDiskFile()
{
    DataFile *file;
    int i = 0;
    foreach( file, m_oChildren )
    {
        if (!(file->isDisk()))
        {
              i++;
        }
    }
    return i;
}

void DataDirectory::doSize(unsigned long size, bool a)
{
    DataFile *send;

    send = static_cast<DataFile *>(sender());

    if (a)
    {
        m_u64Size += size;
        if (send && !send->isDisk())
        {
            m_u64NewDataSize += size; 
        }
        debug(_("Add size %1, now size is %2").arg(size).arg(m_u64Size));
    }
    else
    {
        m_u64Size -= size;
        if (send && !send->isDisk())
        {
            m_u64NewDataSize -= size;
        }
        debug(_("Sub size %1, now size is %2").arg(size).arg(m_u64Size));
    }
    this->size(size, a);
    updateSize(m_u64Size);
}

void DataDirectory::doDirectory(bool d, DataFile *item)
{
    if (d)
        ++m_u64Dirs;
    else
        --m_u64Dirs;
    directory(d, item);
    debug(_("Includes %1 dir(s), after %2").arg(m_u64Dirs).arg(d ? _("add") : _("delete")));
}

void DataDirectory::doFile(bool f, DataFile *item)
{
    if (f)
        ++m_u64Files;
    else
        --m_u64Files;
    file(f, item);
    debug(_("Includes %1 file(s), after %2").arg(m_u64Files).arg(f ? _("add") : _("delete")));
}

void DataDirectory::doChild(DataFile *child, bool b)
{
    int idx;
    int suffix;
    bool renamed;
    QHash<QString, DataFile *>::const_iterator it;
    DataFile *old;
    DataDirectory *linked;
    QString newName;
    QFileInfo f;

    idx = m_oChildren.indexOf(child);
    if (b && -1 == idx)
    {
        old = m_oHashChildren.value(child->display());
        if (old)
        {
            // todo if has same name.
            debug(_("Duplicate child %1").arg(child->display()));
            switch (CFG->get("DataSetting", "Duplicate Data", 0).toInt())
            {
            case 0:
                do
                {

                   
                    QScopedPointer<InputChange> p(new InputChange(MainWindow::Instance()));
                  //  renamed = EXEC->input(newName, _("Rename"), _("Add new name for %1").arg(child->display()), child->display());
                    renamed=p->input(newName, _("Rename"), _("File name duplicate, need to rename:"),child->display());

                    if (!renamed) break;
                    if (newName.trimmed().isEmpty())
                    {
                        EXEC->error(_("Add File(s)") + " - " + _("Empty Name"),
                                    _("Cannot named with empty."));
                        continue;
                    }
                    if (m_oHashChildren.value(newName))
                    {
                        EXEC->error(_("Add File(s)") + " - " + _("Duplicate Name"),
                                    _("There is data named %1").arg(newName));
                        continue;
                    }
                    if (newName.length() > 103)
                    {
                        EXEC->error(_("Add File(s)") + " - " + _("Name Too Long"),
                                    _("Cannot add named length more than 103 unicode charsets."));
                        continue;
                    }
                    if (renamed) break;
                } while (true);
                if (renamed) child->rename(newName, true);
                else { child->setValid(false); return; }
                break;
            case 1: // add suffix
                suffix = 0;
                f.setFile(child->destination());
                do
                {
                    if (f.suffix().isEmpty())
                    {
                        newName = QString("%1_%2").arg(f.baseName()).arg(suffix++);
                        if (newName.length() > NAME_LENGTH)
                        {
                            newName = f.fileName().mid(0, NAME_LENGTH - QString("_%1").arg(suffix).length());
                            newName.append(QString("_%2").arg(suffix));
                        }
                    }
                    else
                    {
                        newName = QString("%1_%2.%3").arg(f.baseName()).arg(suffix++).arg(f.suffix());
                        if (newName.length() > NAME_LENGTH)
                        {
                            newName = f.fileName().mid(0, NAME_LENGTH - QString("_%1.%2").arg(suffix).arg(f.suffix()).length());
                            newName.append(QString("_%1.%2").arg(suffix).arg(f.suffix()).length());
                        }
                    }
                } while (m_oHashChildren.value(newName));
                child->rename(newName, true);
                break;
            default:
                if (!old->isDisk()) delete old;
                else { child->setValid(false); return; }
                break;
            }
        }
        m_oHashChildren.insert(child->display(), child);
        m_oChildren << child;
        if (m_oLinkTarget)
        {
            linked = static_cast<DataDirectory *>(m_oLinkTarget);
            linked->append(child);
        }
        debug(_("Add child %1").arg(child->display()));
        return;
    }
    if (!b && -1 != idx)
    {

        m_oHashChildren.remove(child->display());
        m_oChildren.takeAt(idx);
        if (m_oLinkTarget)
        {
            linked = static_cast<DataDirectory *>(m_oLinkTarget);
            linked->take(child);

        }
        debug(_("Move child %1 out").arg(child->display()));
        return;
    }
}

DataDirectory::~DataDirectory()
{}
