#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QProcess>  // For the external text editor

#define _CONSOLE_ERROR( x ) emit sendConsoleOut(QString() + x , ConsoleOut::Error);
#define _CONSOLE_INFO( x ) emit sendConsoleOut(QString() + x , ConsoleOut::Information);
#define _CONSOLE_VERBOSE( x ) emit sendConsoleOut(QString() + x , ConsoleOut::Verbose);
#define _CONSOLE_FATAL( x ) emit sendConsoleOut(QString() + x , ConsoleOut::Fatal);

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{   
    ui->setupUi(this);
    //TODO:  Must be a way to set maximised by default?
    this->showMaximized();

    //read in configuration using ConfigDialog (but dont display it)
    ConfigDialog c(&this->scriptsDirectory, &this->externalTextEditor,
                   &this->externalMidiPlayer, this);
    if(!c.readConfig()){
        c.exec();
    }
    extMidiPlayer = new QProcess(this);

    //Setup external Midi player
    connect(extMidiPlayer, SIGNAL(finished(int)), this, SLOT(extMidiPlayer_finished(int)));

    /*Usage Instructions*/
    this->ui->usageLabel->setText(QString()+
    "To get started:  Select a script from the list on the right hand side.\n" +
    "Read the description, and type some useful data into the inputs.\n"+
    "Press 'Run Script' and see what happens....\n\n"+
    "Images can be viewed using the image viewer on the left, and permenant data can be stored"+
    "using the variables list at the top right, errors and other useful information will appear"+
    "on the console at the bottom of the page.");


    /*Setup Console Out Widget */
    consoleOutModel = new ConsoleOutModel(this);
    ui->consoleOutView->setModel(consoleOutModel);
    connect(ui->verboseBox,SIGNAL(toggled(bool)), consoleOutModel, SLOT(changeFilterVerbose(bool)));
    connect(ui->errorBox,SIGNAL(toggled(bool)), consoleOutModel, SLOT(changeFilterError(bool)));
    connect(ui->infoBox,SIGNAL(toggled(bool)), consoleOutModel, SLOT(changefilterInformation(bool)));
    connect(ui->fatalBox,SIGNAL(toggled(bool)), consoleOutModel, SLOT(changeFilterFatal(bool)));
    //connect mainwindow #defines to console out
    connect(this, SIGNAL(sendConsoleOut(QString, ConsoleOut::OutputType)), consoleOutModel, SLOT(receiveConsoleOut(QString,ConsoleOut::OutputType)));


    /*Setup File List Widget*/
    //Config Stuff - To be set externally
    rootPath = "";
    imageFileModel = new QFileSystemModel(this);
    ui->imageFileView->setModel(imageFileModel);
    //Set the file view to where QT thinks the users stuff is
    ui->imageFileView->setCurrentIndex(imageFileModel->index(
            QDesktopServices::storageLocation(QDesktopServices::HomeLocation)));
    imageFileModel->setRootPath(rootPath);
    ui->imageFileView->hideColumn(1);
    ui->imageFileView->hideColumn(2);
    ui->imageFileView->hideColumn(3);


    /*Setup Variables Widget*/
    variablesModel = new VariablesTableModel(this);
    ui->variablesView->setModel(variablesModel);
    //connect to console out
    connect(variablesModel, SIGNAL(sendConsoleOut(QString,ConsoleOut::OutputType)), consoleOutModel, SLOT(receiveConsoleOut(QString,ConsoleOut::OutputType)));
    //connect to run scripts widget (for menu etc)
    connect(variablesModel, SIGNAL(loadedVariables(QStringList,QStringList)), ui->runScriptWidget, SLOT(loadVariables(QStringList,QStringList)));
    //Add Menu to toolbutton
    variablesWidgetMenu = new QMenu(this);
    variablesWidgetMenu->addAction(ui->actionOpen_Variables);
    variablesWidgetMenu->addAction(ui->actionImport_Variables);
    variablesWidgetMenu->addAction(ui->actionSave_Variables);
    variablesWidgetMenu->addAction(ui->actionSave_Variables_As);
    variablesWidgetMenu->addSeparator();
    variablesWidgetMenu->addAction(ui->actionPlay_Variable_in_External_Midi_Player);
    variablesWidgetMenu->addAction(ui->actionKill_External_Midi_Player);
    variablesWidgetMenu->addSeparator();
    variablesWidgetMenu->addAction(ui->actionSet_Current_Image_from_Variable);
    ui->variablesViewToolB->setMenu(variablesWidgetMenu);
    //Initialise return values menu
    retMenu = new QMenu(this);
    ui->variablesViewRetB->setMenu(retMenu);
    connect(ui->runScriptWidget, SIGNAL(returnsChanged(QStringList,QStringList)), this, SLOT(loadReturnValues(QStringList,QStringList)));
    //Add Menu
    addMenu = new QMenu(this);
    addMenu->addAction(ui->actionAdd_Blank_Variable);
    addMenu->addSeparator();
    addMenu->addAction(ui->actionAddVarImagePath);
    addMenu->addAction(ui->actionAddVarPoints);
    addMenu->addSeparator();
    addMenu->addAction(ui->action_Insert_Variable_File_Path_s);
    ui->variablesViewAddB->setMenu(addMenu);

    /*Setup Script List Widget  (& Run Scripts Widget)*/
    scriptsModel = new ScriptListModel(this, &scriptsDirectory);
    ui->scriptListView->setModel(scriptsModel);
    //Section width was set by changing the size of the vertical headers to 20
    //Script list widget connects to run script widget, it should probably do this directly,
      //but for now..
    connect(ui->scriptListView, SIGNAL(activated(const QModelIndex)), this, SLOT(scriptListView_activated(QModelIndex)));
    connect(this, SIGNAL(scriptChanged(QString, QString, QString, QString, QString, QString)),
            ui->runScriptWidget, SLOT(loadScript(QString,QString,QString,QString,QString, QString)));

    //connect console out messages(needs to be done last, as objects have to exist!)
    connect(ui->runScriptWidget, SIGNAL(sendConsoleOut(QString,ConsoleOut::OutputType)), consoleOutModel, SLOT(receiveConsoleOut(QString,ConsoleOut::OutputType)));
    connect(this->scriptsModel, SIGNAL(sendConsoleOut(QString, ConsoleOut::OutputType)), consoleOutModel, SLOT(receiveConsoleOut(QString,ConsoleOut::OutputType)));

}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::changeEvent(QEvent *e)
{
    QMainWindow::changeEvent(e);
    switch (e->type()) {
    case QEvent::LanguageChange:
        ui->retranslateUi(this);
        break;
    default:
        break;
    }
}

/** Current Image View Stuff ********************************************************************/
void MainWindow::on_imageFileView_activated(QModelIndex index)
{
    //TODO: Should use actions?
    //QMessageBox::warning(this, "warning", imageFileModel->filePath(index));
    if(imageFileModel->fileInfo(index).isFile() && imageFileModel->fileInfo(index).isReadable()){
        if(!ui->mainImageGraphicsView->setImageFilePath(imageFileModel->filePath(index))){
            _CONSOLE_ERROR("Failed to load image: " + imageFileModel->filePath(index));
            return;
        }else{
            currentImagePath = imageFileModel->filePath(index);
        }
    }


}

void MainWindow::on_actionOpen_Image_triggered()
{
    QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"),
               QDesktopServices::storageLocation(QDesktopServices::HomeLocation), tr("Images (*.*)"));
    if(!fileName.isEmpty()){
        ui->mainImageGraphicsView->setImageFilePath(fileName);
        this->currentImagePath = fileName;
    }
}

void MainWindow::on_actionRefresh_Image_triggered()
{
    if(!this->currentImagePath.isEmpty()){
        ui->mainImageGraphicsView->setImageFilePath(currentImagePath);
    }
}

/** Variables View Stuff ********************************************************/
void MainWindow::on_actionSave_Variables_triggered()
{
    if (!currentVariablesPath.isEmpty()){
        _CONSOLE_VERBOSE(QString("Attempting to write to file") + currentVariablesPath);
        variablesModel->writeToFile(currentVariablesPath);
    }else{
        _CONSOLE_VERBOSE(QString("Current Variables Path is empty."));
        this->on_actionSave_Variables_As_triggered();
    }
}

void MainWindow::on_actionOpen_Variables_triggered()
{
    //TODO:  Ask to save?
    QString fileName = QFileDialog::getOpenFileName( this,
                            "Open Variables File",
                            QDesktopServices::storageLocation(QDesktopServices::HomeLocation),
                            "SAMI Files (*.SAMI);;All Files (*.*)");
    if(!fileName.isEmpty()){
        currentVariablesPath = fileName;
        _CONSOLE_VERBOSE(QString("Attempting to open file") + currentVariablesPath);
        variablesModel->populateFromFile(currentVariablesPath);
    }
}

void MainWindow::on_actionImport_Variables_triggered()
{
    QString fileName = QFileDialog::getOpenFileName( this,
                            "Import Variables File",
                            QDesktopServices::storageLocation(QDesktopServices::HomeLocation),
                            "SAMI Files (*.SAMI);;All Files (*.*)");
    if(!fileName.isEmpty()){
        currentVariablesPath = fileName;
        _CONSOLE_VERBOSE(QString("Attempting to import file") + currentVariablesPath);
        variablesModel->importFromFile(currentVariablesPath);
    }
}

void MainWindow::on_actionSave_Variables_As_triggered()
{
    //TODO: Add Home Dir
    QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"),
                                                    QDesktopServices::storageLocation(QDesktopServices::HomeLocation),
                                                    tr("SAMI file (*.SAMI);;All Files (*.*"));
    if(!fileName.isEmpty()){
        if(fileName.section('.', -1 ).toLower()!="sami"){
            fileName += ".sami";
        }
        currentVariablesPath = fileName;
        _CONSOLE_VERBOSE(QString("Attempting to write to file") + currentVariablesPath);
        variablesModel->writeToFile(currentVariablesPath);
    }else{
        _CONSOLE_VERBOSE("QFileDialog returned NULL");
    }
}
void MainWindow::on_actionAdd_Blank_Variable_triggered()
{
    variablesModel->insertRow(ui->variablesView->currentIndex().row()+1, QModelIndex());
}

void MainWindow::on_variablesViewDelB_clicked()
{
    variablesModel->removeRow(ui->variablesView->currentIndex().row(), QModelIndex());
}

void MainWindow::on_variablesViewEditB_clicked()
{
    if (ui->variablesView->currentIndex().isValid()){
        int row = ui->variablesView->currentIndex().row();                        
        QString value = this->variablesModel->getText(row, 1);
        QString variable = this->variablesModel->getText(row, 0);
        VariablesWidgetDialog v(&variable, &value, this);
        if(v.exec() == QDialog::Accepted){
            this->variablesModel->setText(row, 0, variable);
            this->variablesModel->setText(row, 1, value);
        }
   }
}

void MainWindow::on_variablesViewUpB_clicked()
{
    int r = ui->variablesView->currentIndex().row();
    if(r >= 1 && ui->variablesView->currentIndex().isValid()){
        QString current0 = variablesModel->getText(r, 0);
        QString current1 = variablesModel->getText(r, 1);
        QString above0 = variablesModel->getText(r-1,0);
        QString above1 = variablesModel->getText(r-1, 1);
        variablesModel->setText(r, 0, above0);
        variablesModel->setText(r, 1, above1);
        variablesModel->setText(r-1, 0, current0);
        variablesModel->setText(r-1, 1, current1);
        ui->variablesView->setCurrentIndex(
                variablesModel->index(r-1, 1, QModelIndex()));
    }
}

void MainWindow::on_variablesViewDownB_clicked()
{
    int r = ui->variablesView->currentIndex().row();
    if(r <= variablesModel->rowCount(QModelIndex())-2
       && ui->variablesView->currentIndex().isValid()){
        QString current0 = variablesModel->getText(r, 0);
        QString current1 = variablesModel->getText(r, 1);
        QString below0 = variablesModel->getText(r+1,0);
        QString below1 = variablesModel->getText(r+1, 1);
        variablesModel->setText(r, 0, below0);
        variablesModel->setText(r, 1, below1);
        variablesModel->setText(r+1, 0, current0);
        variablesModel->setText(r+1, 1, current1);
        ui->variablesView->setCurrentIndex(
                variablesModel->index(r+1, 1, QModelIndex()));
    }
}

void MainWindow::on_actionAddVarImagePath_triggered()
{
    if(!currentImagePath.isEmpty()){
        variablesModel->insertRow(0, QModelIndex());
        variablesModel->setText(0, 0, "Image Path");
        variablesModel->setText(0, 1, currentImagePath);
    }else{
        _CONSOLE_VERBOSE("Current Image Path Empty");
    }
}


void MainWindow::on_actionAddVarPoints_triggered()
{
    if(!currentImagePath.isEmpty())
    {
        QString variable, value;
        DrawPointsDialog d(this->currentImagePath, &variable, &value, this);
        if(d.exec() == QDialog::Accepted){
            variablesModel->insertRow(0, QModelIndex());
            variablesModel->setText(0, 0, variable);
            variablesModel->setText(0, 1, value);
        }
    }else{
        _CONSOLE_ERROR("You havent selected an image (under current image)");
    }
    _CONSOLE_FATAL("This is still being implemented!");
}


void MainWindow::on_actionPlay_Variable_in_External_Midi_Player_triggered()
{
    if(extMidiPlayer->state()!=QProcess::NotRunning){
        _CONSOLE_FATAL("External Midi Player Already Running!");
    }
    if(ui->variablesView->currentIndex().isValid()){
        _CONSOLE_VERBOSE("Starting external midi player");
        extMidiPlayer->start(this->externalMidiPlayer + " " + variablesModel->getText(
                ui->variablesView->currentIndex().row(), 1 ));
        if(!extMidiPlayer->waitForStarted(1000)){
            _CONSOLE_ERROR("External midi player to start, invalid filename?");
        }else{
            ui->actionPlay_Variable_in_External_Midi_Player->setEnabled(false);
        }
    }else{
        _CONSOLE_ERROR("variables view index not valid, did you select a variable?");
    }
}

void MainWindow::extMidiPlayer_finished(int exitCode){
    ui->actionPlay_Variable_in_External_Midi_Player->setEnabled(true);
    if(exitCode != EXIT_SUCCESS){
        _CONSOLE_FATAL("External Midi Player exited without EXIT_SUCCESS");
    }
}


void MainWindow::on_actionKill_External_Midi_Player_triggered()
{
    if(extMidiPlayer->state()!=QProcess::NotRunning){
        extMidiPlayer->kill();
    }else{
        _CONSOLE_FATAL("External Midi Player not running");
    }
}

void MainWindow::on_actionSet_Current_Image_from_Variable_triggered()
{
    if(ui->variablesView->currentIndex().isValid()){
        currentImagePath = variablesModel->getText(ui->variablesView->currentIndex().row(), 1);
        ui->mainImageGraphicsView->setImageFilePath(currentImagePath);
    }else{
        _CONSOLE_ERROR("No Variable Selected!");
    }
}

void MainWindow::on_action_Insert_Variable_File_Path_s_triggered()
{
    QStringList files = QFileDialog::getOpenFileNames(
                            this, "Select one or more files to open",
                            QDesktopServices::storageLocation(QDesktopServices::HomeLocation),
                            "All Files (*.*)");
    QStringList list = files;   //Qt Doc suggests iterating over a copy of files :S
    QString value = "";
    for (QStringList::Iterator it = list.begin();it != list.end();++it) {
        if(value.isEmpty()){value = *it;}
        else{value+=QString(",")+*it;}
    }
    variablesModel->insertRow(0, QModelIndex());
    variablesModel->setText(0, 0, "<File Path>");
    variablesModel->setText(0, 1, value);
}

void MainWindow::loadReturnValues(QStringList returnNames, QStringList returnValues){
    _CONSOLE_VERBOSE("Building Menu (MainWindow)");

    retMenu->clear();
    retMenuAction.clear();
    for (int i = 0; i < returnNames.size(); ++i){
        retMenuAction.append(new QAction(returnNames.at(i), this));
        retMenuAction.last()->setProperty("name", returnNames.at(i));
        retMenuAction.last()->setProperty("value", returnValues.at(i));
        connect(retMenuAction.last(), SIGNAL(triggered()), this, SLOT(retMenu_clicked()));
        retMenu->addAction(retMenuAction.last());
    }
}

void MainWindow::retMenu_clicked(){
    _CONSOLE_VERBOSE("MainWindow::retMenu_clicked()");
    if(ui->variablesView->currentIndex().isValid()){
        variablesModel->setText(ui->variablesView->currentIndex().row(), 1,
                                sender()->property("value").toString());
    }else{
        _CONSOLE_VERBOSE("VariablesView index not valid, adding row");
        variablesModel->insertRow(0, QModelIndex());
        variablesModel->setText(0, 0, sender()->property("name").toString());
        variablesModel->setText(0, 1, sender()->property("value").toString());
    }
}

/** SCRIPT LIST VIEW STUFF *************************************************************/

void MainWindow::scriptListView_activated(const QModelIndex &index){
    if(!index.isValid())  //Need more??
        return;
    int r = index.row();

    emit scriptChanged(scriptsModel->getName(r), scriptsModel->getPath(r),
                       scriptsModel->getDecription(r), scriptsModel->getRequire(r),
                       scriptsModel->getOptional(r), scriptsModel->getReturn(r));

    ui->stackedWidget->setCurrentIndex(1);

}



void MainWindow::on_actionEdit_Script_triggered()
{
    if(ui->scriptListView->currentIndex().isValid()){
        _CONSOLE_VERBOSE("Starting external editor");
        if(!QProcess::startDetached(this->externalTextEditor + " " + scriptsModel->getPath(
                ui->scriptListView->currentIndex().row()))){
            _CONSOLE_ERROR("External Editor Failed to start, invalid filename?");
        }
    }else{
        _CONSOLE_ERROR("script list view index not valid, did you select a script?");
    }

}


void MainWindow::on_actionRefresh_Scripts_triggered()
{
    scriptsModel->refresh();
    ui->runScriptWidget->clear();
}

/**  MENU & TOOLBAR STUFF ***************************************************************/

void MainWindow::on_actionCurrent_Image_triggered(bool checked)
{
    if(checked){ui->mainImageDisplayWidget->show();
    }else{ui->mainImageDisplayWidget->hide();  }
}

void MainWindow::on_actionCurrent_Image_Path_triggered(bool checked)
{
    if(checked){ui->fileListWidget->show();
    }else{ui->fileListWidget->hide();}
}
void MainWindow::on_actionVariables_triggered(bool checked)
{
    if(checked){ui->variablesWidget->show();}
    else{ui->variablesWidget->hide();}
}

void MainWindow::on_actionConsole_triggered(bool checked)
{
    if(checked){ui->consoleWidget->show();}
    else{ui->consoleWidget->hide();}
}

void MainWindow::on_actionScripts_triggered(bool checked)
{
    if(checked){ui->scriptListWidget->show();}
    else{ui->scriptListWidget->hide();}
}

void MainWindow::on_actionHelp_Contents_triggered()
{
    if(!QDesktopServices::openUrl(QUrl("help/index.html", QUrl::TolerantMode))){
        _CONSOLE_FATAL("Failed to open ./help/index.html in external web browser.");
    }
}

void MainWindow::on_actionAbout_triggered()
{
    QMessageBox::about(this, "About", QString("SAMI (SAMI Ain't a Musical Instrument) by ")
                       +"Simon StJohn-Green and Jack Dobinson, GUI by Simon StJohn-Green."
                       +"\nSee sonify.blogspot.com");
}

void MainWindow::on_actionSplash_Screen_triggered()
{
    ui->stackedWidget->setCurrentIndex(0);
}

/*These cause the tick to disappear if the dock widgets are
  closed externally */
void MainWindow::on_mainImageDisplayWidget_visibilityChanged(bool visible)
{
    ui->actionCurrent_Image->setChecked(visible);
}
void MainWindow::on_fileListWidget_visibilityChanged(bool visible)
{
    ui->actionCurrent_Image_Path->setChecked(visible);
}
void MainWindow::on_consoleWidget_visibilityChanged(bool visible)
{
    ui->actionConsole->setChecked(visible);
}
void MainWindow::on_variablesWidget_visibilityChanged(bool visible)
{
    ui->actionVariables->setChecked(visible);
}
void MainWindow::on_scriptListWidget_visibilityChanged(bool visible)
{
    ui->actionScripts->setChecked(visible);
}

/*Config Stuff*/

void MainWindow::on_actionPreferences_triggered()
{
    ConfigDialog c(&this->scriptsDirectory, &this->externalTextEditor, &this->externalMidiPlayer);
    c.exec();
}
