/****************************************************************************
** qspdsay - un entorno gráfico al comando spd-say
**---------------------------------------------------------------------------
** qspdsay - main.cpp, qspdsay.cpp, qspdsay.h, qspdsay.qrc, qspdsay.ui
**===========================================================================
** FJA - neocipres@gmail.com                Julio de 2011   (12/07/2012)
**===========================================================================
** This file may be used under the terms of the GNU General Public
** License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of
** this file.  Please review the following information to ensure GNU
** General Public Licensing requirements will be met:
**
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
****************************************************************************/
#include "qspdsay.h"
#include "ui_qspdsay.h"
#include <QMessageBox>
#include <QFileDialog>
#include <QTextStream>
#include <QKeyEvent>
#include <QSettings>

qspdsay::qspdsay(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::qspdsay)
{
    ui->setupUi(this);
    comando = "spd-say";
    acfg = "/home/paco/.speech-dispatcher/conf/speechd.conf";
    QString argsI = QString::null;
    argsI.append("-l "+ui->cBLengua->currentText());
    argsI.append(" -t "+ui->cBLector->currentText());
    ui->lEArgumentos->setText(argsI);
    proc = new QProcess(ui->centralWidget);

    connect(ui->actionAcerca_de_Qt, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
    connect(ui->actionAcerca_de_qspdsay, SIGNAL(triggered()), this, SLOT(acercade()));

    connect(ui->pBAceptar, SIGNAL(clicked()), this, SLOT(aceptar()));
    connect(ui->pBStop, SIGNAL(clicked()), this, SLOT(stopLeer()));
    connect(ui->actionAyuda_spd_say, SIGNAL(triggered()), this, SLOT(ayuda()));
    connect(ui->cBLengua, SIGNAL(currentIndexChanged(QString )), this, SLOT(modificaLengua(QString )));
    connect(ui->cBLector, SIGNAL(currentIndexChanged(QString )), this, SLOT(modificaLector(QString )));
    connect(ui->hSRate, SIGNAL(valueChanged(int)), this, SLOT(modificaRate(int )));
    connect(ui->actionAbrir_Archivo, SIGNAL(triggered()), this, SLOT(abrirA()));
    connect(ui->actionGuardar_Archivo, SIGNAL(triggered()), this, SLOT(guardarAcomo()));
    connect(ui->actionGuardar, SIGNAL(triggered()), this, SLOT(guardarA()));
    connect(ui->actionCerrar, SIGNAL(triggered()), this, SLOT(cerrarA()));
    connect(ui->actionVer_speechd_conf, SIGNAL(triggered()), this, SLOT(verConf()));

    connect(proc, SIGNAL(readyReadStandardOutput()), this, SLOT(readFromStdout()));
    connect(proc, SIGNAL(readyReadStandardError()), this, SLOT(readFromStderr()));
    connect(proc, SIGNAL(error(QProcess::ProcessError )), this, SLOT(tipoError(QProcess::ProcessError )));

    for (int i = 0; i < MaxRecentFiles; ++i) {
            recentFileActs[i] = new QAction(this);
            recentFileActs[i]->setVisible(false);
            connect(recentFileActs[i], SIGNAL(triggered()), this, SLOT(openRecentFile()));
        }

    crearMenu();
    readSettings();

}

qspdsay::~qspdsay()
{
    //writeSettings();
    delete ui;
}

void qspdsay::crearMenu()
{   ui->menuArchivo->addSeparator();
    for (int i = 0; i < MaxRecentFiles; ++i)
            ui->menuArchivo->addAction(recentFileActs[i]);
    updateRecentFileActions();
}

void qspdsay::acercade()
{
    QString separador = QString::null;
    separador.fill('=', 40);
    QString nomAp = "<p><b><BIG><BIG>qspdsay</BIG></BIG></b></p>";
    QString desAp1 = "<p><b>"+QString::fromUtf8("Entorno gráfico al comando spd-say")+"</b></p>";
    QString desAp2 = "<p>"+QString::fromUtf8("<br><b>spd-say</b> es un comando que forma parte del proyecto <a href= http://devel.freebsoft.org/speechd>speech-dispatcher</a>, el cual proporciona una capa independiente del dispositivo para la síntesis de voz.")+"</p>";
    QString teclas = QString::null;
    teclas.append(QString::fromUtf8("<p>F1 -> Inicia el proceso de lectura</p>" ));
    QString autor =  "<p><b>FJA</b> - neocipres@gmail.com</p>";
    QString version = QString::fromUtf8("<p>Versión: 0.9.9</p>");
    QString modificado = "<p>Julio de 2011  -  Modificado el 12 de Jul/2012</p>";
    QString licencia = QString::fromUtf8("<p>Distribuido de acuerdo a los términos de la <a href= http://www.viti.es/gnu/licenses/gpl.html>licencia GNU/GPL</a></p>");
    QString  gnu = "<p><a href= http://www.gnu.org/home.es.html>Proyecto GNU</a></p>";
    QMessageBox::about(ui->centralWidget, "Acerca de ...",nomAp+desAp1+desAp2+teclas+separador+autor+modificado+separador+version+licencia+gnu);
}

void qspdsay::aceptar()
{
    QString cmd = comando;
    QStringList args = (QStringList() << ui->lEArgumentos->text().split(" "));
    if (ui->rBLinea->isChecked()) {
        QString linea = leeLinea();
        args << linea;
    }
    if (ui->rBBloque->isChecked()) {
        QString bloque = leeBloque();
        args << bloque;
    }
    if (ui->rBTextoC->isChecked()) {

        args << ui->tEEditor->toPlainText();
    }
    ejecutar(cmd, args);
}

void qspdsay::stopLeer()
{
    cerrarA();
    ui->rBLinea->setChecked(true);
    aceptar();

}

void qspdsay::ejecutar(QString cmd, QStringList args)
{
    ui->tESalida->clear();
    if (proc->state() == 0) proc->start(cmd, args, QIODevice::ReadOnly);
    else ui->statusBar->showMessage(QString("El proceso %1 no puede ejecutarse, hay otro proceso corriendo.").arg(cmd), 5000);

}

void qspdsay::readFromStdout()
{
    QByteArray datos = proc->readAllStandardOutput();
    QString linea = QString::null;
    linea.append(QString::fromUtf8(datos));

    ui->tESalida->append(linea);
}

void qspdsay::readFromStderr()
{
    QByteArray datos = proc->readAllStandardError();
    QString linea = QString::null;

    ui->tESalida->append("<b>"+linea+"</b>");
}

void qspdsay::tipoError(QProcess::ProcessError error)
{
    QString cadE;

    switch (error) {
    case QProcess::FailedToStart:
        cadE = QString::fromUtf8("El proceso %1 ha fallado al iniciarse.\nPuede que no exista, o no se tengan los permisos suficientes").arg(comando);
        break;
    case  QProcess::Crashed:
        cadE = QString::fromUtf8("Después de un inicio correcto,\nel proceso %1 se ha roto.").arg(comando);
        break;
    case  QProcess::Timedout:
        cadE = "The last waitFor...() function timed out. The state of QProcess is unchanged, and you can try calling waitFor...() again.";
        break;
    case QProcess::WriteError:
        cadE = "An error occurred when attempting to write to the process.\nFor example, the process may not be running, or it may have closed its input channel.";
        break;
    case QProcess::ReadError:
        cadE = "An error occurred when attempting to read from the process.\nFor example, the process may not be running.";
        break;
    case QProcess::UnknownError:
        cadE = QString::fromUtf8("Un error desconocido en el proceso %1.").arg(comando);
        break;
    }
    QMessageBox::warning( ui->centralWidget,
                          tr("Error - spd-say"),
                          tr("Error: ")+QString::number(error)+". "+cadE,
                          tr("Aceptar") );
}

void qspdsay::ayuda()
{
    QStringList args;
    args << "-h";
    ejecutar("spd-say", args);
}

void qspdsay::modificaLengua(QString )
{
    modificarArgs();
}

void qspdsay::modificaLector(QString )
{
    modificarArgs();
}

void qspdsay::modificaRate(int )
{
    modificarArgs();
}


void qspdsay::modificarArgs()
{
    QString args;
    ui->lEArgumentos->clear();
    args.append("-l "+ui->cBLengua->currentText());
    args.append(" -t "+ui->cBLector->currentText());
    if (ui->hSRate->value() != 0) args.append(" -r "+QString::number(ui->hSRate->value()));
    ui->lEArgumentos->setText(args);
}

QString qspdsay::leeLinea()
{
    c = ui->tEEditor->textCursor();
    c.select(QTextCursor::LineUnderCursor); // QTextCursor::BlockUnderCursor
    QString linea = c.selectedText();
    c.movePosition(QTextCursor::Down); // QTextCursor::NextBlock
    ui->tEEditor->setTextCursor(c);
    return linea;
}

QString qspdsay::leeBloque()
{
    c = ui->tEEditor->textCursor();
    c.select(QTextCursor::BlockUnderCursor);
    QString bloque = c.selectedText();
    c.movePosition(QTextCursor::NextBlock);
    ui->tEEditor->setTextCursor(c);
    return bloque;
}

void qspdsay::abrirA()
{
    QString nomF = QFileDialog::getOpenFileName(this,
                                    tr("Abrir archivo de texto a voz"),
                                    QDir::homePath(),
                                    tr("Archivos de voz (*.voz);;Archivos de texto (*.txt)"));
    if ( !nomF.isEmpty () ) {
            loadFile(nomF);
    }
}

void qspdsay::loadFile(const QString &nomF)
{
    QFile fichero(nomF);
    if (!fichero.open( QFile::ReadOnly | QFile::Text ))
        {
           QString aviso = tr("No se puede abrir el archivo:\n\n %1\n\n%2").arg(nomF).arg(fichero.errorString());
           QMessageBox::critical(ui->centralWidget,
                          "Error al abrir",
                          aviso,
                          "Aceptar");
           return;
    }
    QTextStream ts(&fichero);
    ui->tEEditor->clear();
    ui->tEEditor->setText(ts.readAll());
    setCurrentFile(nomF);
    ui->statusBar->showMessage(tr("Archivo cargado"), 3000);
}

bool qspdsay::txtNoMod()
{
    if (ui->tEEditor->document()->isModified()) {
            int ret = QMessageBox::warning(this, tr("archivo modificado"),
            QString::fromUtf8("El documento ha sido modificado.\n"
                "¿Quiere salvar los cambios?"),
            QMessageBox::Yes | QMessageBox::Default,
            QMessageBox::No,
            QMessageBox::Cancel | QMessageBox::Escape);
            if (ret == QMessageBox::Yes)
                return guardarA();
            else if (ret == QMessageBox::Cancel)
                return false;
        }
    return true;
}

bool qspdsay::guardarA()
{
    if (curFile.isEmpty())
            return guardarAcomo();
        else
            saveFile(curFile);
            return true;
}

bool qspdsay::guardarAcomo()
{
    QString nomF = QFileDialog::getSaveFileName(this,
                                   tr("Guardar archivo"),
                                   QDir::homePath(),
                                   tr("Archivos de voz (*.voz);;Archivos de texto (*.txt)"));

    if (nomF.isEmpty()) return false;
    //if (!nomF.contains(".voz")) {nomF.append(".voz");}
    saveFile(nomF);
    return true;
}

void qspdsay::saveFile(const QString &nomF)
{
    QFile fichero(nomF);
    if ( !fichero.open(QFile::WriteOnly | QFile::Text) ){
            QString aviso = tr("No se puede grabar el archivo:\n\n %1\n\n%2").arg(nomF).arg(fichero.errorString());
            QMessageBox::critical(ui->centralWidget,
                   "Error al grabar",
                    aviso,
                   "Aceptar");
            return;
    }
    QTextStream ts(&fichero);
    ts << ui->tEEditor->toPlainText();
    fichero.close();
    QApplication::restoreOverrideCursor();
    setCurrentFile(nomF);
    ui->statusBar->showMessage("Archivo de voz: "+nomF+" guardado", 5000);
}

void qspdsay::cerrarA()
{
    curFile = "";
    ui->tEEditor->clear();

}

void qspdsay::verConf()
{
    QFile fichero(acfg);
    if ( !fichero.open( QFile::ReadOnly | QFile::Text ) ){
            ui->statusBar->showMessage("No se puede abrir: "+acfg, 5000);
            return;
    }
    QTextStream ts(&fichero);
    ui->tESalida->clear();
    ui->tESalida->setText(ts.readAll());
}

void qspdsay::keyPressEvent(QKeyEvent *event)
{
    int tecla = event->key();
    switch (tecla) {
        case Qt::Key_F1:
            aceptar();
            break;
 }
}

void qspdsay::closeEvent(QCloseEvent* event)
{
    if (txtNoMod()) {
        writeSettings();
        event->accept();
    } else {
        event->ignore();
    }
}

void qspdsay::openRecentFile()
{
    QAction *action = qobject_cast<QAction *>(sender());
    if (action)
        loadFile(action->data().toString());
}

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

void qspdsay::updateRecentFileActions()
{
    QSettings settings;
    QStringList files = settings.value("recentFileList").toStringList();

    int numRecentFiles = qMin(files.size(), (int)MaxRecentFiles);

    for (int i = 0; i < numRecentFiles; ++i) {
        QString text = tr("&%1 %2").arg(i + 1).arg(strippedName(files[i]));
        recentFileActs[i]->setText(text);
        recentFileActs[i]->setData(files[i]);
        recentFileActs[i]->setVisible(true);
    }
    for (int j = numRecentFiles; j < MaxRecentFiles; ++j)
        recentFileActs[j]->setVisible(false);

    //separatorAct->setVisible(numRecentFiles > 0);
}

void qspdsay::setCurrentFile(const QString &fileName)
{
    curFile = fileName;
    setWindowFilePath(curFile);

    QSettings settings;
    QStringList files = settings.value("recentFileList").toStringList();
    files.removeAll(fileName);
    files.prepend(fileName);
    while (files.size() > MaxRecentFiles)
        files.removeLast();

    settings.setValue("recentFileList", files);

    foreach (QWidget *widget, QApplication::topLevelWidgets()) {
        qspdsay *mainWin = qobject_cast<qspdsay *>(widget);
        if (mainWin)
            mainWin->updateRecentFileActions();
    }
}

void qspdsay::readSettings()
{
    QSettings settings;
    QPoint pos = settings.value("pos", QPoint(20, 20)).toPoint();
    QSize size = settings.value("size", QSize(800, 600)).toSize();
    QByteArray state = settings.value("state", QByteArray()).toByteArray();
    QString ua = settings.value("ua", "").toString();
    restoreState(state);
    resize(size);
    move(pos);
    if (!ua.isEmpty()) loadFile(ua);
}

void qspdsay::writeSettings()
{
    /* Save postion/size of main window y el último archivo abierto */

    QSettings settings;
    settings.setValue("pos", pos());
    settings.setValue("size", size());
    settings.setValue("state", saveState());
    settings.setValue("ua", curFile);
}