/*
    SuperCollider Qt IDE
    Copyright (c) 2012 Jakob Leben & Tim Blechmann
    http://www.audiosynth.com

    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 program 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, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
*/

#pragma once

#include "sc_introspection.hpp"

#include <QAction>
#include <QByteArray>
#include <QDateTime>
#include <QDebug>
#include <QProcess>
#include <QThread>
#include <QUuid>
#include <QtNetwork/QLocalSocket>
#include <QtNetwork/QLocalServer>

namespace ScIDE {

namespace Settings { class Manager; }
class ScIntrospectionParser;

class ScProcess:
    public QProcess
{
    Q_OBJECT

public:
    ScProcess( Settings::Manager *, QObject * parent );

    enum ActionRole {
        ToggleRunning = 0,
        Start,
        Stop,
        Restart,
        RecompileClassLibrary,
        StopMain,
        ShowQuarks,

        ActionCount
    };

    const ScLanguage::Introspection & introspection() { return mIntrospection; }

    void sendRequest( const QString &id, const QString &command, const QString &data )
    {
        QString cmd = QStringLiteral("ScIDE.request(\"%1\",'%2',\"%3\")")
            .arg(id)
            .arg(command)
            .arg(data);

        evaluateCode(cmd, true);
    }

    QAction *action(ActionRole role)
    {
        return mActions[role];
    }
    
    bool compiled() { return mCompiled; }
    
    void post(QString & text) { scPost(text); }
    void updateTextMirrorForDocument ( class Document * doc, int position, int charsRemoved, int charsAdded );
    void updateSelectionMirrorForDocument ( class Document * doc, int start, int range);

public slots:
    void toggleRunning();
    void startLanguage (void);
    void stopLanguage (void);
    void restartLanguage (void);
    void recompileClassLibrary (void);
    void stopMain(void);
    void showQuarks(void);
    void evaluateCode(QString const & commandString, bool silent = false);

signals:
    void scPost(QString const &);
    void statusMessage(const QString &);
    void response(const QString & selector, const QString & data);
    void classLibraryRecompiled();
    void introspectionChanged();

private slots:
    void onNewIpcConnection();
    void onIpcData();
    void finalizeConnection();
    void onProcessStateChanged( QProcess::ProcessState state);
    void onReadyRead(void);
    void updateToggleRunningAction();

private:
    void onStart();
    void onResponse( const QString & selector, const QString & data );

    void prepareActions(Settings::Manager * settings);
    void postQuitNotification();    
    QAction * mActions[ActionCount];

    ScLanguage::Introspection mIntrospection;
    ScIntrospectionParser *mIntrospectionParser;

    QLocalServer *mIpcServer;
    QLocalSocket *mIpcSocket;
    QString mIpcServerName;
    QByteArray mIpcData;
    int mReadSize = 0;

    bool mTerminationRequested;
    QDateTime mTerminationRequestTime;
    bool mCompiled;
};

class ScRequest : public QObject
{
    Q_OBJECT
public:
    ScRequest( ScProcess *sc, QObject * parent = 0 ):
        QObject(parent),
        mSc(sc)
    {
        connect(mSc, SIGNAL(response(QString,QString)),
                this, SLOT(onResponse(QString,QString)));

        connect(mSc, SIGNAL(classLibraryRecompiled()),
                this, SLOT(cancel()));
    }

    void send( const QString & command, const QString & data )
    {
        mId = QUuid::createUuid();
        mCommand = command;
        mSc->sendRequest(mId.toString(), command, data);
    }

public slots:
    void cancel()
    {
        mId = QUuid();
        emit cancelled();
    }

signals:
    void response( const QString & command, const QString & data );
    void cancelled();

private slots:
    void onResponse( const QString & responseId, const QString & responseData )
    {
        if (responseId == mId.toString())
            emit response(mCommand, responseData);
    }

private:
    QString mCommand;
    QUuid mId;
    ScProcess *mSc;
};
}
