
/**
 *
 *  This file is part of the kdewebdev package
 *  Copyright (c) 2002 Keith Isdale <keith@kdewebdev.org>
 *
 *  This library 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 library 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
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public License
 *  along with this library; see the file COPYING.LIB.  If not, write to
 *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 *  Boston, MA 02110-1301, USA.
 **/

#include <klocale.h>
#include <kurl.h>

#include <libxml/tree.h>
#include <libxslt/xsltInternals.h>
#include "xsldbgdebugger.h"

#include <libxsldbg/xsldbgthread.h>
#include <libxsldbg/xsldbgevent.h>
#include <libxsldbg/qtnotifier2.h>
#include <libxsldbg/options.h>
#include <libxsldbg/files.h>
#include <libxsldbg/utils.h>
#include <QTimerEvent>
#include <QEvent>

extern int xsldbgStop;

#include "xsldbgwalkspeedimpl.h"
#include <QMessageBox>
#include <kdebug.h> 

XsldbgDebugger::XsldbgDebugger()
{
    waitingFirstmessage = true;
    initialized = false;
    inspector = 0L;
    walkDialog = 0L;
    outputFileActive = false;

    updateText = "";
    lastType =  XSLDBG_MSG_AWAITING_INPUT;
    readMsg = false;
    procMsg = false;
    // set a slow occurrence of timer events to check for xsldbg commands from user
    updateTimerID  = startTimer(100);

    connectNotifier(this);
}


XsldbgDebugger::~XsldbgDebugger(){
    // ensure that no more notifications are sent
    connectNotifier(0);

    if (getInitialized())
        xsldbgThreadFree();

    if (walkDialog != 0L)
        walkDialog->close();

    setInitialized(false);
}


void  XsldbgDebugger::setInspector(XsldbgInspector *inspector)
{
    this->inspector = inspector;
}

bool XsldbgDebugger::event(QEvent *e)
{
    if (e == 0L)
        return false;

    if (e->type() != QEvent::User) {
        return QObject::event(e);
    }else{
        if (waitingFirstmessage){
            waitingFirstmessage = false;
            emit debuggerReady();
        }

        // we now have a notify message from xsldbg
        XsldbgEvent *event = dynamic_cast<XsldbgEvent*>(e);
        // send to this debugger the messages in event
        event->emitMessage(this);
    }
    return true;

}


void XsldbgDebugger::timerEvent(QTimerEvent *e)
{
    // This function runs in the application's thread

    if (e->timerId() != updateTimerID) {
        XsldbgDebuggerBase::timerEvent(e);
        return;
    }

    if  ((getInputReady() == 0) && (getInputStatus() == XSLDBG_MSG_AWAITING_INPUT) &&
            (commandQue.count() > 0)){
        QString msg = commandQue.first();
        commandQue.removeAll(msg);
        ::fakeInput((const char*)msg.toUtf8().constData());
    }

    if ((!updateText.isEmpty()) && (getInputStatus() == XSLDBG_MSG_AWAITING_INPUT)){
        // flush remainding text to message window
        QString msgCopy = updateText;
        updateText = "";
        emit showMessage(msgCopy);
        lastType  = XSLDBG_MSG_AWAITING_INPUT;
    }

}


QString XsldbgDebugger::fixLocalPaths(QString & file)
{
    QString result = file;

    if (file.left(6) == "file:/")
        result = filesExpandName(file);

    return result;
}


QString XsldbgDebugger::sourceFileName()
{
    return optionsGetStringOption(OPTIONS_SOURCE_FILE_NAME);
}

QString XsldbgDebugger::dataFileName()
{
    return optionsGetStringOption(OPTIONS_DATA_FILE_NAME);
}

QString XsldbgDebugger::outputFileName()
{
    return optionsGetStringOption(OPTIONS_OUTPUT_FILE_NAME);
}

QString XsldbgDebugger::workDirName()
{
    return optionsGetStringOption(OPTIONS_CWD);
}
void XsldbgDebugger::gotoLine(QString fileName, int lineNo, bool breakpoint /*= false*/)
{
    emit lineNoChanged(fileName, lineNo, breakpoint);
}


void XsldbgDebugger::fakeInput(QString text, bool wait)
{
    Q_UNUSED(wait);
    commandQue.append(text);
}


bool XsldbgDebugger::start()
{
    bool result = false;
    if (!getInitialized() && !xsldbgThreadInit()) {
        xsldbgThreadFree();
        kDebug() << "Init of thread failed";
    } else {
        setInitialized(true);
        result = true;
    }
    return result;
}

bool XsldbgDebugger::stop()
{
    if (getInitialized()){
        setThreadStatus(XSLDBG_MSG_THREAD_STOP);
        fakeInput("quit", true);
    }

    // it always succeeds at the moment
    return true;
}

void XsldbgDebugger::slotConfigure()
{
    if (start() == false)
        return;

    if(inspector == 0L ){
        inspector = new XsldbgInspector(this);
        connect(inspector, SIGNAL(closedWindow()), this, SLOT(slotConfigClosed()));
    }
}


void XsldbgDebugger::slotConfigClosed()
{
    inspector = 0L;
}

void XsldbgDebugger::slotStepCmd()
{
    if (start())
        fakeInput("step", true);

    if (inspector != 0L)
        inspector->refreshVariables();
}

void XsldbgDebugger::slotContinueCmd()
{
    if (start())
        fakeInput("continue", false); // don't wait for xsldbg to finish command

    if (inspector != 0L)
        inspector->refreshVariables();

}

void XsldbgDebugger::slotRunCmd()
{
    if (start())
        fakeInput("run", false); // don't wait for xsldbg to finish command

    if (inspector != 0L)
        inspector->refresh();
}


void XsldbgDebugger::slotWalkSpeed(int speed)
{
    if ((speed >= 0) && (speed <= 9)){
        if (start()){
            if (optionsGetIntOption(OPTIONS_WALK_SPEED) == WALKSPEED_STOP){
                // start walking at speed requested
                QString msg("walk ");
                msg.append(QString::number(speed));
                fakeInput(msg, true);
            } else
                // This will take effect imediately
                optionsSetIntOption(OPTIONS_WALK_SPEED, speed);
        }
    }else
        kDebug() << "Invalid walk speed " << speed;
}

void XsldbgDebugger::slotWalkCmd()
{
    if  (walkDialog == 0L )
        walkDialog = new XsldbgWalkSpeedImpl (this);

    if (walkDialog != 0L)
        walkDialog->show(); // if the user changes the speed the dialog will call back slotWalkSpeed(int)
}

void XsldbgDebugger::slotWalkStopCmd()
{
    xsldbgStop = 1;
}

void XsldbgDebugger::slotTraceCmd()
{
    if (start())
        fakeInput("trace", false); // don't wait for xsldbg to finish command
}

void XsldbgDebugger::slotBreakCmd(QString fileName, int lineNumber)
{
    if (outputFileActive == true){
        QMessageBox::information(0L, i18n("Operation Failed"),
                i18n("Cannot set/edit breakpoints on the output file."),
                QMessageBox::Ok);
        return ;
    }

    QString msg("break -l \"");
    msg.append(XsldbgDebugger::fixLocalPaths(fileName)).append("\" ").append(QString::number(lineNumber));

    if (start())
        fakeInput(msg, true);

    if (inspector != 0L)
        inspector->refreshBreakpoints();
}

void XsldbgDebugger::slotBreakCmd(QString templateName, QString modeName)
{

    if (outputFileActive == true){
        QMessageBox::information(0L, i18n("Operation Failed"),
                i18n("Cannot set/edit breakpoints on the output file."),
                QMessageBox::Ok);
        return ;
    }
    QString msg("break \"");
    msg.append(templateName).append("\" \"").append(modeName).append("\"");
    if (start())
        fakeInput(msg, true);

    if (inspector != 0L)
        inspector->refreshBreakpoints();
}

void XsldbgDebugger::slotEnableCmd(QString fileName, int lineNumber)
{

    if (outputFileActive == true){
        QMessageBox::information(0L, i18n("Operation Failed"),
                i18n("Cannot set/edit breakpoints on the output file."),
                QMessageBox::Ok);
        return ;
    }

    QString msg("enable -l \"");
    msg.append(XsldbgDebugger::fixLocalPaths(fileName)).append("\" ").append(QString::number(lineNumber));
    if (start())
        fakeInput(msg, true);

    if (inspector != 0L)
        inspector->refreshBreakpoints();
}


void XsldbgDebugger::slotEnableCmd(int id)
{

    if (outputFileActive == true){
        QMessageBox::information(0L, i18n("Operation Failed"),
                i18n("Cannot set/edit breakpoints on the output file."),
                QMessageBox::Ok);
        return ;
    }

    QString msg("enable ");
    msg.append(QString::number(id));
    if (start())
        fakeInput(msg, true);

    if (inspector != 0L)
        inspector->refreshBreakpoints();
}


void XsldbgDebugger::slotDeleteCmd(QString fileName, int lineNumber)
{

    if (outputFileActive == true){
        QMessageBox::information(0L, i18n("Operation Failed"),
                i18n("Cannot set/edit breakpoints on the output file."),
                QMessageBox::Ok);
        return ;
    }

    QString msg("delete -l \"");
    msg.append(XsldbgDebugger::fixLocalPaths(fileName)).append("\" ").append(QString::number(lineNumber));
    if (start())
        fakeInput(msg, true);
    if (inspector != 0L)
        inspector->refreshBreakpoints();
}


void XsldbgDebugger::slotDeleteCmd(int id)
{

    if (outputFileActive == true){
        QMessageBox::information(0L, i18n("Operation Failed"),
                i18n("Cannot set/edit breakpoints on the output file."),
                QMessageBox::Ok);
        return ;
    }
    QString msg("delete ");
    msg.append(QString::number(id));
    if (start())
        fakeInput(msg, true);
    if (inspector != 0L)
        inspector->refreshBreakpoints();
}


void XsldbgDebugger::slotSourceCmd()
{
    if (start()){
        outputFileActive = false;
        fakeInput("source", true);
    }
}


void XsldbgDebugger::slotDataCmd()
{
    if (start()){
        outputFileActive = false;
        fakeInput("data", true);
    }
}

void XsldbgDebugger::slotShowDocument()
{

    if (outputFileName().length() > 0){
        outputFileActive = true;
        gotoLine(outputFileName(), 1);
    }
}


void XsldbgDebugger::slotExitCmd()
{
    stop();
}


void XsldbgDebugger::slotCatCmd(QString xPathExpression){
    QString msg("cat ");
    msg.append(xPathExpression);

    if (start())
        fakeInput(msg, false); // don't wait for xsldbg to finish command

}

void  XsldbgDebugger::slotCdCmd(QString xPathExpression){
    QString msg("cd ");
    msg.append(xPathExpression);

    if (start())
        fakeInput(msg, true);
}
void XsldbgDebugger::slotSetVariableCmd(QString variableName, QString xPathExpression)
{
    if (!variableName.isEmpty() && !xPathExpression.isEmpty()){
        QString msg("set ");
        msg.append(variableName);
        msg.append(" \"");
        msg.append(xPathExpression);
        msg.append("\"");

        if (start())
            fakeInput(msg, true);
    }
}

void XsldbgDebugger::readSettings()
{
    if (start())
        fakeInput("readconfig", false); // don't wait for xsldbg to finish command
}

void XsldbgDebugger::writeSettings()
{
    if (start())
        fakeInput("writeconfig", false); // don't wait for xsldbg to finish command
}

#include "xsldbgdebugger.moc"
