/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 * $Id$
 *
 * Copyright (C) 2010 Robert Bieber
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/

#include "editorwindow.h"
#include "projectmodel.h"
#include "ui_editorwindow.h"

#include <QDesktopWidget>
#include <QFileSystemModel>
#include <QSettings>
#include <QFileDialog>
#include <QGraphicsScene>

EditorWindow::EditorWindow(QWidget *parent) :
    QMainWindow(parent), ui(new Ui::EditorWindow), parseTreeSelection(0)
{
    ui->setupUi(this);
    prefs = new PreferencesDialog(this);
    project = 0;
    setupUI();
    loadSettings();
    setupMenus();
}


EditorWindow::~EditorWindow()
{
    delete ui;
    delete prefs;
    if(project)
        delete project;
    delete deviceConfig;
    delete deviceDock;
}

void EditorWindow::loadTabFromSkinFile(QString fileName)
{
    /* Checking to see if the file is already open */
    for(int i = 0; i < ui->editorTabs->count(); i++)
    {
        TabContent* current = dynamic_cast<TabContent*>
                                (ui->editorTabs->widget(i));
        if(current->file() == fileName)
        {
            ui->editorTabs->setCurrentIndex(i);
            return;
        }
    }

    /* Adding a new document*/
    SkinDocument* doc = new SkinDocument(parseStatus, fileName, project,
                                         deviceConfig);
    addTab(doc);
    ui->editorTabs->setCurrentWidget(doc);

}

void EditorWindow::loadConfigTab(ConfigDocument* doc)
{
    for(int i = 0; i < ui->editorTabs->count(); i++)
    {
        TabContent* current = dynamic_cast<TabContent*>
                              (ui->editorTabs->widget(i));
        if(current->file() == doc->file())
        {
            ui->editorTabs->setCurrentIndex(i);
            doc->deleteLater();
            return;
        }
    }

    addTab(doc);
    ui->editorTabs->setCurrentWidget(doc);

    QObject::connect(doc, SIGNAL(titleChanged(QString)),
                     this, SLOT(tabTitleChanged(QString)));
}

void EditorWindow::loadSettings()
{

    QSettings settings;

    /* Main Window location */
    settings.beginGroup("EditorWindow");
    QSize size = settings.value("size").toSize();
    QPoint pos = settings.value("position").toPoint();
    QByteArray state = settings.value("state").toByteArray();
    settings.endGroup();

    if(!(size.isNull() || pos.isNull() || state.isNull()))
    {
        resize(size);
        move(pos);
        restoreState(state);
    }

}

void EditorWindow::saveSettings()
{

    QSettings settings;

    /* Saving window and panel positions */
    settings.beginGroup("EditorWindow");
    settings.setValue("position", pos());
    settings.setValue("size", size());
    settings.setValue("state", saveState());
    settings.endGroup();
}

void EditorWindow::setupUI()
{
    /* Connecting the tab bar signals */
    QObject::connect(ui->editorTabs, SIGNAL(currentChanged(int)),
                     this, SLOT(shiftTab(int)));
    QObject::connect(ui->editorTabs, SIGNAL(tabCloseRequested(int)),
                     this, SLOT(closeTab(int)));

    /* Connecting the code gen button */
    QObject::connect(ui->fromTree, SIGNAL(pressed()),
                     this, SLOT(updateCurrent()));

    /* Connecting the preferences dialog */
    QObject::connect(ui->actionPreferences, SIGNAL(triggered()),
                     prefs, SLOT(exec()));

    /* Setting up the parse status label */
    parseStatus = new QLabel(this);
    ui->statusbar->addPermanentWidget(parseStatus);

    /* Setting the selection for parse tree highlighting initially NULL */
    parseTreeSelection = 0;

    /* Adding the skin viewer */
    viewer = new SkinViewer(this);
    ui->skinPreviewLayout->addWidget(viewer);

    /* Positioning the device settings dialog */
    deviceDock = new QDockWidget(tr("Device Configuration"), this);
    deviceConfig = new DeviceState(deviceDock);

    deviceDock->setObjectName("deviceDock");
    deviceDock->setWidget(deviceConfig);
    deviceDock->setFloating(true);
    deviceDock->hide();

    shiftTab(-1);
}

void EditorWindow::setupMenus()
{
    /* Connecting panel show actions */
    QObject::connect(ui->actionFile_Panel, SIGNAL(triggered()),
                     this, SLOT(showPanel()));
    QObject::connect(ui->actionDisplay_Panel, SIGNAL(triggered()),
                     this, SLOT(showPanel()));
    QObject::connect(ui->actionPreview_Panel, SIGNAL(triggered()),
                     this, SLOT(showPanel()));
    QObject::connect(ui->actionDevice_Configuration, SIGNAL(triggered()),
                     deviceDock, SLOT(show()));

    /* Connecting the document management actions */
    QObject::connect(ui->actionNew_Document, SIGNAL(triggered()),
                     this, SLOT(newTab()));
    QObject::connect(ui->actionToolbarNew, SIGNAL(triggered()),
                     this, SLOT(newTab()));

    QObject::connect(ui->actionClose_Document, SIGNAL(triggered()),
                     this, SLOT(closeCurrent()));

    QObject::connect(ui->actionSave_Document, SIGNAL(triggered()),
                     this, SLOT(saveCurrent()));
    QObject::connect(ui->actionSave_Document_As, SIGNAL(triggered()),
                     this, SLOT(saveCurrentAs()));
    QObject::connect(ui->actionToolbarSave, SIGNAL(triggered()),
                     this, SLOT(saveCurrent()));

    QObject::connect(ui->actionOpen_Document, SIGNAL(triggered()),
                     this, SLOT(openFile()));
    QObject::connect(ui->actionToolbarOpen, SIGNAL(triggered()),
                     this, SLOT(openFile()));

    QObject::connect(ui->actionOpen_Project, SIGNAL(triggered()),
                     this, SLOT(openProject()));

    /* Connecting the edit menu */
    QObject::connect(ui->actionUndo, SIGNAL(triggered()),
                     this, SLOT(undo()));
    QObject::connect(ui->actionRedo, SIGNAL(triggered()),
                     this, SLOT(redo()));
    QObject::connect(ui->actionCut, SIGNAL(triggered()),
                     this, SLOT(cut()));
    QObject::connect(ui->actionCopy, SIGNAL(triggered()),
                     this, SLOT(copy()));
    QObject::connect(ui->actionPaste, SIGNAL(triggered()),
                     this, SLOT(paste()));
    QObject::connect(ui->actionFind_Replace, SIGNAL(triggered()),
                     this, SLOT(findReplace()));
}

void EditorWindow::addTab(TabContent *doc)
{
    ui->editorTabs->addTab(doc, doc->title());

    /* Connecting to title change events */
    QObject::connect(doc, SIGNAL(titleChanged(QString)),
                     this, SLOT(tabTitleChanged(QString)));
    QObject::connect(doc, SIGNAL(lineChanged(int)),
                     this, SLOT(lineChanged(int)));

    /* Connecting to settings change events */
    if(doc->type() == TabContent::Skin)
        dynamic_cast<SkinDocument*>(doc)->connectPrefs(prefs);
}


void EditorWindow::newTab()
{
    SkinDocument* doc = new SkinDocument(parseStatus, project, deviceConfig);
    addTab(doc);
    ui->editorTabs->setCurrentWidget(doc);
}

void EditorWindow::shiftTab(int index)
{
    TabContent* widget = dynamic_cast<TabContent*>
                         (ui->editorTabs->currentWidget());
    if(index < 0)
    {
        ui->parseTree->setModel(0);
        ui->actionSave_Document->setEnabled(false);
        ui->actionSave_Document_As->setEnabled(false);
        ui->actionClose_Document->setEnabled(false);
        ui->actionToolbarSave->setEnabled(false);
        ui->fromTree->setEnabled(false);
        ui->actionUndo->setEnabled(false);
        ui->actionRedo->setEnabled(false);
        ui->actionCut->setEnabled(false);
        ui->actionCopy->setEnabled(false);
        ui->actionPaste->setEnabled(false);
        ui->actionFind_Replace->setEnabled(false);
        viewer->setScene(0);
    }
    else if(widget->type() == TabContent::Config)
    {
        ui->actionSave_Document->setEnabled(true);
        ui->actionSave_Document_As->setEnabled(true);
        ui->actionClose_Document->setEnabled(true);
        ui->actionToolbarSave->setEnabled(true);
        ui->actionUndo->setEnabled(false);
        ui->actionRedo->setEnabled(false);
        ui->actionCut->setEnabled(false);
        ui->actionCopy->setEnabled(false);
        ui->actionPaste->setEnabled(false);
        ui->actionFind_Replace->setEnabled(false);
        viewer->setScene(0);
    }
    else if(widget->type() == TabContent::Skin)
    {
        /* Syncing the tree view and the status bar */
        SkinDocument* doc = dynamic_cast<SkinDocument*>(widget);
        ui->parseTree->setModel(doc->getModel());
        parseStatus->setText(doc->getStatus());

        ui->actionSave_Document->setEnabled(true);
        ui->actionSave_Document_As->setEnabled(true);
        ui->actionClose_Document->setEnabled(true);
        ui->actionToolbarSave->setEnabled(true);
        ui->fromTree->setEnabled(true);

        ui->actionUndo->setEnabled(true);
        ui->actionRedo->setEnabled(true);
        ui->actionCut->setEnabled(true);
        ui->actionCopy->setEnabled(true);
        ui->actionPaste->setEnabled(true);
        ui->actionFind_Replace->setEnabled(true);

        sizeColumns();

        /* Syncing the preview */
        viewer->setScene(doc->scene());

    }

    /* Hiding all the find/replace dialogs */
    for(int i = 0; i < ui->editorTabs->count(); i++)
        if(dynamic_cast<TabContent*>(ui->editorTabs->widget(i))->type() ==
           TabContent::Skin)
            dynamic_cast<SkinDocument*>(ui->editorTabs->widget(i))->hideFind();

}

bool EditorWindow::closeTab(int index)
{
    TabContent* widget = dynamic_cast<TabContent*>
                           (ui->editorTabs->widget(index));
    if(widget->requestClose())
    {
        ui->editorTabs->removeTab(index);
        widget->deleteLater();
        return true;
    }

    return false;
}

void EditorWindow::closeCurrent()
{
    closeTab(ui->editorTabs->currentIndex());
}

void EditorWindow::saveCurrent()
{
    if(ui->editorTabs->currentIndex() >= 0)
        dynamic_cast<TabContent*>(ui->editorTabs->currentWidget())->save();
}

void EditorWindow::saveCurrentAs()
{
    if(ui->editorTabs->currentIndex() >= 0)
        dynamic_cast<TabContent*>(ui->editorTabs->currentWidget())->saveAs();
}

void EditorWindow::openFile()
{
    QStringList fileNames;
    QSettings settings;

    settings.beginGroup("SkinDocument");
    QString directory = settings.value("defaultDirectory", "").toString();
    fileNames = QFileDialog::getOpenFileNames(this, tr("Open Files"), directory,
                                              SkinDocument::fileFilter());

    for(int i = 0; i < fileNames.count(); i++)
    {
        if(!QFile::exists(fileNames[i]))
            continue;

        QString current = fileNames[i];

        loadTabFromSkinFile(current);

        /* And setting the new default directory */
        current.chop(current.length() - current.lastIndexOf('/') - 1);
        settings.setValue("defaultDirectory", current);

    }

    settings.endGroup();
}

void EditorWindow::openProject()
{
    QString fileName;
    QSettings settings;

    settings.beginGroup("ProjectModel");
    QString directory = settings.value("defaultDirectory", "").toString();
    fileName = QFileDialog::getOpenFileName(this, tr("Open Project"), directory,
                                            ProjectModel::fileFilter());

    if(QFile::exists(fileName))
    {

        if(project)
            delete project;

        project = new ProjectModel(fileName, this);
        ui->projectTree->setModel(project);

        if(project->getSetting("#screenwidth") != "")
            deviceConfig->setData("screenwidth",
                                  project->getSetting("#screenwidth"));
        if(project->getSetting("#screenheight") != "")
            deviceConfig->setData("screenheight",
                                  project->getSetting("#screenheight"));

        QObject::connect(ui->projectTree, SIGNAL(activated(QModelIndex)),
                         project, SLOT(activated(QModelIndex)));

        fileName.chop(fileName.length() - fileName.lastIndexOf('/') - 1);
        settings.setValue("defaultDirectory", fileName);

        for(int i = 0; i < ui->editorTabs->count(); i++)
        {
            TabContent* doc = dynamic_cast<TabContent*>
                              (ui->editorTabs->widget(i));
            if(doc->type() == TabContent::Skin)
            {
                dynamic_cast<SkinDocument*>(doc)->setProject(project);
                if(i == ui->editorTabs->currentIndex())
                {
                    viewer->setScene(dynamic_cast<SkinDocument*>(doc)->scene());
                }
            }
        }

    }

    settings.endGroup();

}

void EditorWindow::configFileChanged(QString configFile)
{

    if(QFile::exists(configFile))
    {

        if(project)
            delete project;

        project = new ProjectModel(configFile, this);
        ui->projectTree->setModel(project);

        QObject::connect(ui->projectTree, SIGNAL(activated(QModelIndex)),
                         project, SLOT(activated(QModelIndex)));

        for(int i = 0; i < ui->editorTabs->count(); i++)
        {
            TabContent* doc = dynamic_cast<TabContent*>
                              (ui->editorTabs->widget(i));
            if(doc->type() == TabContent::Skin)
            {
                dynamic_cast<SkinDocument*>(doc)->setProject(project);
                if(i == ui->editorTabs->currentIndex())
                {
                    viewer->setScene(dynamic_cast<SkinDocument*>(doc)->scene());
                }
            }
        }

    }


}

void EditorWindow::tabTitleChanged(QString title)
{
    TabContent* sender = dynamic_cast<TabContent*>(QObject::sender());
    ui->editorTabs->setTabText(ui->editorTabs->indexOf(sender), title);
}

void EditorWindow::showPanel()
{
    if(sender() == ui->actionFile_Panel)
        ui->projectDock->setVisible(true);
    if(sender() == ui->actionPreview_Panel)
        ui->skinPreviewDock->setVisible(true);
    if(sender() == ui->actionDisplay_Panel)
        ui->parseTreeDock->setVisible(true);
}

void EditorWindow::closeEvent(QCloseEvent* event)
{

    saveSettings();

    /* Closing all the tabs */
    for(int i = 0; i < ui->editorTabs->count(); i++)
    {
        if(!dynamic_cast<TabContent*>
           (ui->editorTabs->widget(i))->requestClose())
        {
            event->ignore();
            return;
        }
    }

    event->accept();
}

void EditorWindow::updateCurrent()
{
    if(ui->editorTabs->currentIndex() < 0)
        return;

    dynamic_cast<SkinDocument*>
            (ui->editorTabs->currentWidget())->genCode();
}

void EditorWindow::lineChanged(int line)
{
        ui->parseTree->collapseAll();
        ParseTreeModel* model = dynamic_cast<ParseTreeModel*>
                                (ui->parseTree->model());
        parseTreeSelection = new QItemSelectionModel(model);
        expandLine(model, QModelIndex(), line);
        sizeColumns();
        ui->parseTree->setSelectionModel(parseTreeSelection);
}

void EditorWindow::undo()
{
    TabContent* doc = dynamic_cast<TabContent*>
                      (ui->editorTabs->currentWidget());
    if(doc->type() == TabContent::Skin)
        dynamic_cast<SkinDocument*>(doc)->getEditor()->undo();
}

void EditorWindow::redo()
{
    TabContent* doc = dynamic_cast<TabContent*>
                      (ui->editorTabs->currentWidget());
    if(doc->type() == TabContent::Skin)
        dynamic_cast<SkinDocument*>(doc)->getEditor()->redo();

}

void EditorWindow::cut()
{
    TabContent* doc = dynamic_cast<TabContent*>
                      (ui->editorTabs->currentWidget());
    if(doc->type() == TabContent::Skin)
        dynamic_cast<SkinDocument*>(doc)->getEditor()->cut();
}

void EditorWindow::copy()
{
    TabContent* doc = dynamic_cast<TabContent*>
                      (ui->editorTabs->currentWidget());
    if(doc->type() == TabContent::Skin)
        dynamic_cast<SkinDocument*>(doc)->getEditor()->copy();
}

void EditorWindow::paste()
{
    TabContent* doc = dynamic_cast<TabContent*>
                      (ui->editorTabs->currentWidget());
    if(doc->type() == TabContent::Skin)
        dynamic_cast<SkinDocument*>(doc)->getEditor()->paste();
}

void EditorWindow::findReplace()
{
    TabContent* doc = dynamic_cast<TabContent*>
                      (ui->editorTabs->currentWidget());
    if(doc->type() == TabContent::Skin)
        dynamic_cast<SkinDocument*>(doc)->showFind();
}


void EditorWindow::expandLine(ParseTreeModel* model, QModelIndex parent,
                              int line)
{
    for(int i = 0; i < model->rowCount(parent); i++)
    {
        QModelIndex dataType = model->index(i, ParseTreeModel::typeColumn,
                                            parent);
        QModelIndex dataVal = model->index(i, ParseTreeModel::valueColumn,
                                           parent);
        QModelIndex data = model->index(i, ParseTreeModel::lineColumn, parent);
        QModelIndex recurse = model->index(i, 0, parent);

        expandLine(model, recurse, line);

        if(model->data(data, Qt::DisplayRole) == line)
        {
            ui->parseTree->expand(parent);
            ui->parseTree->expand(data);
            ui->parseTree->scrollTo(parent, QAbstractItemView::PositionAtTop);

            parseTreeSelection->select(data, QItemSelectionModel::Select);
            parseTreeSelection->select(dataType, QItemSelectionModel::Select);
            parseTreeSelection->select(dataVal, QItemSelectionModel::Select);
        }

    }
}

void EditorWindow::sizeColumns()
{
    /* Setting the column widths */
    ui->parseTree->resizeColumnToContents(ParseTreeModel::lineColumn);
    ui->parseTree->resizeColumnToContents(ParseTreeModel::typeColumn);
    ui->parseTree->resizeColumnToContents(ParseTreeModel::valueColumn);
}
