/*
 * Carla JACK API for external applications
 * Copyright (C) 2016-2017 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.
 */

// need to include this first
#include "CarlaDefines.h"

// now define as bridge
#define BUILD_BRIDGE 1

// now include a bunch of stuff
#include "CarlaBackendUtils.hpp"
#include "CarlaBridgeUtils.hpp"
#include "CarlaMIDI.h"
#include "CarlaMutex.hpp"
#include "LinkedList.hpp"

#if 0
#include <jack/jack.h>
#include <jack/midiport.h>
#include <jack/transport.h>
#include <jack/session.h>
#include <jack/metadata.h>
#endif

#ifdef __SSE2_MATH__
# include <xmmintrin.h>
#endif

// must be last include
#include "jackbridge/JackBridge.hpp"

// small check to not hurt myself
#ifdef JACKBRIDGE_DIRECT
# error "Cannot create custom jack server while linking to libjack directly"
#endif

CARLA_BACKEND_START_NAMESPACE

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

class CarlaJackAppClient;
struct JackClientState;
struct JackServerState;

struct JackMidiPortBuffer {
    static const uint8_t kMaxEventSize   = 128;
    static const size_t  kMaxEventCount  = 512;
    static const size_t  kBufferPoolSize = kMaxEventCount*8;

    uint16_t count;
    bool isInput;
    jack_midi_event_t* events;

    size_t bufferPoolPos;
    jack_midi_data_t* bufferPool;

    JackMidiPortBuffer()
        : count(0),
          isInput(true),
          events(new jack_midi_event_t[kMaxEventCount]),
          bufferPoolPos(0),
          bufferPool(new jack_midi_data_t[kBufferPoolSize]) {}

    // for unused ports
    JackMidiPortBuffer(const bool input, const char*)
        : count(0),
          isInput(input),
          events(nullptr),
          bufferPoolPos(kBufferPoolSize),
          bufferPool(nullptr) {}

    ~JackMidiPortBuffer()
    {
        delete[] events;
        delete[] bufferPool;
    }

    CARLA_DECLARE_NON_COPY_STRUCT(JackMidiPortBuffer)
};

struct JackPortState {
    char* name;
    char* fullname;
    void* buffer;
    uint index;
    uint flags;
    bool isMidi : 1;
    bool isSystem : 1;
    bool isConnected : 1;
    bool unused : 1;

    JackPortState()
        : name(nullptr),
          fullname(nullptr),
          buffer(nullptr),
          index(0),
          flags(0),
          isMidi(false),
          isSystem(false),
          isConnected(false),
          unused(false) {}

    JackPortState(const char* const cn, const char* const pn, const uint i, const uint f,
                  const bool midi, const bool sys, const bool con)
        : name(strdup(pn)),
          fullname(nullptr),
          buffer(nullptr),
          index(i),
          flags(f),
          isMidi(midi),
          isSystem(sys),
          isConnected(con),
          unused(false)
    {
        char strBuf[STR_MAX+1];
        snprintf(strBuf, STR_MAX, "%s:%s", cn, pn);
        strBuf[STR_MAX] = '\0';

        fullname = strdup(strBuf);
    }

    ~JackPortState()
    {
        free(name);
        free(fullname);
    }

    CARLA_DECLARE_NON_COPY_STRUCT(JackPortState)
};

struct JackClientState {
    const JackServerState& server;
    CarlaMutex mutex;

    bool activated;
    bool deactivated; // activated once, then deactivated

    char* name;

    LinkedList<JackPortState*> audioIns;
    LinkedList<JackPortState*> audioOuts;
    LinkedList<JackPortState*> midiIns;
    LinkedList<JackPortState*> midiOuts;

    JackShutdownCallback shutdownCb;
    void* shutdownCbPtr;

    JackInfoShutdownCallback infoShutdownCb;
    void* infoShutdownCbPtr;

    JackProcessCallback processCb;
    void* processCbPtr;

    JackFreewheelCallback freewheelCb;
    void* freewheelCbPtr;

    JackBufferSizeCallback bufferSizeCb;
    void* bufferSizeCbPtr;

    JackSampleRateCallback sampleRateCb;
    void* sampleRateCbPtr;

    JackSyncCallback syncCb;
    void* syncCbPtr;

    JackClientState(const JackServerState& s, const char* const n)
        : server(s),
          mutex(),
          activated(false),
          deactivated(false),
          name(strdup(n)),
          audioIns(),
          audioOuts(),
          midiIns(),
          midiOuts(),
          shutdownCb(nullptr),
          shutdownCbPtr(nullptr),
          infoShutdownCb(nullptr),
          infoShutdownCbPtr(nullptr),
          processCb(nullptr),
          processCbPtr(nullptr),
          freewheelCb(nullptr),
          freewheelCbPtr(nullptr),
          bufferSizeCb(nullptr),
          bufferSizeCbPtr(nullptr),
          sampleRateCb(nullptr),
          sampleRateCbPtr(nullptr),
          syncCb(nullptr),
          syncCbPtr(nullptr) {}

    ~JackClientState()
    {
        const CarlaMutexLocker cms(mutex);

        for (LinkedList<JackPortState*>::Itenerator it = audioIns.begin2(); it.valid(); it.next())
        {
            if (JackPortState* const jport = it.getValue(nullptr))
                delete jport;
        }

        for (LinkedList<JackPortState*>::Itenerator it = audioOuts.begin2(); it.valid(); it.next())
        {
            if (JackPortState* const jport = it.getValue(nullptr))
                delete jport;
        }

        for (LinkedList<JackPortState*>::Itenerator it = midiIns.begin2(); it.valid(); it.next())
        {
            if (JackPortState* const jport = it.getValue(nullptr))
                delete jport;
        }

        for (LinkedList<JackPortState*>::Itenerator it = midiOuts.begin2(); it.valid(); it.next())
        {
            if (JackPortState* const jport = it.getValue(nullptr))
                delete jport;
        }

        free(name);
        name = nullptr;

        audioIns.clear();
        audioOuts.clear();
    }

    CARLA_DECLARE_NON_COPY_STRUCT(JackClientState)
};

struct JackServerState {
    CarlaJackAppClient* jackAppPtr;

    uint32_t bufferSize;
    double   sampleRate;

    uint8_t numAudioIns;
    uint8_t numAudioOuts;
    uint8_t numMidiIns;
    uint8_t numMidiOuts;

    bool playing;
    jack_position_t position;

    JackServerState(CarlaJackAppClient* const app)
        : jackAppPtr(app),
          bufferSize(0),
          sampleRate(0.0),
          numAudioIns(0),
          numAudioOuts(0),
          numMidiIns(0),
          numMidiOuts(0),
          playing(false),
          position()
    {
        carla_zeroStruct(position);
    }

    CARLA_DECLARE_NON_COPY_STRUCT(JackServerState)
};

CARLA_BACKEND_END_NAMESPACE

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