#include "mainwindow.h"
#include "ui_mainwindow.h"

const QString MainWindow::VagoSettingsName = "settingsVago.ini";

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    // We use this appender because it is the native way to have \r\n in windows in plog library
    // example: https://github.com/SergiusTheBest/plog/blob/master/samples/NativeEOL/Main.cpp
    static plog::RollingFileAppender<plog::TxtFormatter, plog::NativeEOLConverter<>> fileAppender
            (QSTR_TO_CSTR(Util::FileSystem::getAppPath() + "/" + GlobalVars::AppLogName), 1024*5 /* 5 Mb max log size */, 3);
    plog::init(plog::info, &fileAppender);

    ui->setupUi(this);

    LOG_INFO << "Detected AppDir: " + UtilVago::getAppPath();
    LOG_INFO << "True app dir: "+QDir::currentPath();

    setVagoWindowTitle();

    if(!QFile::exists(UtilVago::getOniSplitExecutableAbsolutePath())){
        UtilVago::showAndLogErrorPopUp("OniSplit not found. Please download it at "+GlobalVars::ModsDomain+" and put it the Vago's tools folder. \n\nProgram will now exit.");
        exit(1);
    }

    if(!QFile::exists(UtilVago::getXmlToolsExecutableAbsolutePath())){
        UtilVago::showAndLogErrorPopUp("XmlTools not found. Please download it at "+GlobalVars::ModsDomain+" and put it the Vago's tools folder. \n\nProgram will now exit.");
        exit(1);
    }

    this->vagoSettings = new QSettings(UtilVago::getAppPath() + "/" + this->VagoSettingsName, QSettings::IniFormat);

    //First Execution? Old configuration? Settings missed?
    bool iniChanged=false;
    if(!this->vagoSettings->contains("VagoVersion") || this->vagoSettings->value("VagoVersion")!=GlobalVars::AppVersion){
        this->vagoSettings->setValue("VagoVersion", GlobalVars::AppVersion);
        iniChanged=true;
    }
    if(!this->vagoSettings->contains("Workspace")){
        this->vagoSettings->setValue("Workspace", UtilVago::getAppPath()+"/VagoWorkspace");
        iniChanged=true;
    }
    if(!this->vagoSettings->contains("AeFolder")){

        Util::Dialogs::showInfo("Seems it's the first time you are executing Vago. \n\nPlease input your Anniversary Edition (AE) Folder.");
        QString aefolder=Util::FileSystem::normalizePath(QFileDialog::getExistingDirectory(this,"Choose Anniversary Edition (AE) folder..."));

        if(aefolder.isEmpty()){
            UtilVago::showAndLogErrorPopUp("AE folder is mandatory. Application will now exit.");
            exit(1);
        }

        if(!aefolder.endsWith("AE")){
            Util::Dialogs::showWarning("Seems the folder you selected isn't called 'AE'. \n\nIf you run in any problems you can always change it in Vago preferences window.");
        }

        this->vagoSettings->setValue("AeFolder", aefolder);
        iniChanged=true;
    }
    if(!this->vagoSettings->contains("WindowWidth")){
        this->vagoSettings->setValue("WindowWidth", GlobalVars::DefaultWindowWidth);
        iniChanged=true;
    }
    if(!this->vagoSettings->contains("WindowHeight")){
        this->vagoSettings->setValue("WindowHeight", GlobalVars::DefaultWindowHeight);
        iniChanged=true;
    }
    if(!this->vagoSettings->contains("OniWindow")){
        this->vagoSettings->setValue("OniWindow", true);
        iniChanged=true;
    }
    if(!this->vagoSettings->contains("SeparateInWorkspace")){
        this->vagoSettings->setValue("SeparateInWorkspace",true);
        iniChanged=true;
    }
    if(!this->vagoSettings->contains("AskSaveProject")){
        this->vagoSettings->setValue("AskSaveProject", true);
        iniChanged=true;
    }
    if(!this->vagoSettings->contains("AskToOpenLastProject")){
        this->vagoSettings->setValue("AskToOpenLastProject", false);
        iniChanged=true;
    }
    if(!this->vagoSettings->contains("LastProjectPath")){
        this->vagoSettings->setValue("LastProjectPath", this->vagoSettings->value("Workspace"));
        iniChanged=true;
    }
    for(int i=0; i<this->recentProjectsMaxSize; i++){
        if(!this->vagoSettings->contains("RecentProject" + QString::number(i+1))){
            this->vagoSettings->setValue("RecentProject" + QString::number(i+1), "");
            iniChanged=true;
        }
    }
#ifdef Q_OS_MAC
    if(!this->vagoSettings->contains("useYesAsDefaultWhenRemovingItems")){
        this->vagoSettings->setValue("useYesAsDefaultWhenRemovingItems", false);
        iniChanged=true;
    }
#endif

    if(iniChanged){
        this->vagoSettings->sync();
    }
    ///

    this->workspaceLocation=this->vagoSettings->value("Workspace").toString();
    this->workspaceWizardsLocation=this->workspaceLocation+"/Wizards";
    this->AeLocation=this->vagoSettings->value("AeFolder").toString();
    this->outputFolder=this->workspaceLocation;
    this->startedWindowWidth=this->vagoSettings->value("WindowWidth").toInt();
    this->startedWindowHeight=this->vagoSettings->value("WindowHeight").toInt();
#ifdef Q_OS_MAC
    this->useYesAsDefaultWhenRemovingItems=this->vagoSettings->value("useYesAsDefaultWhenRemovingItems").toBool();
#endif

    //Create our workspace if it doesn't exists yet
    if(!QDir(this->workspaceLocation).exists()){
        QDir().mkdir(this->workspaceLocation);
    }
    this->itemsLoaded=new QLabel(this);
    ui->statusBar->addWidget(this->itemsLoaded);
    this->myBar = new QProgressBar(this);
    this->myBar->setTextVisible(false); //hides text

    this->myBar->setSizePolicy(QSizePolicy::Minimum,QSizePolicy::Fixed);
    this->myBar->setMinimumWidth(150);
    this->myBar->hide(); //hide while not being used
    ui->tbAbortConversion->hide(); //hide while not being used

    ui->statusBar->addPermanentWidget(this->myBar); //this adds automatically in right
    ui->statusBar->addPermanentWidget(ui->tbAbortConversion);

    // User interface
    ui->mainToolBar->addWidget(ui->tbAE); //add ae installer launch button
    ui->mainToolBar->addWidget(ui->emptySpacerLabel); //trick, we can't add directly a space so we add an empty
    ui->mainToolBar->addWidget(ui->tbOni); //add oni launch buttonlabel
    ui->mainToolBar->addWidget(ui->emptySpacerLabel2); //same as before
    ui->mainToolBar->addWidget(ui->tbCommand); //add option to manual onisplit commands
    ui->mainToolBar->addWidget(ui->emptySpacerLabel3); //same as before
    ui->mainToolBar->addWidget(ui->tbXmlToolsInterface); //add option to manual onisplit commands
    ui->mainToolBar->addWidget(ui->emptySpacerLabel4); //same as before
    ui->mainToolBar->addWidget(ui->tbOpenFolder); //add option to open folder with files converted etc

    ui->mainToolBar->setLayoutDirection(Qt::RightToLeft);

    ui->pbConvert->setMinimumHeight(ui->pbConvert->sizeHint().height()*1.5); // This is OS indepented. It maintain size ratio over the Windows and Mac.


#ifdef Q_OS_MAC
    // setUnifiedTitleAndToolBarOnMac(true); // Qt suggests to use it on mac | http://www.slideshare.net/qtbynokia/how-to-make-your-qt-app-look-native // align on left doesn't work if active
    ui->tbOni->setIcon(QIcon(":/new/icons/oni_icon_mac.png")); // Oni executable on mac have a different icon than windows
    // Set mac platform the first one in the menu, and also make it checkable by default
    ui->menuTarget_Platform->removeAction(ui->actionWindows);
    ui->menuTarget_Platform->addAction(ui->actionWindows);
    ui->actionWindows->setChecked(false);
    ui->actionMac_Windows_demo->setChecked(true);
    // resize(800,600); // Mac OS pcs should be able to render this resolution without any problem. It's also better
    //// because the components on mac use more space
#endif

    resize(this->startedWindowWidth,this->startedWindowHeight);

#ifdef Q_OS_MAC
    ui->pbConvert->setToolTip(ui->pbConvert->toolTip() + " (⌘ + Enter)");
#else
    ui->pbConvert->setToolTip(ui->pbConvert->toolTip() + " (Ctrl + Enter)");
#endif

    //Commands Mapping
    this->commandMap = QHash<QString, QString>();
    mapCommands();

    updateItemsLoaded(ui->twSourcesXML);

    loadRecentProjects();
}

MainWindow::~MainWindow()
{
    delete ui;
    LOG_INFO << "Application Exited.";
}


void MainWindow::showEvent(QShowEvent *e)
{
    // If we don't have a converter yet, application wasn't started.
    if(!this->applicationIsFullyLoaded)
    {
        // Apparently Qt doesn't contains a slot to when the application was fully load (mainwindow). So we do our own implementation instead.
        connect(this, SIGNAL(signalAppIsLoaded()), this, SLOT(applicationWasLoaded()), Qt::ConnectionType::QueuedConnection);
        emit signalAppIsLoaded();
    }

    e->accept();
}

// Called only when the MainWindow was fully loaded and painted on the screen. This slot is only called once.
void MainWindow::applicationWasLoaded(){
#ifdef Q_OS_WIN
    // QProgressBar only works after the windows was shown
    // http://stackoverflow.com/questions/24840941/qwintaskbarprogress-wont-show (Kervala answer)

    this->win7TaskBarButton = new QWinTaskbarButton();

    this->win7TaskBarButton->setWindow(this->windowHandle());

    this->win7TaskBarProgress = this->win7TaskBarButton->progress();

    //Create a thread for do the conversion in background
    this->myConverter = new Converter(UtilVago::getAppPath(), &this->listToProccess, this->win7TaskBarProgress);
#else
    this->myConverter = new Converter(UtilVago::getAppPath(), &this->listToProccess);
#endif

    connectSlots();

    LOG_INFO << "Application started.";

    this->applicationIsFullyLoaded = true;

    QString lastSavedProject = this->vagoSettings->value("RecentProject1").toString();

    if(!lastSavedProject.isEmpty() && this->vagoSettings->value("AskToOpenLastProject").toBool()){
        if(Util::Dialogs::showQuestion(this,"Do you want to load latest project?\n\nLatest project was '" + Util::FileSystem::cutNameWithoutBackSlash(lastSavedProject) + "'.")){
            loadProjectState(lastSavedProject);
        }
    }
}


void MainWindow::on_actionExit_triggered()
{
    close();
}

void MainWindow::on_actionAbout_triggered()
{
    //Show preferences
    About *aboutWindow = new About(this);
    aboutWindow->show(); //it destroys itself when finished.
}

void MainWindow::on_actionAE_Package_Creator_triggered()
{
    // it deletes itself once closed
    WizardFactory<PackageWizard>::startInstance(UtilVago::getAppPath(), this->workspaceWizardsLocation, this->vagoSettings);
}

void MainWindow::on_actionSound_Wizard_triggered()
{
    // it deletes itself once closed
    WizardFactory<SoundWizard>::startInstance(UtilVago::getAppPath(), this->workspaceWizardsLocation, this->vagoSettings, &this->commandMap);
}

void MainWindow::on_actionBackground_Image_Wizard_triggered()
{
    // it deletes itself once closed
    WizardFactory<BGImageWizard>::startInstance(UtilVago::getAppPath(), this->workspaceWizardsLocation, this->vagoSettings);
}

void MainWindow::on_actionWindow_Messages_Wizard_triggered()
{
    // it deletes itself once closed
    WizardFactory<WmWizard>::startInstance(UtilVago::getAppPath(), this->workspaceWizardsLocation, this->vagoSettings);
}

void MainWindow::on_tbOni_clicked()
{
    QStringList arguments;

    if(this->vagoSettings->value("OniWindow").toBool()){ // Run in a window?
        arguments << "-noswitch";
    }
#ifdef Q_OS_WIN
    else{
        arguments << "-switch"; // only supported on windows. Was added by daodan dll.
    }
#endif

    arguments << "-debugfiles";

    if(!QProcess::startDetached(this->AeLocation+"/"+GlobalVars::OniExe,arguments,this->AeLocation)){
        Util::StatusBar::showError(ui->statusBar, "Oni could not be started!");
    }
}

void MainWindow::on_tbAE_clicked()
{
    // If the app turn out someday to a native app use QProcess::startDetached instead...

    if(!QDesktopServices::openUrl("file:///"+this->AeLocation+"/AEInstaller/bin/AEInstaller2.jar")){
        Util::StatusBar::showError(ui->statusBar, "Could not start AE Installer!");
    }
}

void MainWindow::on_tbOpenFolder_clicked()
{
    QDesktopServices::openUrl(QUrl("file:///"+this->outputFolder));
}


void MainWindow::on_tbXmlToolsInterface_clicked()
{
    //We pass no parent because we want to have an independent window for XmlToolsInterface,
    //so we can minimize it or maximize indepently from the MainWindow
    XmlToolsInterface *xmlToolsWindow = new XmlToolsInterface();
    xmlToolsWindow->show(); //it destroys itself when finished.
}

void MainWindow::on_tbAbortConversion_clicked()
{
    if(Util::Dialogs::showQuestion(this,"Are you sure you want to abort the current conversion?")){
        emit terminateCurrProcess();
    }
}

void MainWindow::on_cbEnvMap_toggled(bool checked)
{
    ui->leEnvMapTexture->setEnabled(checked);
}

void MainWindow::on_cbTexture_toggled(bool checked)
{
    ui->leTextureName->setEnabled(checked);
}

void MainWindow::on_cbSpecificFilesLevels_toggled(bool checked)
{
    ui->leSpecificFilesLevels->setEnabled(checked);
}

void MainWindow::on_cbWithAnimation_toggled(bool checked)
{
    ui->leAnimationName->setEnabled(checked);
}

void MainWindow::on_actionCheck_For_Updates_triggered()
{

    //let's check in the web if this version is the latest
    QNetworkAccessManager *manager = new QNetworkAccessManager(this);
    connect(manager, SIGNAL(finished(QNetworkReply*)),
            this, SLOT(checkVagoLastVersion(QNetworkReply*)));

    manager->get(QNetworkRequest(QUrl(GlobalVars::VagoCheckUpdatesUrl)));

}

void MainWindow::checkVagoLastVersion(QNetworkReply *result){

    if(result->error()==QNetworkReply::NoError){
        QScriptEngine engine;
        QScriptValue sc = engine.evaluate("(" + QString(result->readAll()) + ")");

        // "field_version":{"und":[{"value":"0.6a","format":null,"safe_value":"0.6a"}]} //Note the use of [{}] which means it's a array of 1 element with one object inside (so the use of [0] bellow

        QString newVersion = sc.property("field_version").toObject().property("und").toObject().property("0").toObject().property("value").toString();

        if(newVersion!=GlobalVars::AppVersion){
            Util::Dialogs::showRichInfo("There's a new version of Vago! (v"+newVersion+")<br/><br/>"+
                                        "You can download it <a href='"+GlobalVars::VagoWebUrl+"'>here</a>.");
        }
        else{
            Util::Dialogs::showInfo("You are using last version.");
        }
    }
    else{
        UtilVago::showAndLogErrorPopUp("An error occurred checking last version:\n\n"+result->errorString());
    }
    result->deleteLater();
}

void MainWindow::on_pbAddSourceXML_clicked()
{
    addFilesSource( ui->twSourcesXML,QFileDialog::getOpenFileNames(this,"Choose the files...","./" , "All Files (*.*)"));
}

void MainWindow::on_pbAddSourceTextures_clicked()
{
    addFilesSource( ui->twSourcesTextures, QFileDialog::getOpenFileNames(this,"Choose the files...","./" , "All Files (*.*)"));
}

void MainWindow::on_pbAddSourceObjects_clicked()
{
    addFilesSource( ui->twSourcesObjects,QFileDialog::getOpenFileNames(this,"Choose the files...","./" , "All Files (*.*)"));
}

void MainWindow::on_pbAddSourceCharacters_clicked()
{
    addFilesSource( ui->twSourcesCharacters,QFileDialog::getOpenFileNames(this,"Choose the files...","./" , "All Files (*.*)"));
}

void MainWindow::on_pbAddSourceLevels_clicked()
{
    if(QString::compare(ui->cbFromLevels->currentText(),"ONI FILES",Qt::CaseSensitive)==0 && QString::compare(ui->cbToLevels->currentText(),"DAT",Qt::CaseSensitive)==0){ //CaseSensitive is faster)
        addFilesSource(ui->twSourcesLevels,Util::Dialogs::multipleDirSelection("Choose folders with ONIs..."));
    }
    else{
        addFilesSource(ui->twSourcesLevels,QFileDialog::getOpenFileNames(this,"Choose the files...","./" , "All Files (*.*)"));
    }
}

void MainWindow::on_pbAddSourceMisc_clicked()
{
    addFilesSource( ui->twSourcesMisc,QFileDialog::getOpenFileNames(this,"Choose the files...","./" , "All Files (*.*)"));
}

QString MainWindow::getFileOutputFolder(QString fromTo, QString myOutputFolder){

    if(myOutputFolder==""){ //We may want to change to a non standart location with context menu
        myOutputFolder=this->outputFolder;
    }

    if(this->vagoSettings->value("SeparateInWorkspace").toBool() && myOutputFolder==this->workspaceLocation){
        myOutputFolder+="/"+ui->tabWidget->tabText(ui->tabWidget->currentIndex());
        myOutputFolder+="/"+QString(fromTo).replace(" / ","_").replace(" > "," - ");
    }
    return Util::String::insertQuotes(myOutputFolder+"/");
}

void MainWindow::addFilesSource(DropTableWidget *myTable, QStringList files){

    //Get Conversion pretended
    QString from,to;

    QString fromTo = getTypeConversion(myTable);

    from = QString(fromTo).remove(fromTo.indexOf(" >"),fromTo.size()-1); //parse the string to get the from, only 1 time parsed by each group of files = very fast
    to = QString(fromTo).remove(0,fromTo.lastIndexOf("> ")+2); //+2 to start after "> "

    //Pre-processing (check if the files/folders received are valid), e.g. check for ONI FILES->DAT if are only given folders and not files
    if(from=="ONI FILES" && to=="DAT"){
        //check if it's a folder
        for(const QString &myFile : files){
            if(!QDir(myFile).exists()){
                Util::StatusBar::showError(ui->statusBar, "Only folders are allowed for this operation.");
                return;
            }
        }

    }
    else{
        for(const QString &myFile : files){

            //check if it's a file
            if(QDir(myFile).exists()){
                Util::StatusBar::showError(ui->statusBar, "Only files are allowed for this operation.");
                return;
            }

            // Check if the given files have the expected extensions
            QFileInfo fileInfo(myFile);

            QStringList expectedExtensions;

            bool extensionIsValid = false;

            if(
                    from == "DAT / TXMP ONI" ||
                    from == "DAT / SNDD ONI" ||
                    from == "DAT / SUBT ONI"
                    ){
                expectedExtensions << "DAT" << "ONI";
            }
            else if(from == "TGA / DDS / PNG / JPG"){
                expectedExtensions << "TGA" << "DDS" << "PNG" << "JPG";
            }
            else if(
                    from == "TRAM ONI" ||
                    from == "TRBS / ONCC ONI" ||
                    from == "M3GM ONI" ||
                    from == "ONWC ONI" ||
                    from == "OBAN ONI (cam)" ||
                    from == "AKEV ONI"
                    ){
                expectedExtensions << "ONI";
            }
            else if(from == "TRBS XML" || from == "MASTER XML"){
                expectedExtensions << "XML";
            }
            else if(from == "TRBS DAE"){
                expectedExtensions << "DAE";
            }
            else if(from == "FILM DAT"){
                expectedExtensions << "DAT";
            }
            else if(from == "WAV / AIF"){
                expectedExtensions << "WAV" << "AIF";
            }
            else{
                expectedExtensions << from;
            }

            for(const QString &currExpectedExtension : expectedExtensions){
                if(fileInfo.suffix().toUpper() == currExpectedExtension){
                    extensionIsValid = true;
                    break;
                }
            }

            if(!extensionIsValid){
                QString errorMsg = "Can't add the file '" + fileInfo.fileName() + "'. It isn't a " + expectedExtensions.join(" or ") + " file.";
                Util::Dialogs::showError(errorMsg);
                Util::StatusBar::showError(ui->statusBar, errorMsg);
                return;
            }
        }
    }

    //Build command
    QString command, lastFileName;

    QString myOutputFolder=getFileOutputFolder(fromTo);

    //if folder doesn't exist onisplit will create it for us :)
    for(QString currentFile : files){

        currentFile=Util::FileSystem::normalizeAndQuote(currentFile); //insert quotes ("") in file

        if(lastFileName.isEmpty()){ //Optimization: all commands are the same for each file, just replace the filename

            command=getCommand(myTable,myOutputFolder,from,to,currentFile);

            if(command.isEmpty()){ //something wrong was happening (not inputted a texture name?)
                return; //stop adding files
            }
            currentFile=Util::FileSystem::cutName(currentFile);
        }else{ //one parsing was already made just replace the filename by the old one in the command

            currentFile=Util::FileSystem::cutName(currentFile);

            command.replace(lastFileName,currentFile,Qt::CaseSensitive); //case sentive is faster
        }

        lastFileName=currentFile;

        addRowTable(myTable,lastFileName,fromTo,command);
    }
    updateItemsLoaded(myTable);
    rowsWereChangedInDropTableWidget();
}

QString MainWindow::fileParsingXML(QString tabTitle, QString myOutputFolder, QString from, QString to , QString file){

    QString command;

    if(from=="ONI" && to=="XML"){
        return command=this->commandMap.value(tabTitle+"->"+from+"->"+to)+" "+myOutputFolder+" "+file;
    }
    else if(from=="XML" && to=="ONI"){
        return command=this->commandMap.value(tabTitle+"->"+from+"->"+to)+" "+myOutputFolder+" "+file;
    }

    return "";

}

QString MainWindow::fileParsingTextures(QString tabTitle, QString myOutputFolder, QString from, QString to , QString file){

    QString command=this->commandMap.value(tabTitle+"->"+from+"->"+to)+" "+myOutputFolder;

    if(ui->gbTextures->isEnabled()){ //faster than compare strings (if is DAT/ONI)

        if(ui->cbMipMapsTextures->isEnabled() && ui->cbMipMapsTextures->isChecked()){
            command+=" "+this->commandMap.value(tabTitle+"->"+ui->cbMipMapsTextures->text());
        }

        if(ui->cbNoUwrap->isEnabled() && ui->cbNoUwrap->isChecked()){
            command+=" "+this->commandMap.value(tabTitle+"->"+ui->cbNoUwrap->text());
        }

        if(ui->cbNoVwrap->isEnabled() && ui->cbNoVwrap->isChecked()){
            command+=" "+this->commandMap.value(tabTitle+"->"+ui->cbNoVwrap->text());
        }

        if(ui->cbLarge->isEnabled() && ui->cbLarge->isChecked()){
            command+=" "+this->commandMap.value(tabTitle+"->"+ui->cbLarge->text());
        }

        command+=" "+this->commandMap.value(tabTitle+"->"+getTextureRBCheckedTypeTexture()->text());

        if(ui->cbEnvMap->isEnabled() && ui->cbEnvMap->isChecked()){
            if(ui->leEnvMapTexture->text().isEmpty()){
                Util::StatusBar::showError(ui->statusBar, "Checkbox '"+ui->cbEnvMap->text()+"' is selected. The name texture name cannot be empty.");
                return "";
            }
            command+=" "+this->commandMap.value(tabTitle+"->"+ui->cbEnvMap->text()) + ui->leEnvMapTexture->text().remove(".oni",Qt::CaseInsensitive);
        }
    }

    return command+=" "+file; //add source
}

QString MainWindow::fileParsingCharacters(QString tabTitle, QString myOutputFolder, QString from, QString to , QString file){

    QString command=this->commandMap.value(tabTitle + "->" + from + "->" + to) + " " + myOutputFolder + " " + file ;


    if(ui->cbCellShading->isEnabled() && ui->cbCellShading->isChecked()){
        command+=" "+this->commandMap.value(tabTitle + "->" + ui->cbCellShading->text());
    }

    if(ui->cbNormals->isEnabled() && ui->cbNormals->isChecked()){
        command+=" "+this->commandMap.value(tabTitle + "->" + ui->cbNormals->text());
    }

    if(ui->cbStandingPose->isEnabled() && ui->cbStandingPose->isChecked()){
        command+=" "+this->commandMap.value(tabTitle + "->" + ui->cbStandingPose->text());
    }

    if(ui->cbWithTRBS_ONCC->isEnabled() && ui->cbWithTRBS_ONCC->isChecked()){
        if(ui->leTRBS_ONCC->text().isEmpty()){
            Util::StatusBar::showError(ui->statusBar, "Checkbox '" + ui->cbWithTRBS_ONCC->text() + "' is selected. The name cannot be empty.");
            return "";
        }

        command+= " " + this->commandMap.value(tabTitle + "->" + ui->cbWithTRBS_ONCC->text()) + Util::FileSystem::normalizeAndQuote(ui->leTRBS_ONCC->text());
    }

    return command;
}


QString MainWindow::fileParsingObjects(QString tabTitle, QString myOutputFolder, QString from, QString to , QString file){

    QString command=this->commandMap.value(tabTitle+"->"+from+"->"+to)+" "+myOutputFolder;

    if(ui->cbTexture->isEnabled() && ui->cbTexture->isChecked()){
        if(ui->leTextureName->text().isEmpty()){
            Util::StatusBar::showError(ui->statusBar, "Checkbox '"+ui->cbTexture->text()+"' is selected. The file source cannot be empty.");
            return "";
        }
        command+=" "+this->commandMap.value(tabTitle+"->"+ui->cbTexture->text()) + ui->leTextureName->text();
    }
    else if(ui->cbWithAnimation->isEnabled() && ui->cbWithAnimation->isChecked()){
        if(ui->leAnimationName->text().isEmpty()){
            Util::StatusBar::showError(ui->statusBar, "Checkbox '"+ui->cbWithAnimation->text()+"' is selected. The file source cannot be empty.");
            return "";
        }
        command+=" "+Util::FileSystem::normalizeAndQuote(ui->leAnimationName->text()) + " " + this->commandMap.value(tabTitle+"->"+ui->cbWithAnimation->text()) + file;
        return command;
    }

    if(from=="OBAN ONI (cam)"){
        command+=" -geom:camera";
    }

    return command+=" "+file; //add source
}

QString MainWindow::fileParsingLevels(QString tabTitle, QString myOutputFolder, QString from, QString to , QString file){

    QString datName, command;

    if(!(from=="ONI FILES" && to=="DAT")){ // to all except this one

        command=this->commandMap.value(tabTitle+"->"+from+"->"+to);

        if(ui->cbSpecificFilesLevels->isEnabled() && ui->cbSpecificFilesLevels->isChecked()){

            if(ui->leSpecificFilesLevels->text().isEmpty()){
                Util::StatusBar::showError(ui->statusBar, "Checkbox '"+ui->cbSpecificFilesLevels->text()+"' is selected. The files pattern cannot be empty.");
                return "";
            }

            command+=":"+ui->leSpecificFilesLevels->text();
        }

        if(from=="DAT" && to=="ONI FILES"){ // extract files to a subdir with the files name ex: level0_Final
            command += " " + myOutputFolder.insert(myOutputFolder.size()-2,QString(Util::FileSystem::cutName(file)).replace(".dat","")) + " " + file;
        }
        else{
            command+=" "+myOutputFolder+" "+file;
        }

    }

    if((from=="ONI FILES" || from=="MASTER XML") && to=="DAT"){ // almost the same command for both
        QString datName;

        if(from=="MASTER XML"){
            command+=GlobalVars::OniSplitProcSeparator; //insert mark so we know this action will take 2 commands
        }

        if(ui->cbDatLevels->isEnabled() && ui->cbDatLevels->isChecked()){
            if(ui->leTargetDatLevels->text().isEmpty()){
                Util::StatusBar::showError(ui->statusBar, "Checkbox '"+ui->cbDatLevels->text()+"' is selected. The name cannot be empty.");
                return "";
            }
            datName+=QString(myOutputFolder).insert(myOutputFolder.size()-1,ui->leTargetDatLevels->text()); //set name inputted by user
            if(!ui->leTargetDatLevels->text().toUpper().endsWith(".DAT")){
                datName.insert(datName.size()-1,".dat"); //append extension if necessary (-1 to maintain final quote)
            }
        }
        else{
            if(from=="ONI FILES"){
                datName=QString(myOutputFolder).insert(myOutputFolder.size()-1,Util::FileSystem::cutName(file).remove("/")+".dat"); //if none iputted set the same name of input file
            }
            else if(from=="MASTER XML"){
                datName=QString(myOutputFolder).insert(myOutputFolder.size()-1,Util::FileSystem::cutName(file).remove("/").replace(".xml",".dat",Qt::CaseInsensitive)); //if none iputted set the same name of input file
            }
        }
        if(from=="ONI FILES"){
            if(ui->actionWindows->isEnabled() && ui->actionWindows->isChecked()){ //is target plataform select windows?
                return command=this->commandMap.value(tabTitle+"->"+from+"->"+to+"(PC)")+" "+ file + " "+datName;
            }
            else{
                return command=this->commandMap.value(tabTitle+"->"+from+"->"+to+"(demoPCMAC)")+" "+ file + " "+datName;
            }
        }
        else if(from=="MASTER XML"){
            if(ui->actionWindows->isEnabled() && ui->actionWindows->isChecked()){ //is target plataform select windows?
                command+=this->commandMap.value(tabTitle+"->ONI FILES->"+to+"(PC)")+" "+myOutputFolder+" "+datName; //add second command
            }
            else{
                command+=this->commandMap.value(tabTitle+"->ONI FILES->"+to+"(demoPCMAC)")+" "+myOutputFolder+" "+datName; //add second command
            }
        }
    }

    if(ui->cbBnvLevels->isEnabled() && ui->cbBnvLevels->isChecked()){

        if(ui->leBnvLevels->isEnabled() && ui->leBnvLevels->text().isEmpty()){
            Util::StatusBar::showError(ui->statusBar, "Checkbox '"+ui->cbBnvLevels->text()+"' is selected. The BNV file cannot be empty.");
            return "";
        }
        command+=" "+Util::FileSystem::normalizeAndQuote(ui->leBnvLevels->text());
    }

    if(ui->cbAdditionalSourcesLevels->isEnabled() && ui->cbAdditionalSourcesLevels->isChecked()){

        if(ui->leAdditSourcesLevels->text().isEmpty()){
            Util::StatusBar::showError(ui->statusBar, "Checkbox '"+ui->cbAdditionalSourcesLevels->text()+"' is selected. The source files cannot be empty.");
            return "";
        }

        QString additionalFiles=ui->leAdditSourcesLevels->text();

        int currentIndex=0, nextIndex=0;

        //parse all files (separated by spaces)
        while(true){
            nextIndex=additionalFiles.indexOf(";",currentIndex+1);

            command += " "+Util::FileSystem::normalizeAndQuote(additionalFiles.mid(currentIndex,(nextIndex-currentIndex)));

            if(nextIndex==-1){ //we got to the end, stop parsing
                break;
            }
            currentIndex=nextIndex+1; //update currentIndex +1 for start after the separator
        }
    }

    if(ui->cbGridsLevels->isEnabled() && ui->cbGridsLevels->isChecked()){
        command+=GlobalVars::OniSplitProcSeparator+this->commandMap.value(tabTitle+"->"+ui->cbGridsLevels->text())+" "+Util::FileSystem::normalizeAndQuote(ui->leBnvLevels->text())+" "+file+" -out:"+myOutputFolder;
    }

    return command;
}

QString MainWindow::fileParsingMisc(QString myOutputFolder, QString from, QString to , QString file){
    return this->commandMap.value("misc->"+from+"->"+to)+" "+myOutputFolder+" "+file;
}

void MainWindow::addRowTable(DropTableWidget *myTable, QString fileName, QString fromTo, QString command, bool isToDisabled){
    //Get actual number rows
    int twSize=myTable->rowCount();

    //increase the rows for the new item
    myTable->setRowCount(twSize+1);

    //Add to table and list to
    QTableWidgetItem *newFile = new QTableWidgetItem(fileName);
    QTableWidgetItem *newConversion = new QTableWidgetItem(fromTo);
    QTableWidgetItem *newCommand = new QTableWidgetItem(command);

    if(isToDisabled){
        myTable->setDisableStyleWidgetItem(newFile);
        myTable->setDisableStyleWidgetItem(newConversion);
        myTable->setDisableStyleWidgetItem(newCommand);
    }

    myTable->setItem(twSize,0,newFile);
    myTable->setItem(twSize,1,newConversion);
    myTable->setItem(twSize,2,newCommand);

    myTable->updateTableToolTips(twSize); //Update tool tips
}

void MainWindow::on_pbConvert_clicked()
{
    startConversion();
}

void MainWindow::startConversion(){

    DropTableWidget* currTable = getCurrentTableWidget();

    bool ready=false;
    for(int i=0; i<currTable->rowCount(); i++){ //There are items to process?
        if(currTable->item(i,2)->background()!=currTable->disabledBackStyle){
            ready=true;
            break;
        }
    }

    if(!ready){
        Util::StatusBar::showError(ui->statusBar, "Please add sources to convert first.");
        return;
    }

    if(this->myBar->isVisible()){
        Util::Dialogs::showError("Another conversion is progress. Please wait until it finishes.");
        return;
    }

    for(int i=0; i<currTable->rowCount(); i++){
        //Only process enabled items
        if(currTable->item(i,2)->background()!=currTable->disabledBackStyle){
            this->listToProccess.append(currTable->item(i,2)->text());
        }
    }

    this->myConverter->start();
}

void MainWindow::TsetupProgressBar(int max){
    this->myBar->setValue(0);
    this->myBar->show();
    this->myBar->setMaximum(max);
    ui->tbAbortConversion->show();
}

void  MainWindow::TupdateProgressBar(){
    this->myBar->setValue(this->myBar->value()+1); //more one task done
}

void MainWindow::TresultConversion(QString result, int numErrors){
    QApplication::alert(this); //Show a notification if window is not active
    this->myBar->hide();
    ui->tbAbortConversion->hide();

    if(numErrors!=0){
        QString sNumErrors=QString::number(numErrors);
        if(numErrors>1){
            UtilVago::showErrorPopUpLogButton(result+"\n This is the last of "+sNumErrors+" errors.");
            Util::StatusBar::showError(ui->statusBar, "Something gone wrong. Check log file ("+sNumErrors+" errors).");
        }
        else{
            UtilVago::showErrorPopUpLogButton(result);
            Util::StatusBar::showError(ui->statusBar, "Something gone wrong. Check log file.");
        }
    }
    else{
        Util::StatusBar::showSuccess(ui->statusBar, "Everything went well!");
    }
}

void MainWindow::TconversionAborted(){
    this->myBar->hide();
    ui->tbAbortConversion->hide();

    Util::StatusBar::showError(ui->statusBar, "Conversion was aborted.");
}

void MainWindow::mapCommands(){
    ////////////////////////////////////////////////////////////////////////XML Commands
    this->commandMap.insert("xml->ONI->XML","-extract:xml");
    this->commandMap.insert("xml->XML->ONI","-create");
    //######################General Options

    //Possible Combinations
    this->commandMap.insertMulti("xml->ONI","XML");
    this->commandMap.insertMulti("xml->XML","ONI");

    ////////////////////////////////////////////////////////////////////////Textures Commands
    this->commandMap.insert("textures->DAT / TXMP ONI->DDS","-extract:dds");
    this->commandMap.insert("textures->DAT / TXMP ONI->TGA","-extract:tga");
    this->commandMap.insert("textures->DAT / TXMP ONI->PNG","-extract:png");
    this->commandMap.insert("textures->DAT / TXMP ONI->JPG","-extract:jpg");
    this->commandMap.insert("textures->TGA / DDS / PNG / JPG->TXMP ONI","-create:txmp");
    //######################Textures Options
    this->commandMap.insert("textures->"+ui->rbBGR32->text(),"-format:bgr32");
    this->commandMap.insert("textures->"+ui->rbBGRA32->text(),"-format:bgra32");
    this->commandMap.insert("textures->"+ui->rbBGR555->text(),"-format:bgr555");
    this->commandMap.insert("textures->"+ui->rbBGRA5551->text(),"-format:bgra5551");
    this->commandMap.insert("textures->"+ui->rbBGRA444->text(),"-format:bgra4444");
    this->commandMap.insert("textures->"+ui->rbDxt1->text(),"-format:dxt1");
    this->commandMap.insert("textures->"+ui->cbMipMapsTextures->text(),"-genmipmaps");
    this->commandMap.insert("textures->"+ui->cbNoUwrap->text(),"-nouwrap");
    this->commandMap.insert("textures->"+ui->cbNoVwrap->text(),"-novwrap");
    this->commandMap.insert("textures->"+ui->cbLarge->text(),"-large");
    this->commandMap.insert("textures->"+ui->cbEnvMap->text(),"-envmap:");
    //Possible Combinations
    this->commandMap.insertMulti("textures->DAT / TXMP ONI","TGA");
    this->commandMap.insertMulti("textures->DAT / TXMP ONI","DDS");
    this->commandMap.insertMulti("textures->DAT / TXMP ONI","PNG");
    this->commandMap.insertMulti("textures->DAT / TXMP ONI","JPG");
    this->commandMap.insertMulti("textures->TGA / DDS / PNG / JPG","TXMP ONI");

    ////////////////////////////////////////////////////////////////////////Characters Commands
    this->commandMap.insert("characters->TRAM ONI->XML / XML & DAE","-extract:xml");
    this->commandMap.insert("characters->TRBS / ONCC ONI->DAE","-extract:dae");
    this->commandMap.insert("characters->TRBS XML->TRBS ONI","-create");
    this->commandMap.insert("characters->TRBS DAE->TRBS ONI","-create:trbs");
    this->commandMap.insert("characters->FILM DAT->XML","film2xml");

    //######################Characters Options
    this->commandMap.insert("characters->"+ui->cbWithTRBS_ONCC->text(),"-anim-body:");
    this->commandMap.insert("characters->"+ui->cbCellShading->text(),"-cel");
    this->commandMap.insert("characters->"+ui->cbNormals->text(),"-normals");
    this->commandMap.insert("characters->"+ui->cbStandingPose->text(),"-noanim");
    //Possible Combinations
    this->commandMap.insertMulti("characters->TRAM ONI","XML / XML & DAE");
    this->commandMap.insertMulti("characters->TRBS / ONCC ONI","DAE");
    this->commandMap.insertMulti("characters->DAE","TRBS ONI");
    this->commandMap.insertMulti("characters->TRBS DAE","TRBS ONI");
    this->commandMap.insertMulti("characters->TRBS XML","TRBS ONI");
    this->commandMap.insertMulti("characters->FILM DAT","XML");

    ////////////////////////////////////////////////////////////////////////Objects Commands
    this->commandMap.insert("objects->M3GM ONI->OBJ","-extract:obj");
    this->commandMap.insert("objects->M3GM ONI->DAE","-extract:dae");
    this->commandMap.insert("objects->ONWC ONI->OBJ","-extract:obj");
    this->commandMap.insert("objects->ONWC ONI->DAE","-extract:dae");
    this->commandMap.insert("objects->OBAN ONI (cam)->DAE","-extract:dae");
    this->commandMap.insert("objects->OBJ->M3GM ONI","-create:m3gm");
    //######################Objects Options
    this->commandMap.insert("objects->"+ui->cbTexture->text(),"-tex:");
    this->commandMap.insert("objects->"+ui->cbWithAnimation->text(),"-geom:");
    //Possible Combinations
    this->commandMap.insertMulti("objects->M3GM ONI","OBJ");
    this->commandMap.insertMulti("objects->M3GM ONI","DAE");
    this->commandMap.insertMulti("objects->ONWC ONI","OBJ");
    this->commandMap.insertMulti("objects->ONWC ONI","DAE");
    this->commandMap.insertMulti("objects->OBAN ONI (cam)","DAE");
    this->commandMap.insertMulti("objects->OBJ","M3GM ONI");


    ////////////////////////////////////////////////////////////////////////Levels Commands
    this->commandMap.insert("levels->DAT->ONI FILES","-export");
    //this->commandMap.insert("levels->ONI FILES->DAT","-import"); //Not used.
    this->commandMap.insert("levels->ONI FILES->DAT(PC)","-import:nosep");
    this->commandMap.insert("levels->ONI FILES->DAT(demoPCMAC)","-import:sep");
    this->commandMap.insert("levels->AKEV ONI->DAE","-extract:dae");
    this->commandMap.insert("levels->DAE->AKEV ONI","-create:akev");
    this->commandMap.insert("levels->MASTER XML->DAT","-create:level");
    this->commandMap.insert("levels->MASTER XML->ONI FILES","-create:level");
    //######################Levels Options
    this->commandMap.insert("levels->"+ui->cbGridsLevels->text(),"-grid:create");
    //Possible Combinations
    this->commandMap.insertMulti("levels->DAT","ONI FILES");
    this->commandMap.insertMulti("levels->ONI FILES","DAT");
    this->commandMap.insertMulti("levels->AKEV ONI","DAE");
    this->commandMap.insertMulti("levels->DAE","AKEV ONI");
    this->commandMap.insertMulti("levels->MASTER XML","DAT");
    this->commandMap.insertMulti("levels->MASTER XML","ONI FILES");

    ////////////////////////////////////////////////////////////////////////Misc Commands
    this->commandMap.insert("misc->DAT / SNDD ONI->WAV","-extract:wav");
    this->commandMap.insert("misc->DAT / SNDD ONI->AIF","-extract:aif");
    this->commandMap.insert("misc->DAT / SUBT ONI->TXT","-extract:txt");
    this->commandMap.insert("misc->WAV / AIF->SNDD ONI","-create");
    this->commandMap.insert("misc->TXT->SUBT ONI","-create:subt");
    //Possible Combinations
    this->commandMap.insertMulti("misc->DAT / SNDD ONI","WAV");
    this->commandMap.insertMulti("misc->DAT / SNDD ONI","AIF");
    this->commandMap.insertMulti("misc->DAT / SUBT ONI","TXT");
    this->commandMap.insertMulti("misc->WAV / AIF","SNDD ONI");
    this->commandMap.insertMulti("misc->TXT","SUBT ONI");

}

void MainWindow::on_cbFromXML_currentIndexChanged(const QString &arg1)
{
    updateComboBox(arg1, ui->cbToXML);
}


void MainWindow::on_cbFromTextures_currentIndexChanged(const QString &arg1)
{
    updateComboBox(arg1, ui->cbToTextures);
}

void MainWindow::on_cbFromObjects_currentIndexChanged(const QString &arg1)
{
    updateComboBox(arg1, ui->cbToObjects);
}

void MainWindow::on_cbFromCharacters_currentIndexChanged(const QString &arg1)
{
    updateComboBox(arg1, ui->cbToCharacters);
}

void MainWindow::on_cbFromLevels_currentIndexChanged(const QString &arg1)
{
    updateComboBox(arg1, ui->cbToLevels);
}

void MainWindow::on_cbFromMisc_currentIndexChanged(const QString &arg1)
{
    updateComboBox(arg1, ui->cbToMisc);
}

void MainWindow::updateComboBox(const QString &arg1, QComboBox *comboBox){

    QString identifier;

    if(comboBox == ui->cbToXML){
        identifier = ui->tabWidget->tabText(XMLTabIndex);
    }
    else if(comboBox == ui->cbToTextures){
        identifier = ui->tabWidget->tabText(TexturesTabIndex);

        //Options are only used for DAT/TXMP ONI -> Image
        if(QString::compare(arg1,"DAT / TXMP ONI",Qt::CaseSensitive)==0){ //case sensitive is faster
            ui->gbTextures->setEnabled(false);
        }
        else{
            ui->gbTextures->setEnabled(true);
            ui->leEnvMapTexture->setEnabled(ui->cbEnvMap->isChecked());
        }
    }
    else if(comboBox == ui->cbToCharacters){
        identifier = ui->tabWidget->tabText(CharactersTabIndex);

        ui->cbWithTRBS_ONCC->setEnabled(false);
        ui->cbCellShading->setEnabled(false);
        ui->cbNormals->setEnabled(false);
        ui->cbStandingPose->setEnabled(false);
        ui->leTRBS_ONCC->setEnabled(false);

        //#error add drag and drop to Extract TRAM with TRBS/ONCC
        if(QString::compare(arg1,"TRAM ONI",Qt::CaseSensitive)==0){ //case sensitive is faster
            ui->cbWithTRBS_ONCC->setEnabled(true);
            ui->leTRBS_ONCC->setEnabled(ui->cbWithTRBS_ONCC->isChecked());
        }
        else if(QString::compare(arg1,"TRBS DAE",Qt::CaseSensitive)==0){
            ui->cbNormals->setEnabled(true);
            ui->cbCellShading->setEnabled(true);
        }
        else if(QString::compare(arg1,"TRBS / ONCC ONI",Qt::CaseSensitive)==0){
            ui->cbStandingPose->setEnabled(true);
        }

    }
    else if(comboBox == ui->cbToObjects){
        identifier = ui->tabWidget->tabText(ObjectsTabIndex);

        ui->cbTexture->setEnabled(false);
        ui->leTextureName->setEnabled(false);
        ui->cbWithAnimation->setEnabled(false);
        ui->leAnimationName->setEnabled(false);

        if(QString::compare(arg1,"M3GM ONI",Qt::CaseSensitive)==0){ //case sensitive is faster
            ui->cbWithAnimation->setEnabled(true);
            ui->leAnimationName->setEnabled(ui->cbWithAnimation->isChecked());
        }
        else if(QString::compare(arg1,"OBJ",Qt::CaseSensitive)==0){
            ui->cbTexture->setEnabled(true);
            ui->leTextureName->setEnabled(ui->cbTexture->isChecked());
        }
    }
    else if(comboBox == ui->cbToLevels){
        identifier = ui->tabWidget->tabText(LevelsTabIndex);

        ui->cbSpecificFilesLevels->setEnabled(false);
        ui->leSpecificFilesLevels->setEnabled(false);
        ui->cbDatLevels->setEnabled(false);
        ui->leTargetDatLevels->setEnabled(false);
        ui->cbBnvLevels->setEnabled(false);
        ui->leBnvLevels->setEnabled(false);
        ui->cbAdditionalSourcesLevels->setEnabled(false);
        ui->leAdditSourcesLevels->setEnabled(false);
        ui->cbGridsLevels->setEnabled(false);

        if(arg1=="DAT"){ //case sensitive is faster
            ui->cbSpecificFilesLevels->setEnabled(true);
            ui->leSpecificFilesLevels->setEnabled( ui->cbSpecificFilesLevels->isChecked());
        }
        else if(arg1=="ONI FILES"){ //case sensitive is faster
            ui->cbDatLevels->setEnabled(true);
            ui->leTargetDatLevels->setEnabled(ui->cbDatLevels->isChecked());
        }
        else if(arg1=="DAE"){
            ui->cbBnvLevels->setEnabled(true);
            ui->leBnvLevels->setEnabled(ui->cbBnvLevels->isChecked());
            ui->cbAdditionalSourcesLevels->setEnabled(true);
            ui->leAdditSourcesLevels->setEnabled(ui->cbAdditionalSourcesLevels->isChecked());
        }
    }
    else{ // Misc
        identifier = ui->tabWidget->tabText(MiscTabIndex);
    }

    identifier = identifier.toLower(); // get current tab title text (lower case)

    comboBox->clear();

    QStringList toUpdate=QStringList();

    QStringList values=commandMap.values(identifier+"->"+arg1);

    for (int i = values.size()-1; i >= 0; i--){ //By defaut MultiItems have the inversed order (http://qt-project.org/doc/qt-4.8/qhash.html#insertMulti)
        toUpdate << values.at(i);
    }

    comboBox->addItems(toUpdate);
}


void MainWindow::on_actionWindows_triggered()
{
    ui->actionWindows->setChecked(true);
    ui->actionMac_Windows_demo->setChecked(false);
}

void MainWindow::on_actionMac_Windows_demo_triggered()
{
    ui->actionMac_Windows_demo->setChecked(true);
    ui->actionWindows->setChecked(false);
}

void MainWindow::on_pbRemoveSourceXML_clicked()
{
    removeTableContents( ui->twSourcesXML);
}

void MainWindow::on_pbRemoveSourceTextures_clicked()
{
    removeTableContents(ui->twSourcesTextures);
}

void MainWindow::on_pbRemoveSourceObjects_clicked()
{
    removeTableContents(ui->twSourcesObjects);
}

void MainWindow::on_pbRemoveSourceCharacters_clicked()
{
    removeTableContents(ui->twSourcesCharacters);
}

void MainWindow::on_pbRemoveSourceLevels_clicked()
{
    removeTableContents(ui->twSourcesLevels);
}

void MainWindow::on_pbRemoveSourceMisc_clicked()
{
    removeTableContents(ui->twSourcesMisc);
}

void MainWindow::on_pbClearSourcesXML_clicked()
{
    clearTableContents(ui->twSourcesXML);
}

void MainWindow::on_pbClearSourcesTextures_clicked()
{
    clearTableContents(ui->twSourcesTextures);
}

void MainWindow::on_pbClearSourcesObjects_clicked()
{
    clearTableContents(ui->twSourcesObjects);
}

void MainWindow::on_pbClearSourcesCharacters_clicked()
{
    clearTableContents(ui->twSourcesCharacters);
}

void MainWindow::on_pbClearSourcesLevels_clicked()
{
    clearTableContents(ui->twSourcesLevels);
}

void MainWindow::on_pbClearSourcesMisc_clicked()
{
    clearTableContents(ui->twSourcesMisc);
}

void MainWindow::removeTableContents(DropTableWidget *myTable){
    int size = myTable->selectionModel()->selectedRows().size();

    QMessageBox::StandardButton defaultButton = QMessageBox::NoButton; // default button for clear asking question, only customizable in mac os

    if(size==0){
        Util::Dialogs::showInfo("Select a row first.");
        return;
    }

#ifdef Q_OS_MAC
    if(this->useYesAsDefaultWhenRemovingItems){
        defaultButton = QMessageBox::Yes;
    }
    else{
        defaultButton = QMessageBox::No;
    }
#endif


    if(Util::Dialogs::showQuestion(this,"Are you sure you want to delete the selected rows?",defaultButton)){
        for(int i=0; i<size; i++){
            //myTable->removeRow(myTable->selectedItems().at(size-i-1)->row());
            myTable->removeRow(myTable->selectionModel()->selectedRows().at(size-i-1).row());
        }
        updateItemsLoaded(myTable);
        rowsWereChangedInDropTableWidget();
    }
}

void MainWindow::clearTableContents(DropTableWidget *myTable){

    QMessageBox::StandardButton defaultButton = QMessageBox::NoButton; // default button for clear asking question, only customizable in mac os

    if(myTable->rowCount()==0){
        Util::Dialogs::showInfo("Nothing to clear.");
        return;
    }

#ifdef Q_OS_MAC
    if(this->useYesAsDefaultWhenRemovingItems){
        defaultButton = QMessageBox::Yes;
    }
    else{
        defaultButton = QMessageBox::No;
    }
#endif

    if(Util::Dialogs::showQuestion(this,"Are you sure you want to clear the content?",defaultButton)){
        clearTableNoPrompt(myTable);
        updateItemsLoaded(myTable);
        rowsWereChangedInDropTableWidget();
    }

}

void MainWindow::clearTableNoPrompt(DropTableWidget *myTable){
    myTable->clearContents();
    myTable->setRowCount(0);
}

void MainWindow::on_actionPreferences_triggered()
{
    //Show preferences
    Preferences *preferencesWindow = new Preferences(this,this->vagoSettings);
    preferencesWindow->exec(); //it destroys itself when finished.
}


void MainWindow::closeEvent(QCloseEvent *event){
    if(this->vagoSettings->value("AskSaveProject").toBool() && this->unsavedChangesExist){
        QMessageBox::StandardButton result = askToSaveCurrentProject();
        if(result == QMessageBox::StandardButton::Cancel){
            event->ignore();
            return;
        }
    }

    // Exit application (this will also close all other windows which don't have parent, for instance ManualCommands)
    QApplication::quit();
}

QMessageBox::StandardButton MainWindow::askToSaveCurrentProject(){
    QMessageBox::StandardButton result =
            Util::Dialogs::showQuestionWithCancel(this,"There are unsaved changes. Do you want to save the current project?", QMessageBox::StandardButton::Yes);

    if(result == QMessageBox::StandardButton::Yes){
        on_actionSave_triggered();
    }

    return result;
}

void MainWindow::on_cbToLevels_currentIndexChanged(const QString &arg1)
{

    if(ui->cbFromLevels->currentText()=="MASTER XML" && arg1=="DAT"){
        ui->cbDatLevels->setEnabled(true);
    }
    else if(ui->cbFromLevels->currentText()=="MASTER XML" && arg1=="ONI FILES"){
        ui->cbDatLevels->setEnabled(false);
        ui->cbDatLevels->setChecked(false);
    }

}

void MainWindow::on_cbDatLevels_toggled(bool checked)
{
    ui->leTargetDatLevels->setEnabled(checked);
}

void MainWindow::on_cbBnvLevels_toggled(bool checked)
{
    ui->leBnvLevels->setEnabled(checked);
    ui->cbGridsLevels->setEnabled(checked);
    ui->cbGridsLevels->setChecked(checked);
    if(checked && !projectIsLoading){
        QString file=QFileDialog::getOpenFileName(this,"Choose the BNV.dae file...","./" , "All Files (*.*)");
        if(!file.isEmpty()){
            ui->leBnvLevels->setText(file);
        }
    }
}

void MainWindow::on_cbAdditionalSourcesLevels_toggled(bool checked)
{
    ui->leAdditSourcesLevels->setEnabled(checked);

    if(checked && !projectIsLoading){
        QStringList filesSelected=QFileDialog::getOpenFileNames(this,"Choose the additional .dae files...","./" , "All Files (*.*)");
        QString filesJoined;
        int size=filesSelected.size();

        if(!filesSelected.isEmpty()){
            for(int i=0; i<size-1; i++){
                filesJoined+=filesSelected.at(i)+" ";
            }
            filesJoined+=filesSelected.at(size-1); //last doesn't have space after
            ui->leAdditSourcesLevels->setText(filesJoined);
        }

    }
}

void MainWindow::on_cbWithTRBS_ONCC_toggled(bool checked)
{
    ui->leTRBS_ONCC->setEnabled(checked);
}

void MainWindow::on_actionCheck_OniSplit_version_triggered()
{
    QProcess myProcess;
    myProcess.setWorkingDirectory(UtilVago::getAppPath());
    myProcess.start(UtilVago::getOniSplitExecutable()+" -version");
    myProcess.waitForFinished();

    QString result=myProcess.readAllStandardOutput();

    Util::Dialogs::showInfo("This Vago version was built with base in OniSplit version "+GlobalVars::BuiltOniSplitVersion+"\n\nCurrent version is:\n"+result.trimmed());
}

void MainWindow::on_actionCheck_xmlTools_version_triggered()
{
    QProcess myProcess;
    myProcess.setWorkingDirectory(UtilVago::getAppPath());
    myProcess.start(UtilVago::getXmlToolsExecutable()+" --version");
    myProcess.waitForFinished();
    QString result=myProcess.readLine();

    Util::Dialogs::showInfo("This Vago version was built with base in XmlTools version "+GlobalVars::BuiltXmlToolsVersion+"\n\nCurrent version is:\n"+result.trimmed());
}

/**
  Update items loaded
**/
void MainWindow::on_tabWidget_currentChanged(int)
{
    updateItemsLoaded(getCurrentTableWidget());
}

void MainWindow::updateItemsLoaded(DropTableWidget *currentTable){

    int numItems=currentTable->rowCount();

    this->itemsLoaded->setText(QString().setNum(numItems)+ (numItems==1?" item ":" items ") +"loaded");
}

void MainWindow::rowsWereChangedInDropTableWidget(){
    // We have changed rows, we have now unsaved changes.
    if(!this->unsavedChangesExist){
        this->unsavedChangesExist = true;
        setVagoWindowTitle();
    }
}

void MainWindow::on_tbCommand_clicked()
{
    //We pass no parent because we want to have an independent window for ManualCommands,
    //so we can minimize it or maximize indepently from the MainWindow
    ManualCommands *commandsWindow = new ManualCommands();
    commandsWindow->show(); //it destroys itself when finished.
}

void MainWindow::on_actionWorkspace_triggered()
{
    ui->actionWorkspace->setChecked(true);
    ui->actionOther->setChecked(false);
    this->outputFolder=this->workspaceLocation;
    ui->tbOpenFolder->setToolTip("Open Vago workspace");
    Util::StatusBar::showSuccess(ui->statusBar, "Vago is now outputting the NEW items for Vago workspace.");
}

void MainWindow::on_actionOther_triggered()
{
    QString newDir=QFileDialog::getExistingDirectory(this,"Choose the folder for output NEW files directly...",this->AeLocation+"/GameDataFolder");
    newDir=Util::FileSystem::normalizePath(newDir);

    if(newDir.isEmpty()){
        ui->actionOther->setChecked(false);
        return; //do nothing
    }

    if(newDir==this->workspaceLocation){
        on_actionWorkspace_triggered(); //set it to vago workspace
        return;
    }

    ui->actionOther->setChecked(true);
    ui->actionWorkspace->setChecked(false);

    this->outputFolder=newDir;

    QString newDirName=Util::FileSystem::cutName(newDir);
    ui->tbOpenFolder->setToolTip("Open "+newDirName+" output folder");
    Util::StatusBar::showSuccess(ui->statusBar, "Vago is now outputting the NEW items for "+newDirName+".");
}

void MainWindow::on_actionView_log_triggered()
{
    UtilVago::openLogFile();
}

void MainWindow::on_actionOpen_AE_folder_triggered()
{
    QDesktopServices::openUrl(QUrl("file:///"+this->AeLocation));
}

void MainWindow::on_actionSave_Project_triggered()
{

    QString filePath = QFileDialog::getSaveFileName(this, tr("Save File"),
                                                    this->vagoSettings->value("LastProjectPath").toString(),
                                                    tr("Vago project files (*.vgp)"));

    if(!filePath.isEmpty()){
        saveProjectState(filePath);
    }

}

// New Project
void MainWindow::on_actionNew_Project_triggered()
{
    if(this->vagoSettings->value("AskSaveProject").toBool() && this->unsavedChangesExist){
        QMessageBox::StandardButton result = askToSaveCurrentProject();
        if(result == QMessageBox::StandardButton::Cancel){
            return;
        }
    }

    QList<DropTableWidget*> myTables = getAllTableWidgets();

    for(DropTableWidget* const currTable : myTables){
        clearTableNoPrompt(currTable);
    }

    this->lastProjectFilePath=""; // clear last project file path
    this->unsavedChangesExist = false;

    setVagoWindowTitle(); // update vago title
}

void MainWindow::on_actionSave_triggered()
{
    if(this->lastProjectFilePath.isEmpty()){
        on_actionSave_Project_triggered();
        return;
    }

    saveProjectState(this->lastProjectFilePath);
}

void MainWindow::on_actionLoad_Project_triggered()
{

    QString filePath = QFileDialog::getOpenFileName(this, tr("Load File"),
                                                    this->vagoSettings->value("LastProjectPath").toString(),
                                                    tr("Vago project files (*.vgp)"));
    if(!filePath.isEmpty()){
        loadProjectState(filePath);
    }
}

void MainWindow::on_actionProject1_triggered()
{
    loadProjectState(this->ui->actionProject1->text());
}

void MainWindow::on_actionProject2_triggered()
{
    loadProjectState(this->ui->actionProject2->text());
}

void MainWindow::on_actionProject3_triggered()
{
    loadProjectState(this->ui->actionProject3->text());
}

void MainWindow::on_actionProject4_triggered()
{
    loadProjectState(this->ui->actionProject4->text());
}

void MainWindow::on_actionProject5_triggered()
{
    loadProjectState(this->ui->actionProject5->text());
}

QString MainWindow::getTypeConversion(DropTableWidget *myTable){
    QString from,to;

    if(myTable==ui->twSourcesXML){
        from=ui->cbFromXML->currentText();
        to=ui->cbToXML->currentText();
    }
    else if(myTable==ui->twSourcesTextures){
        from=ui->cbFromTextures->currentText();
        to=ui->cbToTextures->currentText();
    }
    else if(myTable==ui->twSourcesObjects){
        from=ui->cbFromObjects->currentText();
        to=ui->cbToObjects->currentText();
    }
    else if(myTable==ui->twSourcesCharacters){
        from=ui->cbFromCharacters->currentText();
        to=ui->cbToCharacters->currentText();
    }
    else if(myTable==ui->twSourcesLevels){
        from=ui->cbFromLevels->currentText();
        to=ui->cbToLevels->currentText();
    }
    else{
        from=ui->cbFromMisc->currentText();
        to=ui->cbToMisc->currentText();
    }

    return from + " > " + to;
}

//Drop table widget context menu
void MainWindow::dtContextMenu(DropTableWidget* myTable, QContextMenuEvent *event){
    QModelIndex index = myTable->indexAt(event->pos());

    //item exists?
    if(!index.isValid())
        return;

    if(myTable->selectionModel()->selectedRows().size()==0){ //No multiple rows selected
        myTable->selectRow(myTable->itemAt(event->pos())->row()); //select all the row of the item clicked
    }

    QList<int> selectedRows = QList<int>();

    for(const QModelIndex &rowItem : myTable->selectionModel()->selectedRows()){
        selectedRows << rowItem.row();
    }

    std::unique_ptr<QMenu> menu = std::make_unique<QMenu>();
    std::unique_ptr<QAction> copy =  std::make_unique<QAction>("Copy",myTable);
    std::unique_ptr<QAction> moveUp = std::make_unique<QAction>("Move Up",myTable);
    std::unique_ptr<QAction> moveDown = std::make_unique<QAction>("Move Down",myTable);
    std::unique_ptr<QAction> changeOptions = std::make_unique<QAction>("Change To Current Options",myTable);
    std::unique_ptr<QMenu> changeOutput = std::make_unique<QMenu>("Change Output for:");
    std::unique_ptr<QAction> outWorkspace = std::make_unique<QAction>("Workspace",myTable);
    std::unique_ptr<QAction> outCurrOutput = std::make_unique<QAction>("Current Output Folder",myTable);
    std::unique_ptr<QAction> outOther = std::make_unique<QAction>("Other...",myTable);
    std::unique_ptr<QAction> edisable = std::make_unique<QAction>("Enable/Disable",myTable);

    menu->addAction(copy.get());
    menu->addSeparator();
    menu->addAction(moveUp.get());
    menu->addAction(moveDown.get());
    menu->addSeparator();
    menu->addAction(changeOptions.get());
    menu->addMenu(changeOutput.get());
    changeOutput->addActions(QList<QAction*>() << outWorkspace.get() << outCurrOutput.get() << outOther.get());
    menu->addAction(edisable.get());


    //if it's in the first row it can't be setted up
    if(selectedRows.at(0)==0){
        moveUp->setEnabled(false);
    }

    //if we are at bottom we can't go down
    if(selectedRows.at(selectedRows.size()-1)==myTable->rowCount()-1){
        moveDown->setEnabled(false);
    }

    //Can we change the settings? (the conversion must be the same)
    QString currentSettings = (getTypeConversion(myTable)); //call function at the mainWindow with a signal (different threads?)
    for(const int row : selectedRows){
        if( myTable->item(row,1)->text() != currentSettings){ //If we find out any of the selected items can't be convert disable operation
            changeOptions->setEnabled(false);
            break;
        }
    }

    QAction* selectedOption = menu->exec(event->globalPos());

    if(selectedOption==copy.get()){
        //Let's copy the contents to the clipboard

        QString toCopy;

        int size=selectedRows.size();

        //Let's format it a bit...
        for(int i=0; i<size; i++){
            for(int j=0; j<myTable->columnCount(); j++){
                toCopy+=myTable->item(selectedRows.at(i),j)->text();
                if(j!=myTable->columnCount()-1){
                    toCopy+="\t";
                }
            }
            if(i!=size-1){
                toCopy+="\n";
            }
        }

        QApplication::clipboard()->setText(toCopy);
        Util::StatusBar::showSuccess(ui->statusBar, QString::number(size) + (size==1?" item ":" items ")+ "copied to the clipboard");
    }
    else if(selectedOption==moveUp.get()){
        qSort(selectedRows); //let's order the selections by the row number, so we know exactly how to swap it
        myTable->swapPositions(selectedRows,-1);
        rowsWereChangedInDropTableWidget();
    }
    else if(selectedOption==moveDown.get()){
        qSort(selectedRows);
        myTable->swapPositions(selectedRows,+1);
        rowsWereChangedInDropTableWidget();
    }
    else if(selectedOption==changeOptions.get()){
        changeToCurrentSettings(selectedRows,myTable);
    }
    else if(selectedOption==outWorkspace.get()){
        changeItemsOutput(myTable,selectedRows,this->workspaceLocation);
    }
    else if(selectedOption==outCurrOutput.get()){
        changeItemsOutput(myTable,selectedRows,this->outputFolder);
    }
    else if(selectedOption==outOther.get()){

        QString newDir=QFileDialog::getExistingDirectory(this,"Choose the folder for the output of the files selected...",this->AeLocation+"/GameDataFolder");
        newDir=Util::FileSystem::normalizePath(newDir);

        if(newDir.isEmpty()){
            return; //do nothing
        }

        changeItemsOutput(myTable,selectedRows,newDir);

    }
    else if(selectedOption==edisable.get()){

        int enabledCount=0, disabledCount=0;

        for(int i=0; i<selectedRows.size(); i++){

            for(int j=0; j<myTable->columnCount(); j++){
                QTableWidgetItem *currentItem=myTable->item(selectedRows.at(i),j);

                if(currentItem->background()!=myTable->disabledBackStyle){
                    myTable->setDisableStyleWidgetItem(currentItem);
                    if(j==0){ //Only count the row, not the columns
                        disabledCount++;
                    }
                }
                else{ //reset to initial state (enable)
                    myTable->resetStyleWidgetItem(currentItem);
                    if(j==0){
                        enabledCount++;
                    }
                }
            }
        }

        QString result;

        if(enabledCount!=0){
            result+=QString::number(enabledCount) + (enabledCount==1?" item ":" items ") + "Enabled";
        }
        if(enabledCount!=0 && disabledCount!=0){
            result+=" and ";
        }
        if(disabledCount!=0){
            result+=QString::number(disabledCount) + (disabledCount==1?" item ":" items ") + "Disabled";
        }

        rowsWereChangedInDropTableWidget();
        Util::StatusBar::showSuccess(ui->statusBar, result);
    }
}

void MainWindow::changeToCurrentSettings(QList<int> rows, DropTableWidget* myTable){
    //construct a command for each one
    //Output a status message saying the number of changed files
    QString fromTo=getTypeConversion(myTable);
    QString from = QString(fromTo).remove(fromTo.indexOf(" >"),fromTo.size()-1); //parse the string to get the from, only 1 time parsed by each group of files = very fast
    QString to = QString(fromTo).remove(0,fromTo.lastIndexOf("> ")+2); //+2 to start after "> "

    QString command;

    for(int row : rows){
        command=getCommand(myTable,getFileOutputFolder(fromTo,myTable->getOutputAbsolute(row)),from,to,myTable->getFileAbsolute(row));

        if(command.isEmpty()){ //something wrong was happening (not inputted a texture name?)
            return; //stop changing settings
        }

        myTable->item(row,2)->setText(command); //update settings to the current row
        myTable->updateTableToolTips(row);
    }

    rowsWereChangedInDropTableWidget();
    Util::StatusBar::showSuccess(ui->statusBar, QString::number(rows.size()) + (rows.size()==1?" item ":" items ")+ "changed to the current settings");
}

void MainWindow::changeItemsOutput(DropTableWidget* myTable, QList<int> rows, QString newOutput){

    QString command, currentAbsoluteFile, fromTo, from, to;

    for(const int row : rows){ //No optimization possible here, commands may be different
        fromTo=myTable->item(row,1)->text();
        from = QString(fromTo).remove(fromTo.indexOf(" >"),fromTo.size()-1); //parse the string to get the from
        to = QString(fromTo).remove(0,fromTo.lastIndexOf("> ")+2); //+2 to start after "> "

        currentAbsoluteFile=myTable->getFileAbsolute(row);
        command=getCommand(myTable,getFileOutputFolder(fromTo,newOutput),from,to,currentAbsoluteFile);

        if(command.isEmpty()){ //something wrong was happening (not inputted a texture name?)
            return; //stop changing output
        }

        myTable->item(row,2)->setText(command); //update command to the current row
        myTable->updateTableToolTips(row);
    }

    rowsWereChangedInDropTableWidget();
    Util::StatusBar::showSuccess(ui->statusBar, QString::number(rows.size()) + (rows.size()==1?" item ":" items ")+ "changed the output to "+(newOutput!=this->workspaceLocation?Util::FileSystem::cutName(newOutput):"Vago workspace"));
}

QString MainWindow::getCommand(DropTableWidget* myTable, QString myOutputFolder, QString from, QString to , QString file){

    QString tabTitle=ui->tabWidget->tabText(ui->tabWidget->currentIndex()).toLower(); // get current tab title

    if(myTable==ui->twSourcesXML){ //So we only need to parse one command.
        return fileParsingXML(tabTitle, myOutputFolder,from,to,file);
    }
    else if(myTable==ui->twSourcesTextures){
        return fileParsingTextures(tabTitle, myOutputFolder,from,to,file);
    }
    else if(myTable==ui->twSourcesObjects){
        return fileParsingObjects(tabTitle, myOutputFolder,from,to,file);
    }
    else if(myTable==ui->twSourcesCharacters){
        return fileParsingCharacters(tabTitle, myOutputFolder,from,to,file);
    }
    else if(myTable==ui->twSourcesLevels){
        return fileParsingLevels(tabTitle, myOutputFolder,from,to,file);
    }
    else{
        return fileParsingMisc(myOutputFolder,from,to,file);
    }

}

void MainWindow::connectSlots(){

    //This signal is for thread that is working setup the progress bar (make it visible and set it's min-max)
    connect(myConverter, SIGNAL(setupPB(int)), this, SLOT(TsetupProgressBar(int)), Qt::BlockingQueuedConnection);

    //This signal is for thread that is working can update the progress bar of the gui
    connect(myConverter, SIGNAL(taskDone()), this, SLOT(TupdateProgressBar()),Qt::BlockingQueuedConnection);

    //This signal is for thread that is working can show the result of a conversion
    connect(myConverter, SIGNAL(resultConversion(QString,int)), this, SLOT(TresultConversion(QString,int)));

    //This signal is for thread that is working notify the gui thread that the conversion was aborted with sucess
    connect(myConverter, SIGNAL(conversionAborted()), this, SLOT(TconversionAborted()));

    // This signal is to the user be able to terminate a conversion (OniSplit process in class myConverter will be terminated)
    connect(this, SIGNAL(terminateCurrProcess()), myConverter, SLOT(terminateCurrProcess()));

    //Drop signal for General table
    connect(ui->twSourcesXML, SIGNAL(dropped(DropTableWidget*,QStringList)), this, SLOT(addFilesSource(DropTableWidget*,QStringList)));

    //Drop signal for Textures table
    connect(ui->twSourcesTextures, SIGNAL(dropped(DropTableWidget*,QStringList)), this, SLOT(addFilesSource(DropTableWidget*,QStringList)));

    //Drop signal for Objects table
    connect(ui->twSourcesObjects, SIGNAL(dropped(DropTableWidget*,QStringList)), this, SLOT(addFilesSource(DropTableWidget*,QStringList)));

    //Drop signal for Characters table
    connect(ui->twSourcesCharacters, SIGNAL(dropped(DropTableWidget*,QStringList)), this, SLOT(addFilesSource(DropTableWidget*,QStringList)));

    //Drop signal for Levels table
    connect(ui->twSourcesLevels, SIGNAL(dropped(DropTableWidget*,QStringList)), this, SLOT(addFilesSource(DropTableWidget*,QStringList)));

    //Drop signal for Misc table
    connect(ui->twSourcesMisc, SIGNAL(dropped(DropTableWidget*,QStringList)), this, SLOT(addFilesSource(DropTableWidget*,QStringList)));

    //Context menu for General table
    connect(ui->twSourcesXML, SIGNAL(dtContextMenu(DropTableWidget*,QContextMenuEvent*)), this, SLOT(dtContextMenu(DropTableWidget*,QContextMenuEvent*)));

    //Context menu for Textures table
    connect(ui->twSourcesTextures, SIGNAL(dtContextMenu(DropTableWidget*,QContextMenuEvent*)), this, SLOT(dtContextMenu(DropTableWidget*,QContextMenuEvent*)));

    //Context menu for Objects table
    connect(ui->twSourcesObjects, SIGNAL(dtContextMenu(DropTableWidget*,QContextMenuEvent*)), this, SLOT(dtContextMenu(DropTableWidget*,QContextMenuEvent*)));

    //Context menu for Characters table
    connect(ui->twSourcesCharacters, SIGNAL(dtContextMenu(DropTableWidget*,QContextMenuEvent*)), this, SLOT(dtContextMenu(DropTableWidget*,QContextMenuEvent*)));

    //Context menu for Levels table
    connect(ui->twSourcesLevels, SIGNAL(dtContextMenu(DropTableWidget*,QContextMenuEvent*)), this, SLOT(dtContextMenu(DropTableWidget*,QContextMenuEvent*)));

    //Context menu for Misc table
    connect(ui->twSourcesMisc, SIGNAL(dtContextMenu(DropTableWidget*,QContextMenuEvent*)), this, SLOT(dtContextMenu(DropTableWidget*,QContextMenuEvent*)));
}

void MainWindow::saveProjectState(const QString &filePath)
{
    try{
        ProjectFileVago::saveProjectDataToFile(filePath, fetchCurrentProjectData());

        this->vagoSettings->setValue("LastProjectPath",QFileInfo(filePath).absoluteDir().path());

        this->lastProjectFilePath = filePath;
        this->unsavedChangesExist = false;

        addNewRecentProject(filePath);

        setVagoWindowTitle();

        Util::StatusBar::showSuccess(ui->statusBar, "Project saved sucessfully.");
    }
    catch(const std::exception& e){
        QString errorMessage = "An error ocurred while trying to save the project file. Please try another path.";
        UtilVago::showAndLogErrorPopUpLogButton(errorMessage);
        Util::StatusBar::showError(ui->statusBar, "Couldn't save project file.");
    }
}

ProjectFileVago::ProjectData MainWindow::fetchCurrentProjectData(){

    ProjectFileVago::ProjectData currentProjectData;

    auto fFetchTabGenericData =
            [](ProjectFileVago::ProjectTable &projectTable,
            const QComboBox * const cbFrom,
            const QComboBox * const cbTo,
            DropTableWidget const * const table) ->void{

        projectTable.from = cbFrom->currentText();
        projectTable.to = cbTo->currentText();

        for(int i=0; i<table->rowCount(); i++){

            ProjectFileVago::ProjectTableRow currentRow;

            currentRow.fileFolder = table->item(i,0)->text();
            currentRow.fromTo = table->item(i,1)->text();
            currentRow.command= table->item(i,2)->text();

            if(table->item(i,2)->background()==table->disabledBackStyle){
                currentRow.isDisabled = true;
            }

            projectTable.rows.append(currentRow);
        }
    };

    // XML tab
    fFetchTabGenericData(currentProjectData.xmlTable, ui->cbFromXML, ui->cbToXML, ui->twSourcesXML);

    // Textures tab
    fFetchTabGenericData(currentProjectData.texturesTable, ui->cbFromTextures, ui->cbToTextures, ui->twSourcesTextures);
    currentProjectData.texturesTable.rbTexturesType = getTextureRBCheckedTypeTexture()->text();
    currentProjectData.texturesTable.cbGenMipMaps = ui->cbMipMapsTextures->isChecked();
    currentProjectData.texturesTable.cbNoUwrap = ui->cbNoUwrap->isChecked();
    currentProjectData.texturesTable.cbNoVwrap = ui->cbNoVwrap->isChecked();
    currentProjectData.texturesTable.cbLarge = ui->cbLarge->isChecked();
    currentProjectData.texturesTable.cbEnvMap = ui->cbEnvMap->isChecked();
    currentProjectData.texturesTable.leEnvMapTexture = ui->leEnvMapTexture->text();

    // Characters tab
    fFetchTabGenericData(currentProjectData.charactersTable, ui->cbFromCharacters, ui->cbToCharacters, ui->twSourcesCharacters);
    currentProjectData.charactersTable.cbCellShading = ui->cbCellShading->isChecked();
    currentProjectData.charactersTable.cbNormals = ui->cbNormals->isChecked();
    currentProjectData.charactersTable.cbStandingPose = ui->cbStandingPose->isChecked();
    currentProjectData.charactersTable.cbWithTRBS_ONCC = ui->cbWithTRBS_ONCC->isChecked();
    currentProjectData.charactersTable.leTRBS_ONCC = ui->leTRBS_ONCC->text();

    // Objects tab
    fFetchTabGenericData(currentProjectData.objectsTable, ui->cbFromObjects, ui->cbToObjects, ui->twSourcesObjects);
    currentProjectData.objectsTable.cbTexture = ui->cbTexture->isChecked();
    currentProjectData.objectsTable.leTextureName = ui->leTextureName->text();
    currentProjectData.objectsTable.cbWithAnimation = ui->cbWithAnimation->isChecked();
    currentProjectData.objectsTable.leAnimationName = ui->leAnimationName->text();

    // Levels tab
    fFetchTabGenericData(currentProjectData.levelsTable, ui->cbFromLevels, ui->cbToLevels, ui->twSourcesLevels);
    currentProjectData.levelsTable.cbSpecificFilesLevels = ui->cbSpecificFilesLevels->isChecked();
    currentProjectData.levelsTable.leSpecificFilesLevels = ui->leSpecificFilesLevels->text();
    currentProjectData.levelsTable.cbDatLevels = ui->cbDatLevels->isChecked();
    currentProjectData.levelsTable.leTargetDatLevels = ui->leTargetDatLevels->text();
    currentProjectData.levelsTable.cbBnvLevels = ui->cbBnvLevels->isChecked();
    currentProjectData.levelsTable.leBnvLevels = ui->leBnvLevels->text();
    currentProjectData.levelsTable.cbGridsLevels = ui->cbGridsLevels->isChecked();
    currentProjectData.levelsTable.cbAdditionalSourcesLevels = ui->cbAdditionalSourcesLevels->isChecked();
    currentProjectData.levelsTable.leAdditSourcesLevels = ui->leAdditSourcesLevels->text();

    // Misc tab
    fFetchTabGenericData(currentProjectData.miscTable, ui->cbFromMisc, ui->cbToMisc, ui->twSourcesMisc);

    return currentProjectData;
}

QRadioButton* MainWindow::getTextureRBCheckedTypeTexture()
{
    if(ui->rbBGR32->isChecked()){
        return ui->rbBGR32;
    }
    else if(ui->rbBGRA32->isChecked()){
        return ui->rbBGRA32;
    }
    else if(ui->rbBGR555->isChecked()){
        return ui->rbBGR555;
    }
    else if(ui->rbBGRA5551->isChecked()){
        return ui->rbBGRA5551;
    }
    else if(ui->rbBGRA444->isChecked()){
        return ui->rbBGRA444;
    }
    else{ //dxt1 checked
        return ui->rbDxt1;
    }
}

QRadioButton* MainWindow::getTextureRBTypeTextureByName(const QString &texType)
{
    if(QString::compare(texType,ui->rbBGR32->text(),Qt::CaseSensitive)==0){
        return ui->rbBGR32;
    }
    else if(QString::compare(texType,ui->rbBGRA32->text(),Qt::CaseSensitive)==0){
        return ui->rbBGRA32;
    }
    else if(QString::compare(texType, ui->rbBGR555->text(),Qt::CaseSensitive)==0){
        return ui->rbBGR555;
    }
    else if(QString::compare(texType,ui->rbBGRA5551->text(),Qt::CaseSensitive)==0){
        return ui->rbBGRA5551;
    }
    else if(QString::compare(texType,ui->rbBGRA444->text(),Qt::CaseSensitive)==0){
        return ui->rbBGRA444;
    }
    else{ //dxt1
        return ui->rbDxt1;
    }

}

void MainWindow::setVagoWindowTitle(){

    QString vagoTitle = "Vago v"+GlobalVars::AppVersion + " - ";

    if(this->lastProjectFilePath.isEmpty()){
        vagoTitle += "Untitled";
    }
    else{
        vagoTitle += Util::FileSystem::cutNameWithoutBackSlash(this->lastProjectFilePath);
    }

    if(this->unsavedChangesExist){
        vagoTitle += "*";
    }

    setWindowTitle(vagoTitle);
}

DropTableWidget* MainWindow::getCurrentTableWidget(){

    return getTableWidgetByTabName(ui->tabWidget->tabText(ui->tabWidget->currentIndex()));

}

DropTableWidget* MainWindow::getTableWidgetByTabName(const QString &tabName){

    if(tabName.compare("XML",Qt::CaseSensitive)==0){ //case sentive is faster
        return ui->twSourcesXML;
    }
    else if(tabName.compare("Textures",Qt::CaseSensitive)==0){
        return ui->twSourcesTextures;
    }
    else if(tabName.compare("Characters",Qt::CaseSensitive)==0){
        return ui->twSourcesCharacters;
    }
    else if(tabName.compare("Objects",Qt::CaseSensitive)==0){
        return ui->twSourcesObjects;
    }
    else if(tabName.compare("Levels",Qt::CaseSensitive)==0){
        return ui->twSourcesLevels;
    }
    else{
        return ui->twSourcesMisc;
    }

}

QString MainWindow::getCurrentTabName(){
    return ui->tabWidget->tabText(ui->tabWidget->currentIndex());
}

QString MainWindow::getTabNameByTableWidget(DropTableWidget* table){

    if(table == ui->twSourcesXML){
        return ui->tabWidget->tabText(XMLTabIndex);
    }
    else if(table == ui->twSourcesTextures){
        return ui->tabWidget->tabText(TexturesTabIndex);
    }
    else if(table == ui->twSourcesCharacters){
        return ui->tabWidget->tabText(CharactersTabIndex);
    }
    else if(table == ui->twSourcesObjects){
        return ui->tabWidget->tabText(ObjectsTabIndex);
    }
    else if(table == ui->twSourcesLevels){
        return ui->tabWidget->tabText(LevelsTabIndex);
    }
    else{
        return ui->tabWidget->tabText(MiscTabIndex);
    }

}

QList<DropTableWidget*> MainWindow::getAllTableWidgets()
{
    QList<DropTableWidget*> tableWidgets;

    tableWidgets << ui->twSourcesXML << ui->twSourcesTextures << ui->twSourcesCharacters
                 << ui->twSourcesObjects << ui->twSourcesLevels << ui->twSourcesMisc;

    return tableWidgets;
}

void MainWindow::loadProjectState(const QString &filePath)
{

    auto fLoadTabGenericData =
            [this]( // we are capturing this only to call the addRowTable function...
                const ProjectFileVago::ProjectTable &tableData,
                DropTableWidget * const table,
                QComboBox * const cbFrom,
                QComboBox * const cbTo) -> void{

        cbFrom->setCurrentText(tableData.from);
        cbTo->setCurrentText(tableData.to);

        // Add rows
        for(const ProjectFileVago::ProjectTableRow &currentRow : tableData.rows){
            addRowTable(table,currentRow.fileFolder,currentRow.fromTo,currentRow.command, currentRow.isDisabled);
        }

    };

    this->projectIsLoading = true;

    if(this->vagoSettings->value("AskSaveProject").toBool() && this->unsavedChangesExist){
        QMessageBox::StandardButton result = askToSaveCurrentProject();
        if(result == QMessageBox::StandardButton::Cancel){
            this->projectIsLoading = false;
            return;
        }
    }

    try{
        ProjectFileVago::ProjectData projectData = ProjectFileVago::readProjectDataFromFile(filePath);

        // XML tab
        fLoadTabGenericData(projectData.xmlTable, ui->twSourcesXML, ui->cbFromXML, ui->cbToXML);

        // Textures tab
        fLoadTabGenericData(projectData.texturesTable, ui->twSourcesTextures, ui->cbFromTextures, ui->cbToTextures);
        getTextureRBTypeTextureByName(projectData.texturesTable.rbTexturesType)->setChecked(true);
        ui->cbMipMapsTextures->setChecked(projectData.texturesTable.cbGenMipMaps);
        ui->cbNoUwrap->setChecked(projectData.texturesTable.cbNoUwrap);
        ui->cbNoVwrap->setChecked(projectData.texturesTable.cbNoVwrap);
        ui->cbLarge->setChecked(projectData.texturesTable.cbLarge);
        ui->cbEnvMap->setChecked(projectData.texturesTable.cbEnvMap);
        ui->leEnvMapTexture->setText(projectData.texturesTable.leEnvMapTexture);

        // Characters tab
        fLoadTabGenericData(projectData.charactersTable, ui->twSourcesCharacters, ui->cbFromCharacters, ui->cbToCharacters);

        ui->cbCellShading->setChecked(projectData.charactersTable.cbCellShading);
        ui->cbNormals->setChecked(projectData.charactersTable.cbNormals);
        ui->cbStandingPose->setChecked(projectData.charactersTable.cbStandingPose);
        ui->cbWithTRBS_ONCC->setChecked(projectData.charactersTable.cbWithTRBS_ONCC);
        ui->leTRBS_ONCC->setText(projectData.charactersTable.leTRBS_ONCC);

        // Objects tab
        fLoadTabGenericData(projectData.objectsTable, ui->twSourcesObjects, ui->cbFromObjects, ui->cbToObjects);

        ui->cbTexture->setChecked(projectData.objectsTable.cbTexture);
        ui->leTextureName->setText(projectData.objectsTable.leTextureName);
        ui->cbWithAnimation->setChecked(projectData.objectsTable.cbWithAnimation);
        ui->leAnimationName->setText(projectData.objectsTable.leAnimationName);

        // Levels tab
        fLoadTabGenericData(projectData.levelsTable, ui->twSourcesLevels, ui->cbFromLevels, ui->cbToLevels);

        ui->cbSpecificFilesLevels->setChecked(projectData.levelsTable.cbSpecificFilesLevels);
        ui->leSpecificFilesLevels->setText(projectData.levelsTable.leSpecificFilesLevels);
        ui->cbDatLevels->setChecked(projectData.levelsTable.cbDatLevels);
        ui->leTargetDatLevels->setText(projectData.levelsTable.leTargetDatLevels);
        ui->cbBnvLevels->setChecked(projectData.levelsTable.cbBnvLevels);
        ui->leBnvLevels->setText(projectData.levelsTable.leBnvLevels);
        ui->cbGridsLevels->setChecked(projectData.levelsTable.cbGridsLevels);
        ui->cbAdditionalSourcesLevels->setChecked(projectData.levelsTable.cbAdditionalSourcesLevels);
        ui->leAdditSourcesLevels->setText(projectData.levelsTable.leAdditSourcesLevels);

        // Misc tab
        fLoadTabGenericData(projectData.miscTable, ui->twSourcesMisc, ui->cbFromMisc, ui->cbToMisc);

        this->vagoSettings->setValue("LastProjectPath",QFileInfo(filePath).absoluteDir().path());

        this->lastProjectFilePath = filePath;
        this->unsavedChangesExist = false;

        addNewRecentProject(filePath);

        setVagoWindowTitle();

        this->projectIsLoading = false;

        Util::StatusBar::showSuccess(ui->statusBar, "Project loaded sucessfully.");
    }
    catch(const std::exception& e){
        this->projectIsLoading = false;
        QString errorMessage = "Couldn't load the Vago project. Error: " + QString(e.what());
        LOG_ERROR << errorMessage;
        Util::Dialogs::showError(errorMessage);
        Util::StatusBar::showError(ui->statusBar, "Couldn't load project.");
    }

}

void MainWindow::saveRecentProjects(){
    for(int i=0; i<this->recentProjectsList.size(); i++){
        this->vagoSettings->setValue("RecentProject" + QString::number(i+1), recentProjectsList[i]);
    }
}

void MainWindow::loadRecentProjects(){
    for(int i=0; i<this->recentProjectsMaxSize; i++){

        QString currProj = this->vagoSettings->value("RecentProject" + QString::number(i+1)).toString();

        if(!currProj.isEmpty()){
            recentProjectsList.append(currProj);
        }
        else{
            break;
        }
    }

    reloadRecentProjectsMenu();

}

void MainWindow::addNewRecentProject(const QString &filePath){

    // If the new project is equal to the last one simply ignore
    if(filePath == this->vagoSettings->value("RecentProject1").toString()){
        return;
    }

    // If the item already exists in our list remove it, so it can go to the top again
    for(auto it = this->recentProjectsList.begin(); it != this->recentProjectsList.end();){
        if(*it == filePath){
            it = this->recentProjectsList.erase(it);
        }
        else{
            it++;
        }
    }

    // if we gonna overflow our list, remove the older item to reserve space to the new one
    if(this->recentProjectsList.size()==this->recentProjectsMaxSize){
        this->recentProjectsList.removeLast();
    }

    this->vagoSettings->setValue("LastProjectPath",QFileInfo(filePath).absoluteDir().path());

    // add new recent file
    this->recentProjectsList.prepend(filePath);

    reloadRecentProjectsMenu();

    saveRecentProjects();
}

void MainWindow::reloadRecentProjectsMenu(){

    ui->menuRecent_Projects->setEnabled(false);
    ui->actionProject1->setVisible(false);
    ui->actionProject2->setVisible(false);
    ui->actionProject3->setVisible(false);
    ui->actionProject4->setVisible(false);
    ui->actionProject5->setVisible(false);

    {
        QList<QString>::const_iterator it;
        int i;
        for(it = recentProjectsList.cbegin(), i=0; it != recentProjectsList.cend(); it++, i++){

            QAction* currAction = nullptr;

            switch (i){
            case 0:
                currAction = ui->actionProject1;
                break;
            case 1:
                currAction = ui->actionProject2;
                break;
            case 2:
                currAction = ui->actionProject3;
                break;
            case 3:
                currAction = ui->actionProject4;
                break;
            case 4:
                currAction = ui->actionProject5;
                break;
            }

            if(currAction){
                ui->menuRecent_Projects->setEnabled(true);
                currAction->setText(*it);
                currAction->setVisible(true);
            }
        }
    }

}
