/*
 * Carla Plugin Host
 * Copyright (C) 2011-2014 Filipe Coelho <falktx@falktx.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 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.
 *
 * For a full copy of the GNU General Public License see the doc/GPL.txt file.
 */

#ifndef CARLA_ENGINE_INTERNAL_HPP_INCLUDED
#define CARLA_ENGINE_INTERNAL_HPP_INCLUDED

#include "CarlaEngine.hpp"
#include "CarlaEngineOsc.hpp"
#include "CarlaEngineThread.hpp"

#include "CarlaMutex.hpp"
#include "LinkedList.hpp"

// -----------------------------------------------------------------------
// Engine helper macro, sets lastError and returns false/NULL

#define CARLA_SAFE_ASSERT_RETURN_ERR(cond, err)  if (cond) pass(); else { carla_safe_assert(#cond, __FILE__, __LINE__); setLastError(err); return false; }
#define CARLA_SAFE_ASSERT_RETURN_ERRN(cond, err) if (cond) pass(); else { carla_safe_assert(#cond, __FILE__, __LINE__); setLastError(err); return nullptr; }

// -----------------------------------------------------------------------
// Global action lock for UI operations, used for osc only

#ifndef BUILD_BRIDGE
# define CARLA_ENGINE_THREAD_SAFE_SECTION const CarlaCriticalSection::Scope _ccsl(pData->_cs);
#else
# define CARLA_ENGINE_THREAD_SAFE_SECTION
#endif

// -----------------------------------------------------------------------

CARLA_BACKEND_START_NAMESPACE

#if 0
} // Fix editor indentation
#endif

// -----------------------------------------------------------------------
// Maximum pre-allocated events for rack and bridge modes

const unsigned short kMaxEngineEventInternalCount = 512;

// -----------------------------------------------------------------------
// Rack Patchbay stuff

enum RackPatchbayGroupIds {
    RACK_PATCHBAY_GROUP_CARLA     = 0,
    RACK_PATCHBAY_GROUP_AUDIO_IN  = 1,
    RACK_PATCHBAY_GROUP_AUDIO_OUT = 2,
    RACK_PATCHBAY_GROUP_MIDI_IN   = 3,
    RACK_PATCHBAY_GROUP_MIDI_OUT  = 4,
    RACK_PATCHBAY_GROUP_MAX       = 5
};

enum RackPatchbayPortIds {
    RACK_PATCHBAY_PORT_AUDIO_IN1  = -1,
    RACK_PATCHBAY_PORT_AUDIO_IN2  = -2,
    RACK_PATCHBAY_PORT_AUDIO_OUT1 = -3,
    RACK_PATCHBAY_PORT_AUDIO_OUT2 = -4,
    RACK_PATCHBAY_PORT_MIDI_IN    = -5,
    RACK_PATCHBAY_PORT_MIDI_OUT   = -6,
    RACK_PATCHBAY_PORT_MAX        = -7
};

struct PortNameToId {
    int portId;
    char name[STR_MAX+1];
};

struct ConnectionToId {
    uint id;
    int  portOut;
    int  portIn;
};

// -----------------------------------------------------------------------
// EngineRackBuffers

struct EngineRackBuffers {
    float* in[2];
    float* out[2];

    // connections stuff
    LinkedList<int> connectedIn1;
    LinkedList<int> connectedIn2;
    LinkedList<int> connectedOut1;
    LinkedList<int> connectedOut2;
    CarlaCriticalSection connectLock;

    uint lastConnectionId;
    LinkedList<ConnectionToId> usedConnections;

    EngineRackBuffers(const uint32_t bufferSize);
    ~EngineRackBuffers();
    void clear();
    void resize(const uint32_t bufferSize);
    const char* const* getConnections() const;

    CARLA_DECLARE_NON_COPY_STRUCT(EngineRackBuffers)
};

// -----------------------------------------------------------------------
// EnginePatchbayBuffers

struct EnginePatchbayBuffers {
    // TODO
    EnginePatchbayBuffers(const uint32_t bufferSize);
    ~EnginePatchbayBuffers();
    void clear();
    void resize(const uint32_t bufferSize);
    const char* const* getConnections() const;

    CARLA_DECLARE_NON_COPY_STRUCT(EnginePatchbayBuffers)
};

// -----------------------------------------------------------------------
// InternalAudio

struct EngineInternalAudio {
    bool isReady;
    bool usePatchbay;

    uint inCount;
    uint outCount;

    union {
        EngineRackBuffers*     rack;
        EnginePatchbayBuffers* patchbay;
    };

    EngineInternalAudio() noexcept;
    ~EngineInternalAudio() noexcept;
    void initPatchbay() noexcept;
    void clear();
    void create(const uint32_t bufferSize);
    void resize(const uint32_t bufferSize);

    CARLA_DECLARE_NON_COPY_STRUCT(EngineInternalAudio)
};

// -----------------------------------------------------------------------
// InternalEvents

struct EngineInternalEvents {
    EngineEvent* in;
    EngineEvent* out;

    EngineInternalEvents() noexcept;
    ~EngineInternalEvents() noexcept;

    CARLA_DECLARE_NON_COPY_STRUCT(EngineInternalEvents)
};

// -----------------------------------------------------------------------
// InternalTime

struct EngineInternalTime {
    bool playing;
    uint64_t frame;

    EngineInternalTime() noexcept;

    CARLA_DECLARE_NON_COPY_STRUCT(EngineInternalTime)
};

// -----------------------------------------------------------------------
// NextAction

enum EnginePostAction {
    kEnginePostActionNull,
    kEnginePostActionZeroCount,
    kEnginePostActionRemovePlugin,
    kEnginePostActionSwitchPlugins
};

struct EngineNextAction {
    EnginePostAction opcode;
    unsigned int pluginId;
    unsigned int value;
    CarlaMutex   mutex;

    EngineNextAction() noexcept;
    ~EngineNextAction() noexcept;
    void ready() noexcept;

    CARLA_DECLARE_NON_COPY_STRUCT(EngineNextAction)
};

// -----------------------------------------------------------------------
// EnginePluginData

struct EnginePluginData {
    CarlaPlugin* plugin;
    float insPeak[2];
    float outsPeak[2];

    void clear() noexcept;
};

// -----------------------------------------------------------------------
// CarlaEngineProtectedData

struct CarlaEngineProtectedData {
    CarlaEngineOsc    osc;
    CarlaEngineThread thread;

    const CarlaOscData* oscData;

    EngineCallbackFunc callback;
    void*              callbackPtr;

    FileCallbackFunc fileCallback;
    void*            fileCallbackPtr;

    unsigned int hints;
    uint32_t     bufferSize;
    double       sampleRate;

    bool         aboutToClose;    // don't re-activate thread if true
    unsigned int curPluginCount;  // number of plugins loaded (0...max)
    unsigned int maxPluginNumber; // number of plugins allowed (0, 16, 99 or 255)
    unsigned int nextPluginId;    // invalid if == maxPluginNumber

    CarlaString    lastError;
    CarlaString    name;
    EngineOptions  options;
    EngineTimeInfo timeInfo;

    EnginePluginData* plugins;

#ifndef BUILD_BRIDGE
    EngineInternalAudio  bufAudio;
#endif
    EngineInternalEvents bufEvents;
    EngineInternalTime   time;
    EngineNextAction     nextAction;

#ifndef BUILD_BRIDGE
    CarlaCriticalSection _cs; // for handling requests from multiple threads
#endif

    // -------------------------------------------------------------------

    CarlaEngineProtectedData(CarlaEngine* const engine);
    ~CarlaEngineProtectedData() noexcept;

    // -------------------------------------------------------------------

    void doPluginRemove() noexcept;
    void doPluginsSwitch() noexcept;
    void doNextPluginAction(const bool unlock) noexcept;

    // -------------------------------------------------------------------

#ifndef BUILD_BRIDGE
    // the base, where plugins run
    void processRack(float* inBufReal[2], float* outBuf[2], const uint32_t nframes, const bool isOffline);

    // extended, will call processRack() in the middle
    void processRackFull(float** const inBuf, const uint32_t inCount, float** const outBuf, const uint32_t outCount, const uint32_t nframes, const bool isOffline);
#endif

    // -------------------------------------------------------------------

    class ScopedActionLock
    {
    public:
        ScopedActionLock(CarlaEngineProtectedData* const data, const EnginePostAction action, const unsigned int pluginId, const unsigned int value, const bool lockWait) noexcept;
        ~ScopedActionLock() noexcept;

    private:
        CarlaEngineProtectedData* const fData;

        CARLA_PREVENT_HEAP_ALLOCATION
        CARLA_DECLARE_NON_COPY_CLASS(ScopedActionLock)
    };

    // -------------------------------------------------------------------

#ifdef CARLA_PROPER_CPP11_SUPPORT
    CarlaEngineProtectedData() = delete;
    CARLA_DECLARE_NON_COPY_STRUCT(CarlaEngineProtectedData)
#endif
};

// -----------------------------------------------------------------------

CARLA_BACKEND_END_NAMESPACE

#endif // CARLA_ENGINE_INTERNAL_HPP_INCLUDED
