/*
    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 <QObject>
#include <QtNetwork/QUdpSocket>
#include <QAction>
#include <QProcess>
#include <QTimer>
#include <boost/chrono/system_clocks.hpp>
#include <osc/OscReceivedElements.h>

namespace ScIDE {

class ScProcess;
class VolumeWidget;
namespace Settings {
class Manager;
}


class ScServer : public QObject {
    Q_OBJECT

public:
    enum ActionRole {
        ToggleRunning,
        Boot,
        Quit,
        KillAll,
        Reboot,
        ShowMeters,
        ShowScope,
        ShowFreqScope,
        DumpNodeTree,
        DumpNodeTreeWithControls,
        PlotTree,
        DumpOSC,
        Mute,
        Volume,
        VolumeUp,
        VolumeDown,
        VolumeRestore,
        Record,
        PauseRecord,

        ActionCount
    };

    ScServer(ScProcess* scLang, Settings::Manager* settings, QObject* parent);

    bool isRunning() { return mPort != 0; }

    QAction* action(ActionRole role) { return mActions[role]; }

    Q_PROPERTY(float volume READ volume WRITE setVolume NOTIFY volumeChanged)

    float volume() const;
    void setVolume(float volume);
    void setVolumeRange(float min, float max);

    bool isMuted() const;
    void setMuted(bool muted);

    bool isDumpingOSC() const;
    void setDumpingOSC(bool dumping);

    bool isRecording() const;
    bool isPaused() const;

    int recordingTime() const;

public slots:
    void boot();
    void reboot();
    void quit();
    void killAll();
    void toggleRunning();
    void showMeters();
    void showScope();
    void showFreqScope();
    void dumpNodeTree();
    void dumpNodeTreeWithControls();
    void plotTree();
    void queryAllNodes(bool dumpControls);
    void increaseVolume();
    void decreaseVolume();
    void changeVolume(float);
    void restoreVolume();
    void mute() { setMuted(true); }
    void unmute() { setMuted(false); }
    void sendRecording(bool active);
    void setRecording(bool active);
    void pauseRecording(bool flag);

signals:
    void runningStateChanged(bool running, QString const& hostName, int port, bool unresponsive);
    void updateServerStatus(int ugenCount, int synthCount, int groupCount, int defCount, float avgCPU, float peakCPU);
    void volumeChanged(float volume);
    void volumeRangeChanged(float min, float max);
    void mutedChanged(bool muted);
    void recordingChanged(bool recording);
    void pauseChanged(bool paused);


private slots:
    void onScLangStateChanged(QProcess::ProcessState);
    void onScLangReponse(const QString& selector, const QString& data);
    void onServerDataArrived();
    void updateToggleRunningAction();
    void updateRecordingAction();
    void updateEnabledActions();
    void sendMuted(bool muted);
    void sendVolume(float volume);
    void sendDumpingOSC(bool dumping);

protected:
    virtual void timerEvent(QTimerEvent* event);

private:
    void createActions(Settings::Manager*);
    void handleRuningStateChangedMsg(const QString& data);
    void onRunningStateChanged(bool running, QString const& hostName, int port);

    void processServerStatusMessage(const osc::ReceivedMessage&);

    void processOscMessage(const osc::ReceivedMessage&);

    void processOscPacket(const osc::ReceivedPacket& packet) {
        if (packet.IsMessage())
            processOscMessage(osc::ReceivedMessage(packet));
        else
            processOscBundle(osc::ReceivedBundle(packet));
    }

    void processOscBundle(const osc::ReceivedBundle& bundle) {
        for (auto iter = bundle.ElementsBegin(); iter != bundle.ElementsEnd(); ++iter) {
            const osc::ReceivedBundleElement& element = *iter;
            if (element.IsMessage())
                processOscMessage(osc::ReceivedMessage(element));
            else
                processOscBundle(osc::ReceivedBundle(element));
        }
    }

    ScProcess* mLang;

    QUdpSocket* mUdpSocket;
    QHostAddress mServerAddress;
    int mPort;

    QAction* mActions[ActionCount];

    float mVolume = 0, mVolumeMin = -90, mVolumeMax = 6;
    VolumeWidget* mVolumeWidget;
    int mRecordingSeconds;
    bool mIsRecording;
    bool mIsRecordingPaused;
};

}
