/***************************************************************************
                          dcfiletool.cpp  -  description
                             -------------------
    begin                : Die Mär 26 2002
    copyright            : (C) 2002 by Mathias Küster
    email                : mathen@users.berlios.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "dcfiletool.h"

#include <QFile>
#include <QFileInfo>
#include <QMessageBox>
#include <QInputDialog>
#include <QMenu>
#include <QList>
#include <QCursor>
#include <QDialog>
#include <QCheckBox>
#include <QLabel>
#include <QApplication>
#include <QStyle>

#include <dclib/core/cdir.h>

#include "dcconfig.h"
#include "dctransferview.h"
#include "dcconnectionmanager.h"
#include "dcguiutils.h"

#include "DCDialogAskDownloadMode.h"

#include <dclib/dcos.h>

/** */
DCFileTool::DCFileTool() {
}

/** */
DCFileTool::~DCFileTool() {
}

/** */
bool DCFileTool::SelectFileSource(QWidget * parent, ulonglong size, QString tth, QString & file, QString & localrootpath, QString & localpath) {
    bool res = false;
    QStringList LocalFilesList;

    if (tth.isEmpty()) {
        DCTransferView::Instance()->GetLocalFilesList(LocalFilesList, size);
    } else {
        DCTransferView::Instance()->GetLocalFilesList(LocalFilesList, tth, size);
    }

    if (LocalFilesList.empty()) {
        return res;
    }

    // Add as an extra source
    QMenu * m = new QMenu(parent);
    QList<QAction*> actlist;

    QAction * title = m->addAction(QObject::tr("(choose an existing download to add to)"));
    title->setEnabled(false);

    for (int iter = 0; iter < (int) LocalFilesList.size(); iter++) {
        if (LocalFilesList[iter].contains("\\")) {
            actlist.append(m->addAction(LocalFilesList[iter].section("\\", -1, -1)));
        } else {
            actlist.append(m->addAction(LocalFilesList[iter].section("/", -1, -1)));
        }
    }

    QAction * chosen = m->exec(QCursor::pos());

    delete m;

    if (chosen != 0) {
        QFileInfo fi(LocalFilesList[actlist.indexOf(chosen)]);

        file = fi.fileName();
        localpath = "";
        localrootpath = fi.path();

        // now we must check if a localpath exist
        if (localrootpath.indexOf(DCConfig::Instance()->GetDownloadFolder().Data(), 0) == 0) {
            localpath = localrootpath.mid(DCConfig::Instance()->GetDownloadFolder().Length(), localrootpath.length() - DCConfig::Instance()->GetDownloadFolder().Length());
            localrootpath = DCConfig::Instance()->GetDownloadFolder().Data();
        }

        res = true;
    }

    return res;
}

/** */
bool DCFileTool::AddFileSource(QWidget * parent, CString nick, CString hubname, CString hubhost,
        CString remotename, CString localname,
        CString localpath, CString localrootpath,
        eltMedium medium, ulonglong size, CString tth) {
    bool res = false;
    QString sourcefile, slocalrootpath, slocalpath;

    if (SelectFileSource(parent, size, tth.Data(), sourcefile, slocalrootpath, slocalpath) == false) {
        return CheckFile(parent, nick, hubname, hubhost,
                remotename, localname,
                localpath, localrootpath,
                medium, size, tth);
    }

    if (!sourcefile.isEmpty()) {
        // add transfer to the waitlist
        res = CheckFile(parent, nick, hubname, hubhost,
                remotename, sourcefile.toAscii().constData(),
                slocalpath.toAscii().constData(), slocalrootpath.toAscii().constData(),
                medium, size, tth, true);

    }

    return res;
}

/** */
bool DCFileTool::CheckFile(QWidget * parent, CString nick, CString hubname, CString hubhost,
        CString remotename, CString localname,
        CString localpath, CString localrootpath,
        eltMedium medium, ulonglong size, CString hash,
        bool usermulti, eRepairType repair) {
    CDir dir;
    QString name;
    ulonglong fsize = 0, pos_s = 0, pos_e = 0;
    int filestate;
    bool multi = usermulti;

    // first we get the filestate from the downloadmanager
    filestate = DCTransferView::Instance()->DLM_QueueCheck(nick, hubname, hubhost,
            remotename, localname,
            localpath, localrootpath,
            medium, size, hash);

    if (filestate == 1) {
        //		QMessageBox::information( parent, QObject::tr("File download"),
        //			QObject::tr("The same user/file is already in the queue !\n"));
        return false;
    } else if (filestate == 2) {
        /*
         * This was from where uninitialized pos_s and pos_e were
         * passed to CDownloadManager, they are not used when adding
         * a source to an existing download.
         */
        if (multi == false) {
            switch (QMessageBox::warning(parent, QObject::tr("File download"),
                    QObject::tr("A same file is already in the queue!"),
                    QObject::tr("Start a multi-download"),
                    QObject::tr("Cancel"), 0, 1)) {
                case 0:
                    multi = true;
                    break;
                default:
                    return false;
                    break;
            }
        }
    } else if (filestate == 3) {
        QMessageBox::information(parent, QObject::tr("File download"),
                QObject::tr("File is already in the queue but not mark as a multi-download!\n"));
        return false;
    } else if (filestate == 4) {
        QMessageBox::information(parent, QObject::tr("File download"),
                QObject::tr("File is already in the queue with a different size!\n"));
        return false;
    } else if (filestate == 0) {
        if (localrootpath.NotEmpty()) {
            name = localrootpath.Data();
        } else {
            name = DCConfig::Instance()->GetDownloadFolder().Data();
        }

        if (name.isEmpty()) {
            return false;
        }

        localpath = localpath.Replace(':', "");
        localname = localname.Replace(':', "");

        name += QString("/");
        name += QString(localpath.Data());
        name += QString("/");
        name += QString(localname.Data());
        name = CDir::SimplePath(name.toAscii().constData()).Data();

        fsize = dir.getFileSize(name.toAscii().constData(), false);

        // check already download
        if ((size == fsize) && (fsize > 0) && !repair) {
            QMessageBox::information(parent, QObject::tr("File download"),
                    QObject::tr("File already downloaded!\n"));
            return false;
        }

        // check for partial download
        if (fsize > 0) {
            if (repair) {
                // get desired byte range
                QString range;
                bool ok = false;

                // There is probably a better way to get this info.  We assume 2352 sector size (ss)
                // which is most common for BIN/CUE, but we might want to make two menu entries, one
                // for 2352 and one for 2048 (ie. ISO).  The sector offset (so) is the number of bytes
                // into the BIN file where sector zero is reported.  Fireburner for Linux reports
                // from the beginning of the file, but other programs (like cdmage) report from the
                // first data sector (skipping all header and directory information).  We assume zero.
                int ss = 2352;
                int so = 0;

                if (repair == ertBYTES) {
                    range = QInputDialog::getText(
                            parent,
                            QObject::tr("Repair File"),
                            QObject::tr("Enter byte range (m-n)"),
                            QLineEdit::Normal,
                            QString::null,
                            &ok
                            );
                } else {
                    range = QInputDialog::getText(
                            parent,
                            QObject::tr("Repair BIN Sectors"),
                            QObject::tr("Enter sector range (m[-n])"),
                            QLineEdit::Normal,
                            QString::null,
                            &ok
                            );
                }

                if (!ok) {
                    return false;
                }

                int p = range.indexOf('-');

                if (p > 0) {
                    pos_s = range.left(p).toULongLong(&ok);

                    if (!ok) {
                        QMessageBox::critical(
                                parent,
                                QObject::tr("Repair File"),
                                QObject::tr("Invalid start position.")
                                );
                        return false;
                    }

                    pos_e = range.mid(p + 1).toULongLong(&ok);

                    if (!ok) {
                        QMessageBox::critical(
                                parent,
                                QObject::tr("Repair File"),
                                QObject::tr("Invalid end position.")
                                );
                        return false;
                    }
                } else if (repair == ertSECTORS) {
                    pos_s = range.toULongLong(&ok);

                    if (!ok) {
                        QMessageBox::critical(
                                parent,
                                QObject::tr("Repair File"),
                                QObject::tr("Invalid number of sectors.")
                                );
                        return false;
                    }

                    pos_e = 0;
                } else {
                    QMessageBox::critical(
                            parent,
                            QObject::tr("Repair File"),
                            QObject::tr("Invalid range entered.")
                            );
                    return false;
                }

                if (repair == ertSECTORS) {
                    if (pos_e == 0) {
                        pos_e = pos_s;
                    }

                    pos_s = so + pos_s * ss;
                    pos_e = so + (pos_e + 1) * ss - 1;
                }

                if (pos_e > fsize) {
                    pos_e = fsize;
                }

                if (pos_s > pos_e) {
                    pos_s = pos_e;
                }

                multi = true;
                printf("Starting download repair, %s, %llu-%llu\n", name.toAscii().constData(), pos_s, pos_e);
            } else {
                pos_e = 0;
                switch (QMessageBox::warning(parent, QObject::tr("File download"),
                        QObject::tr("File already exists!") + "\n"
                        + name + "\n"
                        + DCGuiUtils::GetSizeString(fsize),
                        QObject::tr("Resume"),
                        QObject::tr("Overwrite"),
                        QObject::tr("Cancel"), 0, 2)) {
                    case 0:
                        pos_s = fsize;
                        break;
                    case 1:
                        QFile::remove(name);
                        fsize = 0;
                        pos_s = 0;
                        break;
                    case 2:
                        return false;
                        break;
                    default:
                        return false;
                        break;
                }
            }
        } else // fsize == 0, file does not already exist
        {
            pos_s = 0;
            pos_e = 0;
        }

        /* Check move finished downloads to directory */
        CString finished = DCConfig::Instance()->GetDownloadFinishedFolder();
        if (finished.NotEmpty() && localrootpath.IsEmpty()) {
            CString destfile = DCConfig::Instance()->GetDownloadFolder();
            destfile += DIRSEPARATOR;
            destfile += localpath;
            destfile += DIRSEPARATOR;
            destfile += localname;

            destfile = CDir::SimplePath(destfile);
            destfile = finished + destfile.Mid(DCConfig::Instance()->GetDownloadFolder().Length());
            if (destfile.Left(1) != DIRSEPARATOR) {
                destfile = DIRSEPARATOR + destfile;
            } else if (destfile.Left(2) == CString(DIRSEPARATOR + DIRSEPARATOR)) {
                destfile = destfile.Mid(1);
            }

            //printf("Checking for '%s'\n", destfile.Data());

            if (dir.IsFile(destfile, false)) {
                int ret = QMessageBox::warning(
                        parent,
                        QObject::tr("File download"),
                        QObject::tr("File already exists in your finished downloads folder") + "\n"
                        + QString(destfile.Data()) + "\n"
                        + DCGuiUtils::GetSizeString(dir.getFileSize(destfile, false)) + "\n"
                        + QObject::tr("It will be overwritten when the download finishes"),
                        QObject::tr("Overwrite"),
                        QObject::tr("Cancel"),
                        QString::null,
                        1, 1 // set default button and escape button to Cancel
                        );
                if (ret == 1) {
                    return false;
                }
            }
        }

        /*
         * FIXME dclib is perfectly capable of using multi download mode
         * on files smaller than 1 segment, because the old hack
         * to disable segmented downloading was to set the segment size to 2GiB or so.
         */
        if (size > (1024 * 1024)) {
            if ((DCConfig::Instance()->GetDefaultDownloadMode() == eDownloadAsk) && (multi == false)) {
                Ui::DCDialogAskDownloadMode admui;
                QDialog * askdialog = new QDialog(parent);
                admui.setupUi(askdialog);
                admui.Label_QUESTION_PIXMAP->setPixmap(QApplication::style()->standardPixmap(QStyle::SP_MessageBoxQuestion));

                if (askdialog->exec() == QDialog::Accepted) {
                    multi = true;
                } else {
                    multi = false;
                }

                if (admui.CheckBox_SET_QUICK_OPTION->isChecked()) {
                    if (multi) {
                        DCConfig::Instance()->SetDefaultDownloadMode(eDownloadMulti, true);
                    } else {
                        DCConfig::Instance()->SetDefaultDownloadMode(eDownloadSingle, true);
                    }
                }

                delete askdialog;
            } else if ((DCConfig::Instance()->GetDefaultDownloadMode() == eDownloadSingle) && (multi == false)) {
                multi = false;
            } else {
                multi = true;
            }
        } else {
            multi = false;
        }
    } else {
        QMessageBox::critical(
                parent,
                "DCFileTool::CheckFile",
                QObject::tr("Download manager returned unknown value: ") + QString().setNum(filestate)
                );
        return false;
    }

    if (DCConnectionManager::Instance()->IsHubOnline(hubname, hubhost) == ehsNONE) {
        /* Check if the user is connected to another hub that we are connected to.
         * I'm uncertain how it will react when multi==false (thus that is not handled),
         * but as this concerns small files it is a bit annoying.
         */
        bool foundUser = false;

        if (multi) {
            CList<DCHubObject> HubList;
            CString emptyString;
            DCHubObject * CurrentHub = 0;

            /* Get hublist for the user, ie hubs that both us and them are
             * connected to.
             */
            if (DCConnectionManager::Instance()->IsUserOnline(nick, emptyString, emptyString, &HubList)) {
                /* Add an item in the queue for each hub, mark the user as found */
                while ((CurrentHub = HubList.Next(CurrentHub)) != 0) {
                    foundUser = true;
                    DCTransferView::Instance()->DLM_QueueAdd(nick, CurrentHub->m_sHubName, CurrentHub->m_sHubHost,
                            remotename, localname,
                            localpath, localrootpath,
                            medium, size, pos_s, pos_e, hash, multi);
                }
            }
        }
        /* Usual behavior:
         * (no user found or option not enabled)
         */
        if (foundUser == false) {
            switch (QMessageBox::warning(parent, QObject::tr("File download"),
                    QObject::tr("Not connected to required hub!"),
                    QObject::tr("Connect"),
                    QObject::tr("Cancel"), 0, 0, 1)) {
                case 0:
                    DCConnectionManager::Instance()->Connect(hubname, hubhost);
                    break;
                case 1:
                    break;
            }
        }
    }

    DCTransferView::Instance()->DLM_QueueAdd(nick, hubname, hubhost,
            remotename, localname,
            localpath, localrootpath,
            medium, size, pos_s, pos_e, hash, multi);

    return true;
}
