/*
 * Copyright 2010 Mihai Niculescu <q.quark@gmail.com>
 *
 * This file is part of EqualX Project (https://launchpad.net/equalx/)
 *
 * EqualX is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * EqualX is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <QDebug>

#include <QAction>
#include <QButtonGroup>
#include <QClipboard>
#include <QCloseEvent>
#include <QDesktopWidget>
#include <QDir>
#include <QFile>
#include <QFileDialog>
#include <QGridLayout>
#include <QLayout>
#include <QMenu>
#include <QMessageBox>
#include <QMimeData>
#include <QMovie>
#include <QPixmap>
#include <QProcess>
#include <QPropertyAnimation>
#include <QPushButton>
#include <QSettings>
#include <QStandardItemModel>
#include <QTextStream>
#include <QTextDocumentFragment>
#include <QToolButton>
#include <QUrl>

#include "defines.h"
#include "Util.h"
#include "EquationView.h"
#include "EquationTemplateWidget.h"
#include "FileInfo.h"
#include "LatexEditor.h"
#include "Library/Library.h"
#include "WidgetFind.h"
#include "SymbolsPanel.h"
#include "RenderEngine.h"
#include "SearchLineEdit.h"

#include "BookmarksPanel/DialogPreferencesBookmark.h"
#include "DialogReplace.h"
#include "DialogPreferences.h"
#include "DialogAbout.h"

#include "MainWindow.h"
#include "ui_mainwindow.h"

#include <iostream>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent),
      ui(new Ui::MainWindowClass),
      mEquationView(0),
      mAutoUpdateTimer(),
      mSymbolsModel(0)
{
    ui->setupUi(this);

    checkRequirements();

    setWindowTitle(APP_FULL_NAME);

    mLatexEditor = ui->editEquation;
    mFindWidget = ui->findWidget;

    mDialogReplace = new DialogReplace(this);
    mDialogPreferences = new DialogPreferences(this);
    mDialogAbout = new DialogAbout(this);
    mDialogBookmark = new DialogPreferencesBookmark(this);

    mEquationData = new EqualX::FileInfo(this);

    mEquationView = new EquationView(this);
    mEquationView->setVisible(true);
    mEquationView->setAcceptDrops(true);

    renderModeButtonGroup = new QButtonGroup;
    renderModeButtonGroup->addButton(ui->renderModeDisplay, LATEX_ENV_MODE_DISPLAY);
    renderModeButtonGroup->addButton(ui->renderModeInline, LATEX_ENV_MODE_INLINE);
    renderModeButtonGroup->addButton(ui->renderModeAlign, LATEX_ENV_MODE_ALIGN);
    renderModeButtonGroup->addButton(ui->renderModeText, LATEX_ENV_MODE_TEXT);

    QStringList treeWidgetErrorsHeaderLabels;
    treeWidgetErrorsHeaderLabels.append(tr("Line"));
    treeWidgetErrorsHeaderLabels.append(tr("Message"));
    ui->treeWidgetErrors->setHeaderLabels(treeWidgetErrorsHeaderLabels);

    // this is the directory holding all files generated by the Render Engine
    QDir buildDir(QDir::tempPath());
    buildDir.mkdir(APP_NAME);
    buildDir.cd(APP_NAME);

    mRenderer = new EqualX::RenderEngine;
    mRenderer->setBuildDir(buildDir.absolutePath());

    mAnimationProgress = new QMovie(":/resources/icons/spinner.gif");
    mAnimationProgress->setScaledSize(QSize(64,64));

    /*
TODO: add a search line edit to search for symbols

    mSearchLineEdit = new SearchLineEdit;
    mSearchLineEdit->setMaximumWidth(200);
    // mSearchLineEdit->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);

    QAction* searcherAction = ui->mainBar->addWidget(mSearchLineEdit);
    searcherAction->setVisible(true);

    QWidget* emptyToolBarWidget = new QWidget;
    emptyToolBarWidget->setMinimumWidth(0);
    emptyToolBarWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);

    ui->mainBar->insertWidget(searcherAction, emptyToolBarWidget)->setVisible(true);
*/
    mLatexOutput = new QPlainTextEdit;
    mLatexOutput->setWindowTitle(tr("EqualX - LaTeX Output"));
    mLatexOutput->setWindowFlags(Qt::Tool);
    mLatexOutput->setReadOnly(true);
    mLatexOutput->setVisible(false);
    mLatexOutput->setLineWrapMode(QPlainTextEdit::NoWrap);


    ui->editPreamble->hide();
    mFindWidget->hide();
    mLatexEditor->setFocus();
    ui->librarySidebar->setVisible(false);

    ui->equationArea->setWidget(mEquationView);


    mAutoUpdateTimer.setSingleShot(true);

    mExportActionsGroup = new QActionGroup(this);
    mExportActionsGroup->setExclusive(true);

    readSettings();
    updateUIFromSettings();

    setActions();
    setSymbolsPanel();
    setMathPanelTabsActions();
    setSignals();

    mLibrary = LibraryManager::Instance();

    newEquation();

    setCustomStyleSheets();
}

MainWindow::~MainWindow()
{
    delete ui;
    delete renderModeButtonGroup;
    delete mAnimationProgress;
    delete mRenderer;

}

void MainWindow::setActions()
{
    ui->actionLibrary_BookmarkThisEquation->setEnabled(false);
    // actions on window
    ui->bookmarkAddButton->setDefaultAction(ui->actionLibrary_BookmarkThisEquation);
    ui->buttonNewEquation->setDefaultAction(ui->actionNew);

    ui->actionView_Refresh->setIconVisibleInMenu(true);



    // Set context menu actions on Equation
    QAction* copyEquationToClipoard = new QAction(QIcon::fromTheme("edit-copy", QIcon(":/resources/icons/menu/edit-copy.png")), tr("Copy"), this);
    copyEquationToClipoard->setIconVisibleInMenu(true);
    copyEquationToClipoard->setShortcut(QKeySequence(Qt::CTRL+Qt::ALT+Qt::Key_C)); // copy equation using CTRL+ALT+C
    connect(copyEquationToClipoard, SIGNAL(triggered()), this, SLOT(copyEquationToClipboard()) );

    QAction* pasteEquationFromClipoard = new QAction(QIcon::fromTheme("edit-paste", QIcon(":/resources/icons/menu/edit-paste.png")), tr("Paste"), this);
    pasteEquationFromClipoard->setShortcut(QKeySequence(Qt::CTRL+Qt::ALT+Qt::Key_V)); // copy equation using CTRL+ALT+V
    connect(pasteEquationFromClipoard, SIGNAL(triggered()), this, SLOT(pasteEquationFromClipboard()) );
    pasteEquationFromClipoard->setIconVisibleInMenu(true);

    QAction* actSep1 = new QAction(this);
    actSep1->setSeparator(true);
    QAction* actSep2 = new QAction(this);
    actSep2->setSeparator(true);
    QAction* actSep3 = new QAction(this);
    actSep3->setSeparator(true);

    ui->equationArea->addAction(ui->actionView_Refresh);
    ui->equationArea->addAction(actSep1);
    ui->equationArea->addAction(ui->actionLibrary_BookmarkThisEquation);

    // add the export types in context menu
    QAction* actExportTypes = new QAction("Export To..", this);
    ui->equationArea->addAction(actExportTypes);

    QMenu* menuExportTypes = new QMenu;

    QStringList fileTypes = mRenderer->fileTypes();
    QString currentExportType = mDialogPreferences->currentExportType();
    foreach(QString type, fileTypes){
        QAction* actType = menuExportTypes->addAction(type.toUpper());
        actType->setCheckable(true);
        actType->setData(type);

        mExportActionsGroup->addAction(actType);
        if(type.contains(currentExportType)){
            actType->setChecked(true);
        }
    }

    actExportTypes->setMenu(menuExportTypes);

    ui->equationArea->addAction(actSep2);
    ui->equationArea->addAction(copyEquationToClipoard);
    ui->equationArea->addAction(pasteEquationFromClipoard);
    ui->equationArea->addAction(actSep3);
    ui->equationArea->addAction(ui->actionView_StyleToolbar);
}

void MainWindow::setSymbolsPanel()
{
    if(mSymbolsModel) delete mSymbolsModel;

    mSymbolsModel = new QStandardItemModel;

    QSettings symsFile(QString("%1/symbols.ini").arg(SYMBOLS_PATH),QSettings::IniFormat);
    QStringList symsGroupsListName = symsFile.childGroups();

    qDebug() << "INI Symbols File has "<< symsGroupsListName.size() << " groups";


    int arraySize=0;
    for(int i=0; i<symsGroupsListName.size(); ++i){
        LatexSymbolsGroup*  symbolGroup = new LatexSymbolsGroup;

        symbolGroup->id = i;
        symbolGroup->name = symsGroupsListName.at(i);

        //qDebug() << "Reading group ["<< symbolGroup->name<<"]";

        symsFile.beginGroup(symbolGroup->name);
        arraySize = symsFile.beginReadArray("symbols");

        LatexSymbol* symbol = new LatexSymbol[arraySize];
        for(int j=0; j<arraySize; ++j){
            symsFile.setArrayIndex(j);
            symbol[j].id=j;
            symbol[j].parent=symbolGroup;
            symbol[j].name = symsFile.value("name").toString();
            symbol[j].latex = symsFile.value("latex").toString();
            symbol[j].icon = symsFile.value("icon").toString();
            symbol[j].packages = symsFile.value("package").toStringList();

            //qDebug() << "appending symbol:["<< symbol[j].name <<"]";

            symbolGroup->symbols.append(&symbol[j]);

            QStandardItem *item = new QStandardItem(QIcon( QString("resources/symbols/%1/%2").arg(symbol[j].parent->name, symbol[j].icon)), symbol[j].latex);
            mSymbolsModel->appendRow(item);

        }

        symsFile.endArray();

        ui->symbolsBar->appendSymbolsGroup(symbolGroup); // SymbolsPanel take ownership

        symsFile.endGroup();
    }

    mLatexEditor->setModel(mSymbolsModel);


}



void MainWindow::addButtonToMathTabs(const QStringList &list, QWidget *tabWidget)
{
    QHBoxLayout * tabLayout = (QHBoxLayout *)tabWidget->layout();

    if(!tabLayout){
        tabLayout = new QHBoxLayout;
    }

    EquationTemplateWidget *eqTemplate = new EquationTemplateWidget(list, tabWidget);

    connect(eqTemplate, SIGNAL(clicked(QString)), mLatexEditor, SLOT(replace(QString)) );

    tabLayout->addWidget(eqTemplate);
    tabLayout->addStretch(0);

    tabLayout->setSpacing(0);
    tabLayout->setContentsMargins(0,0,0,0);

    tabWidget->setLayout(tabLayout);

}

void MainWindow::setMathPanelTabsActions()
{
    // Algebra Tab
    QStringList listAlgebra;
    listAlgebra << ":/resources/icons/tabBar/algebra/eq01.png"
                << ":/resources/icons/tabBar/algebra/eq02.png"
                << ":/resources/icons/tabBar/algebra/eq03.png"
                << ":/resources/icons/tabBar/algebra/eq04.png"
                << ":/resources/icons/tabBar/algebra/eq05.png"
                << ":/resources/icons/tabBar/algebra/eq06.png" ;

    addButtonToMathTabs(listAlgebra,ui->templatesBar->widget(0) );


    // Derivs Tab
    QStringList listDerivs;
    listDerivs << ":/resources/icons/tabBar/derivs/eq01.png"
               << ":/resources/icons/tabBar/derivs/eq02.png"
               << ":/resources/icons/tabBar/derivs/eq03.png"
               << ":/resources/icons/tabBar/derivs/eq04.png"
               << ":/resources/icons/tabBar/derivs/eq05.png"
               << ":/resources/icons/tabBar/derivs/eq06.png" ;

    addButtonToMathTabs(listDerivs, ui->templatesBar->widget(1));

    // Stats Tab
    QStringList listStats;
    listStats << ":/resources/icons/tabBar/stats/eq01.png"
              << ":/resources/icons/tabBar/stats/eq02.png"
              << ":/resources/icons/tabBar/stats/eq03.png"
              << ":/resources/icons/tabBar/stats/eq04.png"
              << ":/resources/icons/tabBar/stats/eq05.png"
              << ":/resources/icons/tabBar/stats/eq06.png"
              << ":/resources/icons/tabBar/stats/eq07.png" ;

    addButtonToMathTabs(listStats, ui->templatesBar->widget(2));

    // Matrix Tab
    QStringList listMatrix;
    listMatrix << ":/resources/icons/tabBar/matrix/eq01.png"
               << ":/resources/icons/tabBar/matrix/eq02.png"
               << ":/resources/icons/tabBar/matrix/eq03.png"
               << ":/resources/icons/tabBar/matrix/eq04.png";

    addButtonToMathTabs(listMatrix, ui->templatesBar->widget(3));

    // Sets Tab
    QStringList listSets;
    listSets << ":/resources/icons/tabBar/sets/eq01.png"
             << ":/resources/icons/tabBar/sets/eq02.png";


    addButtonToMathTabs(listSets, ui->templatesBar->widget(4));

    // Trig Tab
    QStringList listTrig;
    listTrig << ":/resources/icons/tabBar/trig/eq01.png"
             << ":/resources/icons/tabBar/trig/eq02.png"
             << ":/resources/icons/tabBar/trig/eq03.png"
             << ":/resources/icons/tabBar/trig/eq04.png";

    addButtonToMathTabs(listTrig, ui->templatesBar->widget(5));

    // Geometry Tab
    QStringList listGeometry;
    listGeometry << ":/resources/icons/tabBar/geometry/eq01.png"
                 << ":/resources/icons/tabBar/geometry/eq02.png"
                 << ":/resources/icons/tabBar/geometry/eq03.png"
                 << ":/resources/icons/tabBar/geometry/eq04.png";

    addButtonToMathTabs(listGeometry, ui->templatesBar->widget(6));

    // Chemistry Tab
    QStringList listChemistry;
    listChemistry << ":/resources/icons/tabBar/chemistry/eq01.png"
                  << ":/resources/icons/tabBar/chemistry/eq02.png"
                  << ":/resources/icons/tabBar/chemistry/eq03.png"
                  << ":/resources/icons/tabBar/chemistry/eq04.png"
                  << ":/resources/icons/tabBar/chemistry/eq05.png";

    addButtonToMathTabs(listChemistry, ui->templatesBar->widget(7));

    // Physics Tab
    QStringList listPhysics;
    listPhysics << ":/resources/icons/tabBar/physics/eq01.png"
                << ":/resources/icons/tabBar/physics/eq02.png"
                << ":/resources/icons/tabBar/physics/eq03.png"
                << ":/resources/icons/tabBar/physics/eq04.png";

    addButtonToMathTabs(listPhysics, ui->templatesBar->widget(8));



}

void MainWindow::setSignals()
{
    /* MainWindow */
    // Menu Bar
    // File Menu
    connect(ui->actionNew, SIGNAL(triggered()), this, SLOT(newEquation()) );
    connect(ui->actionOpen, SIGNAL(triggered()), this, SLOT(open()) );
    connect(ui->actionSaveAs, SIGNAL(triggered()), this, SLOT(saveAs()) );
    connect(ui->actionQuit, SIGNAL(triggered()), this, SLOT(close()) );

    // Edit Menu
    connect(ui->actionEdit_Preferences, SIGNAL(triggered()), this, SLOT(onPreferences()) );
    connect(ui->actionEdit_Delete, SIGNAL(triggered()), this, SLOT(onDeleteSelection()) );

    // View Menu
    connect(ui->actionView_Refresh, SIGNAL(triggered()), this, SLOT(updateEquation()) );
    connect(ui->actionView_LatexOutput, SIGNAL(triggered()), this, SLOT(showLatexOutputWindow()) );

    // Search Menu
    connect(ui->actionFind, SIGNAL(triggered()), this, SLOT(showFindDialog()));
    connect(ui->actionFind_Next, SIGNAL(triggered()), mLatexEditor, SLOT(findNext()) );
    connect(ui->actionFind_Previous, SIGNAL(triggered()), mLatexEditor, SLOT(findPrevious()) );
    connect(ui->actionReplace, SIGNAL(triggered()), this, SLOT(showReplaceDialog()) );
    connect(mFindWidget, SIGNAL(closing()), mLatexEditor, SLOT(clearSelections()) );
    connect(mDialogReplace, SIGNAL(closing()), mLatexEditor, SLOT(clearSelections()) );

    // Library Menu
    connect(ui->actionLibrary_ShowBookmarks, SIGNAL(triggered()), this, SLOT(showBookmarks()) );
    connect(ui->actionLibrary_BookmarkThisEquation, SIGNAL(triggered()), this, SLOT(onBookmarkAdd()) );
    //---
    connect(ui->actionLibrary_ShowHistory, SIGNAL(triggered()), this, SLOT(showHistory()) );
    connect(ui->actionLibrary_ClearAllHistory, SIGNAL(triggered()), LibraryManager::Instance(), SLOT(clearHistory()) );

    // Help Menu
    connect(ui->actionHelp_About, SIGNAL(triggered()), this, SLOT(onAbout()) );
    //connect(ui->actionAboutQt, SIGNAL(triggered()), qApp, SLOT(aboutQt()) );

    // GUI
    connect(mLatexEditor, SIGNAL(textChanged()), this, SLOT(equationChanged()) );
    connect(ui->editPreamble, SIGNAL(blockCountChanged(int)), mLatexEditor, SLOT(setStartCountingBlocks(int)) );
    connect(ui->editPreamble, SIGNAL(textChanged()), this, SLOT(equationChanged()) );
    connect(ui->renderButton, SIGNAL(released()), this, SLOT(updateEquation()) );
    connect(renderModeButtonGroup, SIGNAL(buttonClicked(int)), this, SLOT(equationChanged()) );
    connect(ui->equationArea, SIGNAL(dropped(QString)), this, SLOT(loadEquation(QString)) );

    connect(ui->colorChooser, SIGNAL(fgColorChanged(QColor)), this, SLOT(equationChanged()) );
    connect(ui->colorChooser, SIGNAL(bgColorChanged(QColor)), this, SLOT(equationChanged()) );
    connect(ui->fontSizeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(equationChanged()) );

    // Find widget
    connect(mFindWidget, SIGNAL(changedFindText(QString)), this, SLOT(onFindTextChanged(QString))    );
    connect(mFindWidget, SIGNAL(find(QString,QTextDocument::FindFlags)), mLatexEditor, SLOT(find(QString,QTextDocument::FindFlags)) );
    connect(mFindWidget, SIGNAL(findAll(QString,QTextDocument::FindFlags)), mLatexEditor, SLOT(findAll(QString,QTextDocument::FindFlags)) );

    // Replace Dialog
    connect(mDialogReplace, SIGNAL(find(QString,QTextDocument::FindFlags)), mLatexEditor, SLOT(findAll(QString,QTextDocument::FindFlags)) );
    connect(mDialogReplace, SIGNAL(replace(QString)), mLatexEditor, SLOT(replace(QString)) );
    connect(mDialogReplace, SIGNAL(replaceAll(QString)), mLatexEditor, SLOT(replaceAll(QString)) );

    // Symbols Toolbar
    connect(ui->symbolsBar, SIGNAL(triggered(LatexSymbol*)), this, SLOT(insertSymbol(LatexSymbol*)) );

    // SearchLineEdit

    // MainToolbar


    // History Panel
    connect(ui->historyWidget, SIGNAL(activated(LibraryModelData)), this, SLOT(onActivatedHistoryRow(LibraryModelData)) );

    // Bookmarks Panel
    connect(ui->bookmarksWidget, SIGNAL(activated(int)), this, SLOT(onActivatedBookmark(int)) );

    // Other
    connect(mDialogPreferences->renderManualUpdate, SIGNAL(toggled(bool)), ui->renderButton, SLOT(setVisible(bool)) );

    connect(mRenderer, SIGNAL(started()), this, SLOT(renderStarted()) );
    connect(mRenderer, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(renderFinished(int,QProcess::ExitStatus)) );
    connect(mRenderer, SIGNAL(error(QProcess::ProcessError)), this, SLOT(renderError(QProcess::ProcessError)) );
    connect(ui->fontSizeComboBox, SIGNAL(currentIndexChanged(int)) , mEquationData, SLOT(setFontSize(int)) );
    connect(ui->colorChooser, SIGNAL(fgColorChanged(QColor)), mEquationData, SLOT(setForegroundColor(QColor)) );
    connect(ui->colorChooser, SIGNAL(bgColorChanged(QColor)), mEquationData, SLOT(setBackgroundColor(QColor)) );
    connect(renderModeButtonGroup, SIGNAL(buttonClicked(int)), mEquationData, SLOT(setEnvironment(int)) );

    connect(&mAutoUpdateTimer, SIGNAL(timeout()), this, SLOT(updateEquation()) );
    connect(mExportActionsGroup, SIGNAL(triggered(QAction*)), mDialogPreferences, SLOT(setCurrentExportType(QAction*)) );
    connect(mExportActionsGroup, SIGNAL(triggered(QAction*)), this, SLOT(updateEquation()) );

}

void MainWindow::setCustomStyleSheets()
{

}


void MainWindow::closeEvent(QCloseEvent *event)
{
    writeSettings();

    QMainWindow::closeEvent(event);
}

void MainWindow::showEquation()
{
    const QMap<int,QString> *errors = mRenderer->latexErrors();
    mLatexOutput->setPlainText(mRenderer->latexLog());

    bool latexHasErrors = !errors->isEmpty();
    if(!mLatexEditor->hasFocus()) mLatexEditor->setFocus();

    if(!latexHasErrors){  // do not try to parse the string for errors when there are none
        mLatexEditor->selectLines(*errors); // clears any highlightings
        ui->editPreamble->selectLines(*errors); // clears any highlightings
        ui->stackedWidget->setCurrentIndex(0);
        mEquationView->setPixmap( mRenderer->file("png") );
        mEquationView->setExportSource(mRenderer->file(mDialogPreferences->exportCBox->currentText()));
        ui->actionLibrary_BookmarkThisEquation->setEnabled(true);
        ui->actionSaveAs->setEnabled(true);

        // add equation to history
        if(!mLibrary->addHistoryItem(ui->equationTitleWidget->text(), mRenderer->file("png")))
            QMessageBox::critical(this, "Error - EqualX", QString("Failed to add equation to history. %1").arg(mLibrary->errorStr()) );


    }
    else{// show the Errors page
        ui->stackedWidget->setCurrentIndex(1);

        QMapIterator<int, QString> i(*errors);
        QList<QTreeWidgetItem *> items;
        QStringList row;
        while (i.hasNext()) {
            i.next();
            row.clear();
            row << QString("%1").arg(i.key())<< i.value();
            items.append(new QTreeWidgetItem((QTreeWidget*)0, row));
        }

        ui->treeWidgetErrors->clear();
        ui->treeWidgetErrors->insertTopLevelItems(0, items);
        mLatexEditor->selectLines(*errors);
        ui->editPreamble->selectLines(*errors);

        ui->actionLibrary_BookmarkThisEquation->setEnabled(false);
        ui->actionSaveAs->setEnabled(false);
    }
}

void MainWindow::newEquation()
{
    QFile f(":/resources/templates/template-preamble.tex");
    f.open(QIODevice::ReadOnly);
    QString preambleContent = f.readAll();
    f.close();

    ui->editPreamble->setPlainText(preambleContent);
    mLatexEditor->clear();
    mEquationView->clear();

    ui->actionLibrary_BookmarkThisEquation->setEnabled(false);

    updateUIFromSettings();
}

void MainWindow::open()
{
    if( curSaveDir.isEmpty() )
        curSaveDir = QDir::homePath();


    QString filter = mRenderer->fileTypes().join("|");
    filter.replace("|", ");;(*.");
    filter = "(*."+filter+")";

    QString allTypesFilter = mRenderer->fileTypes().join(" *.");
    filter = tr("All Supported files (*.")+allTypesFilter+");;"+filter;

    QString fileName = QFileDialog::getOpenFileName(this, tr("Open Equation"),
                                                    curSaveDir,
                                                    filter);
    QFileInfo fi(fileName);
    curSaveDir = fi.canonicalPath();

    if (!fileName.isEmpty())
        loadEquation(fileName);

}

bool MainWindow::saveAs()
{
    if( curSaveDir.isEmpty() )
        curSaveDir = QDir::homePath();

    QString filter = mRenderer->fileTypes().join(";;");
    filter.replace(";;", ");;(*.");
    filter = "(*."+filter+")";

    QString selectedFilter;
    selectedFilter = "(*."+ mExportActionsGroup->checkedAction()->data().toString() +")";

    QString fileName = QFileDialog::getSaveFileName(0, tr("Save Equation as..."), curSaveDir, filter, &selectedFilter );

    if (fileName.isEmpty())
        return false;

    QFileInfo fi(fileName);
    curSaveDir = fi.canonicalPath();

    int s = selectedFilter.length()-3;
    QString fileExt = selectedFilter.mid(2,s);
    fileName+=fileExt;

    return saveEquation(fileName);
}

void MainWindow::preferencesAccepted()
{
    writeSettings();

    updateUIFromSettings();
}

bool MainWindow::saveEquation(const QString &fileName)
{
    qDebug() << "[MainWindow::saveEquation] Saving equation to: " << fileName;

    QString ext = fileName.section(".", -1);

    mRenderer->run(ext);

    QFile::remove(fileName);
    QFile::copy(mRenderer->file(ext), fileName);

    return true;
}

void MainWindow::onDeleteSelection()
{
    mLatexEditor->textCursor().removeSelectedText();
}

void MainWindow::onPreferences()
{
    if(mDialogPreferences->exec()==QDialog::Accepted){
        preferencesAccepted();
    }
}

void MainWindow::onAbout()
{
    mDialogAbout->exec();
}

// read all settings from the Settings File and update preferencesDialog
void MainWindow::readSettings()
{
    QSettings settings(SETTINGS_DIR, SETTINGS_FILE);

    settings.beginGroup("CONVERTERS");
    QString pdfLatexPath = settings.value("pdflatex", DEFAULT_PDFLATEX ).toString();
    QString pdfCairoPath = settings.value("pdftocairo", DEFAULT_PDFCAIRO ).toString();
    QString gsPath = settings.value("gs", DEFAULT_GS ).toString();

    mDialogPreferences->pdfLatexEdit->setText(pdfLatexPath);
    mDialogPreferences->pdfCairoEdit->setText(pdfCairoPath);
    mDialogPreferences->gsEdit->setText(gsPath);
    settings.endGroup();

    EqualX::Converter c;
    c.path = pdfCairoPath;
    c.opts = "-png -transp -singlefile %1 %2";
    mRenderer->addConverter("png", c);

    EqualX::Converter c2;
    c2.path = pdfCairoPath;
    c2.opts = "-svg %1 %2.svg";
    mRenderer->addConverter("svg", c2);

    EqualX::Converter c3;
    c3.path = pdfCairoPath;
    c3.opts = "-jpeg -singlefile %1 %2";
    mRenderer->addConverter("jpg", c3);

    EqualX::Converter c4;
    c4.path = pdfCairoPath;
    c4.opts = "-ps -level2 %1 %2.ps"; //! need level 2 to insert xmp metadata
    mRenderer->addConverter("ps", c4);

    EqualX::Converter c5;
    c5.path = pdfCairoPath;
    c5.opts = "-eps %1 %2.eps";
    mRenderer->addConverter("eps", c5);

    mDialogPreferences->exportCBox->addItems(mRenderer->fileTypes());


    settings.beginGroup("MAIN");
    mDialogPreferences->checkRememberLayout->setChecked( settings.value("remember_layout", DEFAULT_REMEMBER_LAYOUT).toBool() );

    if( mDialogPreferences->checkRememberLayout->isChecked() ){
        ui->actionView_MainToolbar->setChecked( settings.value("mainbar", DEFAULT_SHOW_MAIN_TOOLBAR).toBool() );
        ui->actionView_SymbolsToolbar->setChecked( settings.value("panel_symbols", DEFAULT_SHOW_PANEL_SYMBOLS).toBool() );
        ui->actionView_TemplatesToolbar->setChecked( settings.value("panel_templates", DEFAULT_SHOW_PANEL_TEMPLATES).toBool() );
        ui->actionView_StyleToolbar->setChecked(settings.value("panel_properties", DEFAULT_SHOW_PANEL_PROPERTIES).toBool() );
        ui->actionView_Sidebar->setChecked(settings.value("panel_sidebar", DEFAULT_SHOW_PANEL_SIDEBAR).toBool() );

        QPoint pos = settings.value("position", QPoint(DEFAULT_WINDOW_X, DEFAULT_WINDOW_Y)).toPoint();
        QSize size = settings.value("size", QSize(DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT)).toSize();
        resize(size);
        move(pos);
    }

    QString currentExportType  = settings.value("export_type", DEFAULT_EXPORT_TYPE).toString() ;
    mDialogPreferences->setCurrentExportType(currentExportType);

    settings.endGroup();

    settings.beginGroup("EDITOR");
    mDialogPreferences->setCurrentFont(settings.value("font", DEFAULT_FONT).toString());
    mDialogPreferences->checkSyntaxHighligthing->setChecked(settings.value("highlighting", DEFAULT_HIGHLIGHTING ).toBool());
    mDialogPreferences->checkTextWrapping->setChecked( settings.value("textwrap", DEFAULT_WRAPPING).toBool() );
    mDialogPreferences->groupBoxCompletion->setChecked( settings.value("completion", DEFAULT_COMPLETION).toBool() );
    mDialogPreferences->checkSensitiveCompletion->setChecked( settings.value("case_sensitive_completion", DEFAULT_COMPLETION).toBool() );
    settings.endGroup();

    settings.beginGroup("PREVIEW");
    mDialogPreferences->renderAutoUpdate->setChecked( settings.value("updateAuto", DEFAULT_UPDATE_AUTO ).toBool() );
    mDialogPreferences->renderManualUpdate->setChecked(! settings.value("updateAuto", DEFAULT_UPDATE_AUTO ).toBool() );
    mDialogPreferences->spinUpdateTime->setValue(settings.value("updateTime", DEFAULT_UPDATE_TIME ).toInt());
    mDialogPreferences->bgPreviewColor->setColorName(settings.value("bgcolor", DEFAULT_PREVIEW_BG).toString() );
    mDialogPreferences->fgColorPicker->setColorName( settings.value("render_fgcolor", DEFAULT_RENDER_FG).toString() );
    mDialogPreferences->bgColorPicker->setColorName( settings.value("render_bgcolor", DEFAULT_RENDER_BG).toString() );
    mDialogPreferences->renderFontSize->setCurrentIndex( settings.value("render_fontsize", DEFAULT_RENDER_FONT_SIZE).toInt() );
    switch(settings.value("render_mode", DEFAULT_RENDER_MODE).toInt()){
    case LATEX_ENV_MODE_INLINE:
        mDialogPreferences->renderModeInline->setChecked(true);
        break;
    case LATEX_ENV_MODE_ALIGN:
        mDialogPreferences->renderModeAlign->setChecked(true);
        break;
    case LATEX_ENV_MODE_TEXT:
        mDialogPreferences->renderModeText->setChecked(true);
        break;
    default:
    case LATEX_ENV_MODE_DISPLAY:
        mDialogPreferences->renderModeDisplay->setChecked(true);
        break;
    }
    settings.endGroup();


}

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

    mEquationView->setMovie(mAnimationProgress);
    mAnimationProgress->start();

    QApplication::setOverrideCursor(Qt::BusyCursor);
}

void MainWindow::renderFinished( int exitCode, QProcess::ExitStatus exitStatus)
{
    qDebug() << "[Render Finished] exitCode:"<<exitCode<<" exitStatus:"<<exitStatus;
    mAnimationProgress->stop();
    setEnabled(true);
    QApplication::restoreOverrideCursor();

    if(exitCode!=0 || exitStatus != QProcess::NormalExit){
        QString processErrStr;
        if(mRenderer->currentProcess()){
#if UNIX_PLATFORM
#if QT_VERSION >= 0x050000
            processErrStr = mRenderer->currentProcess()->arguments().join(";");
#endif
#elif WIN_PLATFORM
#if QT_VERSION >= 0x040700
            processErrStr = mRenderer->currentProcess()->nativeArguments();
#endif
#endif
        }
        QMessageBox::critical(this, "Error - EqualX", QString("Command failed to run. %1").arg(processErrStr) );
        mEquationView->clear();

        return;
    }

    showEquation();
}

void MainWindow::renderError(QProcess::ProcessError /*e*/)
{
    mAnimationProgress->stop();
    mEquationView->clear();
    setEnabled(true);
    QApplication::restoreOverrideCursor();

    QString processErrStr;
    if(mRenderer->currentProcess()){
#if UNIX_PLATFORM
#if QT_VERSION >= 0x050000
        processErrStr = mRenderer->currentProcess()->arguments().join(";");
#endif
#elif WIN_PLATFORM
#if QT_VERSION >= 0x040700
        processErrStr = mRenderer->currentProcess()->nativeArguments();
#endif
#endif
    }

    QMessageBox::critical(this, "Error - EqualX", QString("Process failed. %1").arg(processErrStr) );
}

void MainWindow::updateEquation()
{

    qDebug() << "========== Update Equation =============";

    mEquationView->clear();
    if(mLatexEditor->toPlainText().isEmpty()){ // clear the view and do not launch renderer
        return;
    }

    setEnabled(false);

    mEquationData->setSelections(mLatexEditor->getSelections());
    mEquationData->setPreamble(ui->editPreamble->toPlainText());
    mEquationData->setEquation(mLatexEditor->toPlainText());

    QStringList exportTypes;
    exportTypes << "png" << mExportActionsGroup->checkedAction()->data().toString();

    mRenderer->setFileInfo(*mEquationData);
    mRenderer->run(exportTypes);

    mLatexEditor->setFocus();
}

void MainWindow::updateUIFromSettings()
{
    mLatexEditor->selectAll();
    mLatexEditor->setHighLighting( mDialogPreferences->checkSyntaxHighligthing->isChecked() );
    mLatexEditor->setCompletion( mDialogPreferences->groupBoxCompletion->isChecked() );
    mLatexEditor->setCompletionSensitive( mDialogPreferences->checkSensitiveCompletion->isChecked() );
    mLatexEditor->setFont(mDialogPreferences->getFont());
    ui->editPreamble->setFont(mDialogPreferences->getFont());

    if( mDialogPreferences->checkTextWrapping->isChecked() )
        mLatexEditor->setLineWrapMode(QPlainTextEdit::WidgetWidth);
    else
        mLatexEditor->setLineWrapMode(QPlainTextEdit::NoWrap);


    ui->renderButton->setVisible( mDialogPreferences->renderManualUpdate->isChecked() );

    QColor previewCol = mDialogPreferences->bgPreviewColor->getColor();
    QString previewStyleSheet;
    if(previewCol.alpha()!=0)  previewStyleSheet = QString("QScrollArea {background-color: %1} QLabel {background-color: %1}").arg(previewCol.name());
    ui->equationArea->setStyleSheet( previewStyleSheet );

    ui->fontSizeComboBox->setCurrentIndex( mDialogPreferences->renderFontSize->currentIndex() );
    ui->colorChooser->setFgColor( mDialogPreferences->fgColorPicker->getColorName() );
    ui->colorChooser->setBgColor( mDialogPreferences->bgColorPicker->getColorName() );

    switch(mDialogPreferences->renderMode()){
    case LATEX_ENV_MODE_INLINE:
        ui->renderModeInline->setChecked(true);
        break;
    case LATEX_ENV_MODE_ALIGN:
        ui->renderModeAlign->setChecked(true);
        break;
    case LATEX_ENV_MODE_TEXT:
        ui->renderModeText->setChecked(true);
        break;
    default:
    case LATEX_ENV_MODE_DISPLAY:
        ui->renderModeDisplay->setChecked(true);
        break;
    }

    mAutoUpdateTimer.setInterval(mDialogPreferences->spinUpdateTime->value());
    mRenderer->setPDFLatex(mDialogPreferences->pdfLatexEdit->text());
    mRenderer->setGS(mDialogPreferences->gsEdit->text());

    QString currentExportType = mDialogPreferences->currentExportType();
    foreach(QAction* act, mExportActionsGroup->actions()){

        if(act->data().toString().contains(currentExportType, Qt::CaseInsensitive)){
            act->setChecked(true);
        }
    }
}

// read all settings from preferencesDialog and write them to the Settings File
void MainWindow::writeSettings()
{
    QSettings settings(SETTINGS_DIR, SETTINGS_FILE);

    settings.beginGroup("MAIN");
    settings.setValue("remember_layout", mDialogPreferences->checkRememberLayout->isChecked() );
    settings.setValue("position", pos() );
    settings.setValue("size", size());
    settings.setValue("mainbar", ui->mainBar->isVisible() );
    settings.setValue("panel_properties", ui->renderBar->isVisible() );
    settings.setValue("panel_symbols", ui->symbolsBar->isVisible() );
    settings.setValue("panel_templates", ui->templatesBar->isVisible() );
    settings.setValue("panel_sidebar", ui->librarySidebar->isVisible() );
    settings.setValue("export_type", mDialogPreferences->currentExportType() );
    settings.endGroup();

    settings.beginGroup("EDITOR");
    settings.setValue("font", mDialogPreferences->getFont().toString());
    settings.setValue("highlighting", mDialogPreferences->checkSyntaxHighligthing->isChecked() );
    settings.setValue("textwrap", mDialogPreferences->checkTextWrapping->isChecked() );
    settings.setValue("completion", mDialogPreferences->groupBoxCompletion->isChecked() );
    settings.setValue("case_sensitive_completion", mDialogPreferences->checkSensitiveCompletion->isChecked() );
    settings.endGroup();

    settings.beginGroup("PREVIEW");
    settings.setValue("updateAuto", mDialogPreferences->renderAutoUpdate->isChecked() );
    settings.setValue("updateTime", mDialogPreferences->spinUpdateTime->value() );
    settings.setValue("bgcolor", mDialogPreferences->bgPreviewColor->getColorName() );
    settings.setValue("render_fgcolor", mDialogPreferences->fgColorPicker->getColorName() );
    settings.setValue("render_bgcolor", mDialogPreferences->bgColorPicker->getColorName() );
    settings.setValue("render_fontsize", mDialogPreferences->renderFontSize->currentIndex() );
    settings.setValue("LATEX_ENV_MODE", mDialogPreferences->renderMode() );
    settings.endGroup();

    settings.beginGroup("CONVERTERS");
    settings.setValue("pdflatex", mDialogPreferences->pdfLatexEdit->text() );
    settings.setValue("pdfcairo", mDialogPreferences->pdfCairoEdit->text() );
    settings.setValue("gs", mDialogPreferences->gsEdit->text() );
    settings.endGroup();

}

void MainWindow::equationChanged()
{
    mCurrentBookmark.clear();
    ui->bookmarkAddButton->setIcon(QIcon("://resources/icons/menu/bookmark.png"));
    ui->bookmarkAddButton->setToolTip(tr("Bookmark this equation"));

    if(mDialogPreferences->renderAutoUpdate->isChecked())
        mAutoUpdateTimer.start();

}

void MainWindow::onFindTextChanged(const QString &exp)
{
    // if we have a expression to search
    bool findable = (! exp.isEmpty()) && !exp.isNull();

    // Enable/Disable widgets from Search Menu accordingly
    ui->actionFind_Next->setEnabled(findable);
    ui->actionFind_Previous->setEnabled(findable);

    mLatexEditor->setTextCursor( QTextCursor(mLatexEditor->document()) );

}

void MainWindow::showFindDialog()
{
    mFindWidget->setFindExpr( mLatexEditor->selectedText() );
    mFindWidget->show();

    mLatexEditor->setTextCursor( QTextCursor(mLatexEditor->document()) );
}

void MainWindow::showReplaceDialog()
{
    mDialogReplace->setFindExpr( mLatexEditor->selectedText() );
    mDialogReplace->show();

    mLatexEditor->setTextCursor( QTextCursor(mLatexEditor->document()) );

}

void MainWindow::showLatexOutputWindow() const
{
    mLatexOutput->setVisible(!mLatexOutput->isVisible());
    mLatexOutput->resize(500, 400);
}

void MainWindow::insertSymbol(LatexSymbol *symbol)
{
    mLatexEditor->insertPlainText(symbol->latex);
}

void MainWindow::copyEquationToClipboard()
{
    QClipboard *clipboard = QApplication::clipboard();

    QList<QUrl> urls;
    urls.append( QUrl::fromLocalFile(mEquationView->exportSource()) );

    QMimeData *mimeData = new QMimeData();
    mimeData->setUrls(urls);

    /* special handling case for nautilus */
    QString urlForNautilus("copy\nFile://");
    urlForNautilus.append(mEquationView->exportSource());
    mimeData->setData("x-special/gnome-copied-files", urlForNautilus.toLatin1());

    clipboard->setMimeData(mimeData);
}

void MainWindow::pasteEquationFromClipboard()
{
    QClipboard *clipboard = QApplication::clipboard();
    const QMimeData *mimeData = clipboard->mimeData();

    if(!mimeData) return;

    if(mimeData->hasUrls()){
        QUrl url = mimeData->urls().at(0);
        if(!url.isLocalFile()) return;

        QString filename = url.toLocalFile();

        loadEquation(filename);
    }
}

void MainWindow::onActivatedHistoryRow(const LibraryModelData &row)
{
    loadEquation(row.name);
}

void MainWindow::onActivatedBookmark(int bookmarkId)
{
    Bookmark b = mLibrary->getBookmark(bookmarkId);
    ui->equationTitleWidget->setText(b.title);

    loadEquation(b.filePath);

    mCurrentBookmark = b;
    ui->bookmarkAddButton->setIcon(QIcon("://resources/icons/menu/bookmarked.png"));
    ui->bookmarkAddButton->setToolTip(tr("Edit bookmark"));
}

void MainWindow::onBookmarkAdd()
{
    if(!mCurrentBookmark.isValid()){ // bookmark is not in library
        mCurrentBookmark.title = ui->equationTitleWidget->text();
        mCurrentBookmark.description="";
        mCurrentBookmark.filePath = mRenderer->file("png");

        // add the bookmark on the root item (meaning is unsorted)
        mCurrentBookmark.id = mLibrary->addBookmark(mCurrentBookmark);
    }
    else { // bookmark exists in library, we change its properties
        mDialogBookmark->setTitle(mCurrentBookmark.title);
        mDialogBookmark->setDescrition(mCurrentBookmark.description);
        mDialogBookmark->setParentFolder(mCurrentBookmark.idparent);

        if(mDialogBookmark->exec()==QDialog::Accepted){
            int parentId = mDialogBookmark->parentFolder();

            mCurrentBookmark.title = mDialogBookmark->title();
            mCurrentBookmark.description=mDialogBookmark->description();

            mLibrary->updateBookmark(mCurrentBookmark);

            if(parentId != mCurrentBookmark.idparent){ // if parent changed
                mLibrary->moveBookmark(mCurrentBookmark.id, parentId);
                mCurrentBookmark.idparent = parentId;
            }

        }
    }

    ui->bookmarkAddButton->setIcon(QIcon("://resources/icons/menu/bookmarked.png"));
    ui->bookmarkAddButton->setToolTip(tr("Edit bookmark"));

}

void MainWindow::showBookmarks()
{
    ui->librarySidebar->setVisible(true);
    ui->libraryTabWidget->setCurrentIndex(0);
}

void MainWindow::showHistory()
{
    ui->librarySidebar->setVisible(true);
    ui->libraryTabWidget->setCurrentIndex(1);
}

void MainWindow::loadEquation(const QString &fileName)
{
    qDebug() << "[MainWindow::loadEquation] Loading file:"<< fileName;

    bool status = EqualX::File::fetchInfo(fileName, mEquationData);

    if(!status){
        QMessageBox::warning(this, APP_NAME,
                             tr("Can not load file: %1.\n"
                                "Are you sure this file was created with EqualX?").arg(fileName),
                             QMessageBox::Ok);

        return;
    }

    mLatexEditor->setPlainText(mEquationData->equation());
    ui->editPreamble->setPlainText(mEquationData->preamble());
    ui->colorChooser->setFgColor(mEquationData->fgColor());
    ui->colorChooser->setBgColor(mEquationData->bgColor());
    ui->fontSizeComboBox->setCurrentIndex(mEquationData->fontSize());
    switch(mEquationData->environment()){
    case LATEX_ENV_MODE_INLINE:
        ui->renderModeInline->setChecked(true);
        break;
    case LATEX_ENV_MODE_ALIGN:
        ui->renderModeAlign->setChecked(true);
        break;
    case LATEX_ENV_MODE_TEXT:
        ui->renderModeText->setChecked(true);
        break;
    default:
    case LATEX_ENV_MODE_DISPLAY:
        ui->renderModeDisplay->setChecked(true);
        break;
    }

    mLatexEditor->textCursor().clearSelection();
    ui->editPreamble->textCursor().clearSelection();


    if(!mAutoUpdateTimer.isActive()) updateEquation();
}

QString MainWindow::strippedName(const QString &fullFileName)
{
    return QFileInfo(fullFileName).fileName();
}

void MainWindow::checkRequirements()
{
    bool reqsAreMeet = true;
    bool checkPdfLatex, checkPdfCairo, checkGS;

    QString reqMessage = tr("EqualX can not find the applications it needs.\n"
                            "You can set these from Edit->Preferences->Advanced."
                            );

    QSettings settings(SETTINGS_DIR, SETTINGS_FILE);

    settings.beginGroup("CONVERTERS");
    QString pdfLatexPath = settings.value("pdflatex", DEFAULT_PDFLATEX ).toString();
    QString pdfCairoPath = settings.value("pdftocairo", DEFAULT_PDFCAIRO ).toString();
    QString gsPath = settings.value("gs", DEFAULT_GS ).toString();


    // check for pdflatex
    checkPdfLatex = QFile::exists(pdfLatexPath);
    // check for pdftocairo
    checkPdfCairo = QFile::exists(pdfCairoPath);
    // check for ghostscript
    checkGS = QFile::exists(gsPath);


    // Requirements are meet if all checks are true
    reqsAreMeet = checkPdfLatex && checkPdfCairo && checkGS;

    if(reqsAreMeet)
        return ;

    // search MikTex in Path environment variable
    QString pathEnv(qgetenv("Path"));
    QStringList pathsList = pathEnv.split(";");

    QString MikTexPath;
    foreach(QString path, pathsList){
        if(path.contains("MikTex", Qt::CaseInsensitive)){
            MikTexPath=path;
            break;
        }
    }

    qDebug() << "Found Miktex at: " << MikTexPath;

    pdfLatexPath = QDir::toNativeSeparators(MikTexPath+"pdflatex.exe");
    gsPath = QDir::toNativeSeparators(MikTexPath+"mgs.exe");
    //pdfCairoPath = QDir::toNativeSeparators("pdftocairo.exe");

    settings.setValue("pdflatex", pdfLatexPath);
    settings.setValue("pdftocairo", pdfCairoPath);
    settings.setValue("gs", gsPath);
    settings.endGroup();

    checkPdfLatex = QFile::exists(pdfLatexPath);

    checkGS = QFile::exists(gsPath);

    checkPdfCairo = QFile::exists(pdfCairoPath);

    if(!(checkPdfLatex && checkPdfCairo && checkGS)){
        QMessageBox::critical(this, tr("Error - EqualX"), reqMessage, QMessageBox::Ok);
    }
}
