#ifndef WIN32

#include "dcinotifycontrol.h"
#include "dctransferview.h"
#include "dcconfig.h"
#include "dcgui.h"

#include <QDir>
#include <QFile>
#include <QTreeWidget>
#include <QTreeWidgetItem>
#include <QTextStream>
#include <QPushButton>
#include <QMessageBox>
#include <QTreeWidgetItemIterator>
#include <QWidget>

//#include <QtDebug>

#include <dclib/cfilemanager.h>

DCInotifyControl *DCInotifyControl::pInotifyControl = NULL;

DCInotifyControl::DCInotifyControl(QWidget *parent) : QDialog(parent) {
    setupUi(this);

    eis_path = QDir::homePath() + "/.eiskaltdc";
    active_item = NULL;
    inotify = NULL;

    InitDocument();

    LoadDirs();

    if ((!dirs.empty())) {

        inotify = new DCInotify();

        if (!inotify->init_inotify()) {
            m_bIsInitialized = false;
            return;
        }
        inotify->setRecursive(DCConfig::Instance()->GetInotifyRecursive());
        inotify->setInterval(DCConfig::Instance()->GetInotifyInterval());
        connect(inotify, SIGNAL(fs_event(InotifyObject*)), this, SLOT(slotFSEvent(InotifyObject*)));

        for (int i = 0; i < dirs.size(); i++)
            inotify->addDirectory(dirs.at(i));

        inotify->start();

        m_bIsInitialized = true;

        timer = new QTimer(this);
        timer->setSingleShot(true);
        connect(timer, SIGNAL(timeout()), this, SLOT(slotTimerDone()));
        timer->start(DCConfig::Instance()->GetInotifyInterval()*1000 / 2);

        //qDebug() << "Timer initialized for " << DCConfig::Instance()->GetInotifyInterval()*1000/2 << " msecs";

        collector = new QTimer(this);
        connect(collector, SIGNAL(timeout()), this, SLOT(slotCollectorStart()));
        collector->setSingleShot(true);

        slotTimerIntervalChanged(DCConfig::Instance()->GetInotifyInterval());

    }

    pending = false;

}

DCInotifyControl::~DCInotifyControl() {
    if (inotify){
	inotify->terminate();
	inotify->wait(1000);

	delete inotify;
    }

    if (timer){
	killTimer(timer->timerId());

	delete timer;
    }
    if (collector){
	killTimer(collector->timerId());

	delete collector;
    }

    QFile file(eis_path + "/inotify");

    if (file.open(QIODevice::WriteOnly) && (!dirs.empty())) {
        QTextStream stream(&file);

        for (int i = 0; i < dirs.size(); i++)
            stream << dirs.at(i) << "\n";

        dirs.clear();
    }

    file.close();

    if (!events.empty()) {
        for (int i = 0; i < events.size(); i++)
            delete events.at(i);
    }

    QTreeWidgetItemIterator it(treeWidget_SHARE, QTreeWidgetItemIterator::NotHidden);

    while (*it) {
        if (*it)
            delete *it;
        ++it;
    }

    treeWidget_SHARE->clear();
}

void DCInotifyControl::slotAccept() {
    hide();
}

void DCInotifyControl::InitDocument() {
    InotifyMessageType i_type = DCConfig::Instance()->GetInotifyMessageType();

    checkBox_ME->setChecked(IS_INOTIFY_CHG(i_type));
    checkBox_CE->setChecked(IS_INOTIFY_CRT(i_type));
    checkBox_DE->setChecked(IS_INOTIFY_DEL(i_type));

    if (IS_INOTIFY_LOG(i_type)) {
        checkBox_ME->setEnabled(false);
        checkBox_CE->setEnabled(false);
        checkBox_DE->setEnabled(false);

        radioButton_ASK->setEnabled(false);
        radioButton_NONE->setEnabled(false);
        radioButton_RS->setEnabled(false);

        checkBox_LOGONLY->setChecked(true);
    }

    spinBox_TIMER->setValue(DCConfig::Instance()->GetInotifyInterval());

    switch (DCConfig::Instance()->GetInotifyAction()) {
        case eInotifyAsk:
            radioButton_ASK->setChecked(true);
            break;
        case eInotifyNone:
            radioButton_NONE->setChecked(true);
            break;
        case eInotifyRS:
            radioButton_RS->setChecked(true);
            break;
    }

    checkBox_RECURSIVE->setChecked(true);

    connect(radioButton_ASK, SIGNAL(clicked()), this, SLOT(slotInotifyActionChange()));
    connect(radioButton_NONE, SIGNAL(clicked()), this, SLOT(slotInotifyActionChange()));
    connect(radioButton_RS, SIGNAL(clicked()), this, SLOT(slotInotifyActionChange()));

    connect(checkBox_ME, SIGNAL(clicked()), this, SLOT(slotInotifyFilterChange()));
    connect(checkBox_CE, SIGNAL(clicked()), this, SLOT(slotInotifyFilterChange()));
    connect(checkBox_DE, SIGNAL(clicked()), this, SLOT(slotInotifyFilterChange()));

    connect(checkBox_LOGONLY, SIGNAL(clicked()), this, SLOT(slotLogOnly()));

    connect(spinBox_TIMER, SIGNAL(valueChanged(int)), this, SLOT(slotTimerIntervalChanged(int)));

    connect(treeWidget_SHARE, SIGNAL(itemClicked(QTreeWidgetItem*, int)), this, SLOT(slotTreeWidgetItemClicked(QTreeWidgetItem*, int)));

    connect(pushButton_RESTORE, SIGNAL(clicked()), this, SLOT(slotRestoreShareList()));
    connect(pushButton_REMOVE, SIGNAL(clicked()), this, SLOT(slotRemoveSelectedItem()));
    connect(pushButton_APPLY, SIGNAL(clicked()), this, SLOT(slotApply()));
    connect(pushButton_QUIT, SIGNAL(clicked()), this, SLOT(slotAccept()));

    //connect(tabWidget, SIGNAL(currentChanged(int)), this, SLOT(slotCurrentTabChanged(int)));

}

void DCInotifyControl::LoadDirs() {
    QFile file(eis_path + "/inotify");
    CList<DCConfigShareFolder> list;
    DCConfigShareFolder * csf;

    DCConfig::Instance()->GetSharedFolders(&list);

    if (!file.exists()) {

        csf = 0;

        while ((csf = list.Next(csf)) != 0) {
            QTreeWidgetItem * item = new QTreeWidgetItem(treeWidget_SHARE);
            item->setText(0, csf->m_sPath.Data());
            item->setText(1, csf->m_sAlias.Data());

            dirs << QString(csf->m_sPath.Data());

            // qDebug() << "Loaded: " << QString(csf->m_sPath.Data());
        }

    } else {

        if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
            return;

        QTextStream in(&file);

        while (!in.atEnd()) {
            QString folder = in.readLine();
            folder.replace(QString("\n"), QString(""));
            csf = NULL;

            while ((csf = list.Next(csf)) != 0) {
                QString share_folder = csf->m_sPath.Data();

                if (folder == share_folder) {
                    dirs << QString(csf->m_sPath.Data());

                    QTreeWidgetItem * item = new QTreeWidgetItem(treeWidget_SHARE);

                    item->setText(0, csf->m_sPath.Data());
                    item->setText(1, csf->m_sAlias.Data());

                    //qDebug() << "Loaded: " << QString(csf->m_sPath.Data());
                }
            }
        }

        file.close();
    }
}

inline bool DCInotifyControl::isInitialized() const {
    return m_bIsInitialized;
}

void DCInotifyControl::slotFSEvent(InotifyObject *obj) {
    mutex.lock();

    events << obj;

    mutex.unlock();
}

void DCInotifyControl::slotTimerDone() {
    mutex.lock();

    if (events.empty()) {
        //qDebug() << "Event quique empty.";
        timer->start(DCConfig::Instance()->GetInotifyInterval()*1000 / 2);
        //qDebug() << "Timer interval set for " << (DCConfig::Instance()->GetInotifyInterval()*1000/2) << " msecs";
        collector->start(10000);
    } else {
        //qDebug() << "Event quique not empty.";
        InotifyObject *obj = NULL;
        InotifyMessageType i_type = DCConfig::Instance()->GetInotifyMessageType();

        for (int i = 0; i < events.size(); i++) {
            //qDebug() << events.size() << " elements in quique.";
            obj = events.at(i);

            if (obj) {

                if (!IS_INOTIFY_CHG(i_type) && ((obj->event_type == eICHANGE) || (obj->event_type == eIMOVE)))
                    continue;
                if (!IS_INOTIFY_CRT(i_type) && (obj->event_type == eICREATE))
                    continue;
                if (!IS_INOTIFY_DEL(i_type) && (obj->event_type == eIDELETE))
                    continue;

                QTreeWidgetItem *item = new QTreeWidgetItem(treeWidget_LOG);

                QString it_text = "[";

                switch (obj->event_obj) {
                    case eIDIRECTORY:
                        it_text += tr("Directory ");
                        break;
                    case eIFILE:
                        it_text += tr("File ");
                        break;
                }

                switch (obj->event_type) {
                    case eICHANGE:
                        it_text += tr("changed");
                        break;
                    case eICREATE:
                        it_text += tr("created");
                        break;
                    case eIDELETE:
                        it_text += tr("deleted");
                        break;
                    case eIMOVE:
                        it_text += tr("moved");
                        break;
                }

                it_text += "]: " + obj->name;

                item->setText(0, it_text);

                if (!IS_INOTIFY_LOG(i_type)) {
                    if (obj->event_type == eICREATE) {
                        //qDebug() << "eICREATE: " << obj->name;
                        if (deleted.contains(obj->name))
                            deleted.remove(obj->name);
                        if (moved.contains(obj->name))
                            moved.remove(obj->name);
                        if (changed.contains(obj->name))
                            changed.remove(obj->name);

                        created[obj->name] = obj->event_obj;
                    } else if (obj->event_type == eIDELETE) {
                        //qDebug() << "eIDELETE: " << obj->name;
                        if (created.contains(obj->name))
                            created.remove(obj->name);
                        if (moved.contains(obj->name))
                            moved.remove(obj->name);
                        if (changed.contains(obj->name))
                            changed.remove(obj->name);

                        deleted[obj->name] = obj->event_obj;
                    } else if (obj->event_type == eICHANGE) {
                        //qDebug() << "eICHANGE: " << obj->name;
                        if (created.contains(obj->name))
                            created.remove(obj->name);
                        changed[obj->name] = obj->event_obj;
                    } else if (obj->event_type == eIMOVE) {
                        //qDebug() << "eIMOVE: " << obj->name;
                        if (created.contains(obj->name))
                            created.remove(obj->name);
                        if (changed.contains(obj->name))
                            changed.remove(obj->name);
                        if (deleted.contains(obj->name))
                            deleted.remove(obj->name);

                        if (obj->event_obj == eIDIRECTORY) {

                            QDir t_dir(obj->name);

                            if (t_dir.exists())
                                moved[obj->name] = obj->event_obj;
                        } else {

                            if (QFile::exists(obj->name))
                                moved[obj->name] = obj->event_obj;
                        }
                    }

                    //qDebug()<< "Created: " <<  created;
                    //qDebug()<< "Deleted: " << deleted;
                    //qDebug()<< "Changed: " << changed;
                    //qDebug()<< "Moved: " << moved;

                }
            }
        }

        for (int i = 0; i < events.size(); i++) {
            obj = events.at(i);

            if (obj) {
                delete obj;
            }
        }

        events.clear();

        timer->start(DCConfig::Instance()->GetInotifyInterval()*1000 / 2);
        //qDebug() << "Timer interval set for " << (DCConfig::Instance()->GetInotifyInterval()*1000/2) << " msecs";
        collector->start(5000);
    }

    mutex.unlock();
}

void DCInotifyControl::slotInotifyActionChange() {
    if (radioButton_ASK->isChecked())
        DCConfig::Instance()->SetInotifyAction(eInotifyAsk);
    else if (radioButton_NONE->isChecked())
        DCConfig::Instance()->SetInotifyAction(eInotifyNone);
    else
        DCConfig::Instance()->SetInotifyAction(eInotifyRS);
}

void DCInotifyControl::slotInotifyFilterChange() {
    InotifyMessageType i_type = DCConfig::Instance()->GetInotifyMessageType();

    if (checkBox_DE->isChecked())
        SET_INOTIFY_DEL(i_type);
    else
        USET_INOTIFY_DEL(i_type);

    if (checkBox_CE->isChecked())
        SET_INOTIFY_CRT(i_type);
    else
        USET_INOTIFY_CRT(i_type);

    if (checkBox_ME->isChecked())
        SET_INOTIFY_CHG(i_type);
    else
        USET_INOTIFY_CHG(i_type);

    DCConfig::Instance()->SetInotifyMessageType(i_type);
}

void DCInotifyControl::slotLogOnly() {
    InotifyMessageType i_type = DCConfig::Instance()->GetInotifyMessageType();

    bool b = !checkBox_LOGONLY->isChecked();

    checkBox_ME->setEnabled(b);
    checkBox_CE->setEnabled(b);
    checkBox_DE->setEnabled(b);

    radioButton_ASK->setEnabled(b);
    radioButton_NONE->setEnabled(b);
    radioButton_RS->setEnabled(b);

    if (b)
	USET_INOTIFY_LOG(i_type);
    else
	SET_INOTIFY_LOG(i_type);

    DCConfig::Instance()->SetInotifyMessageType(i_type);
}

void DCInotifyControl::slotTimerIntervalChanged(int m) {
    DCConfig::Instance()->SetInotifyInterval(m);

    inotify->setInterval(DCConfig::Instance()->GetInotifyInterval());
}

void DCInotifyControl::slotCollectorStart() {
    bool dirty = false;
    eInotifyAction act = DCConfig::Instance()->GetInotifyAction();

    if (!created.empty()) {

        dirty = true;
        created.clear();

    }
    if (!deleted.empty()) {

        dirty = true;
        deleted.clear();

    }
    if (!moved.empty()) {

        dirty = true;
        moved.clear();

    }
    if (!changed.empty()) {
        QStringList transfers;
        QMapIterator<QString, eIEVENT_OBJ> it(changed);

        DCTransferView::Instance()->GetDownloadFileList(transfers);

        /*if (!transfers.empty()){
            for (int i = 0; i < transfers.size(); i++)
                qDebug() << transfers.at(i);
        }*/

        while (it.hasNext()) {
            it.next();

            if (transfers.contains(it.key())) {
                //qDebug() << "Changed file in transfer";
                continue;
            } else {
                QFile file(it.key());
                //qDebug() << "changed_files: " << changed_files;

                if (changed_files.contains(it.key())) {
                    //qDebug() << "File in changed_files";
                    if (changed_files[it.key()] != file.size()) {
                        //qDebug() << "changed_files[it.key()] != file.size()";
                        changed_files[it.key()] = file.size();
                        continue;
                    } else {
                        //qDebug() << it.key() << " stablizated.";
                        changed_files.remove(it.key());
                        changed.remove(it.key());
                        if (changed.empty())
                            dirty = true;
                    }
                } else {
                    changed_files[it.key()] = file.size();
                    continue;
                }

            }
        }
    }

    if ((dirty && (act == eInotifyAsk)) || pending) {

        if (!DCGuiApp::Instance()->isVisible()) {
            pending = true;
            return;
        }

        QMessageBox msg_box;
        QPushButton *rs_btn, *none_btn;

        rs_btn = msg_box.addButton(tr("Recreate share"), QMessageBox::YesRole);
        none_btn = msg_box.addButton(tr("None"), QMessageBox::YesRole);

        msg_box.setText(tr("Files in shared folders were changed. What you want to do now?"));

        msg_box.setWindowModality(Qt::WindowModal);

        msg_box.exec();

        if (msg_box.clickedButton() == rs_btn)
            CFileManager::Instance()->CreateShareList();

        pending = false;
    } else if (dirty && (act == eInotifyRS))
        CFileManager::Instance()->CreateShareList();



    slotInotifyActionChange();
}

void DCInotifyControl::slotTreeWidgetItemClicked(QTreeWidgetItem *item, int /*column*/) {
    active_item = item;
}

void DCInotifyControl::slotRemoveSelectedItem() {
    if (active_item) {
        treeWidget_SHARE->removeItemWidget(active_item, 0);
        treeWidget_SHARE->removeItemWidget(active_item, 1);

        delete active_item;

        active_item = NULL;
    }
}

void DCInotifyControl::slotRestoreShareList() {
    QTreeWidgetItemIterator it(treeWidget_SHARE, QTreeWidgetItemIterator::NotHidden);

    while (*it) {
        if (*it)
            delete *it;
        ++it;
    }

    treeWidget_SHARE->clear();

    CList<DCConfigShareFolder> list;
    DCConfigShareFolder * csf;

    DCConfig::Instance()->GetSharedFolders(&list);

    csf = 0;

    while ((csf = list.Next(csf)) != 0) {
        QTreeWidgetItem * item = new QTreeWidgetItem(treeWidget_SHARE);
        item->setText(0, csf->m_sPath.Data());
        item->setText(1, csf->m_sAlias.Data());
    }
}

void DCInotifyControl::slotApply() {
    QList<QString> new_dirs;
    QTreeWidgetItemIterator it(treeWidget_SHARE, QTreeWidgetItemIterator::NotHidden);

    while (*it) {
        new_dirs << (*it)->text(0);
        ++it;
    }

    bool b = false;

    if (new_dirs.size() != dirs.size())
        b = true;
    else {
        if (new_dirs.empty())
            return;

        for (int i = 0; i < new_dirs.size(); i++) {
            if (!dirs.contains(new_dirs.at(i))) {
                b = true;

                dirs.clear();
            }
        }
    }

    if (new_dirs.empty()) {
        dirs.clear();

        ReinitInotify();

        return;
    }

    dirs.clear();

    for (int i = 0; i < new_dirs.size(); i++)
        dirs << new_dirs.at(i);

    ReinitInotify();

    new_dirs.clear();

}

void DCInotifyControl::ReinitInotify() {
    if (inotify) {
        inotify->terminate();
        inotify->wait(1000);
        disconnect(inotify, SIGNAL(fs_event(InotifyObject*)), this, SLOT(slotFSEvent(InotifyObject*)));
        delete inotify;
        inotify = NULL;
        if (timer)
            timer->stop();
        if (collector)
            collector->stop();
    }

    if (!dirs.empty()) {

        if (timer)
            timer->stop();
        if (collector)
            collector->stop();

        if (!events.empty()) {
            for (int i = 0; i < events.size(); i++)
                delete events.at(i);
        }

        created.clear();
        deleted.clear();
        moved.clear();
        changed.clear();
        changed_files.clear();

        inotify = new DCInotify();

        if (!inotify->init_inotify()) {
            m_bIsInitialized = false;
            return;
        }

        inotify->setRecursive(DCConfig::Instance()->GetInotifyRecursive());
        inotify->setInterval(DCConfig::Instance()->GetInotifyInterval());
        connect(inotify, SIGNAL(fs_event(InotifyObject*)), this, SLOT(slotFSEvent(InotifyObject*)));

        for (int i = 0; i < dirs.size(); i++)
            inotify->addDirectory(dirs.at(i));

        inotify->start();

        m_bIsInitialized = true;

        if (!timer) {
            timer = new QTimer(this);
            connect(timer, SIGNAL(timeout()), this, SLOT(slotTimerDone()));
        }

        timer->setSingleShot(true);
        timer->start(DCConfig::Instance()->GetInotifyInterval()*1000 / 2);

        if (!collector) {
            collector = new QTimer(this);
            connect(collector, SIGNAL(timeout()), this, SLOT(slotCollectorStart()));
        }

        collector->setSingleShot(true);
    } else {
        //qDebug() << "Inotify disabled.";

        m_bIsInitialized = false;
    }

}

void DCInotifyControl::closeEvent(QCloseEvent *e) {
    if (!shutdown) {
        e->ignore();
        hide();
    } else{
        e->accept();

	QDialog::closeEvent(e);
    }
}

void DCInotifyControl::Shutdown() {
    shutdown = true;
    //setAttribute(Qt::WA_DeleteOnClose);

    close();
}

/*void DCInotifyControl::slotCurrentTabChanged(int tab) {
    QWidget *par = tabWidget->currentWidget();

    pushButton_QUIT->setParent(par);

    pushButton_QUIT->show();
}*/

#endif
