#ifndef BridgePluginHost_config_h
#define BridgePluginHost_config_h

#ifdef _MSC_VER
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <Windows.h>
#else
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <unistd.h>
#endif

#include <string>
#include <stdexcept>

constexpr const char* scannerProcessUID = "wavtoolbridgepluginhostscanner";
constexpr const char* workerProcessUID = "wavtoolbridgeworker";

enum PluginWorkerOp {
    LOAD = 0,
    LOADED,
    LOAD_FAILED,
    FAILED,
    UNBLOCK
};

enum ShmMessageType {
    PROCESS_BLOCK = 0,
    SET_TRANSPORT = 1
};

constexpr const size_t MAX_BUFFER_SIZE = 4096;
constexpr const size_t MAX_MIDI_EVENTS = 2048; // VST SDK max number of events per buffer is 2048
constexpr const size_t MAX_CHANNEL_PAIRS = 4;
constexpr const size_t MAX_MIDI_EVENT_LENGTH = 5;
constexpr const size_t MAX_PARAMETER_AUTOMATIONS = 256;

struct ParameterDescription {
    int index;
    std::string name;
};

struct KeyDescription {
    bool keyDown;
    std::string keyDescription;
};

struct ShmChannelPair {
    float samples[2][MAX_BUFFER_SIZE];
};

struct ShmMidiEvent {
    uint32_t sampleIndex;
    uint8_t length;
    uint8_t data[MAX_MIDI_EVENT_LENGTH];
};

struct ShmParameterAutomation {
    uint32_t parameterIndex;
    float values[MAX_BUFFER_SIZE];
};

struct ShmProcessBlockData {
    uint32_t frameId;
    uint32_t numInputs;
    uint32_t numOutputs;
    uint32_t numSamplesPerChannel;
    uint32_t numMidiEvents;
    uint32_t numParameterAutomations;
    uint32_t parameterSampleStride;
    ShmChannelPair channelss[MAX_CHANNEL_PAIRS];
    ShmMidiEvent midiEvents[MAX_MIDI_EVENTS];
    ShmParameterAutomation automations[MAX_PARAMETER_AUTOMATIONS];
};

struct ShmSetTransportData {
    float bpm;
    int beatNumerator;
    int beatDenominator;
    int64_t samplePosition;
    int64_t sampleLoopStart;
    int64_t sampleLoopEnd;
    bool playing;
    bool recording;
    bool looping;
    
    int samplesLatency;
};

struct ShmData {
    ShmMessageType messageType;
    bool shouldReset;
    union {
        ShmProcessBlockData processBlock;
        ShmSetTransportData setTransport;
    };
};

template<typename T>
inline std::pair<std::string, int> getPeerAddressAndPort(T* ws) {
    auto fd = (uintptr_t)ws->getNativeHandle();

    struct sockaddr_storage addr;
    socklen_t len = sizeof(addr);
    if(getpeername((int)fd, (struct sockaddr*)&addr, &len)) {
        throw std::runtime_error("getpeername failed");
    }
    int port = 0;
    std::string addrStr;
    if (addr.ss_family == AF_INET) {
        struct sockaddr_in *s = (struct sockaddr_in *)&addr;
        port = ntohs(s->sin_port);
        char buf[INET_ADDRSTRLEN] = {0};
        inet_ntop(AF_INET, &(s->sin_addr), buf, INET_ADDRSTRLEN);
        addrStr = std::string(buf);
    } else if (addr.ss_family == AF_INET6) {
        struct sockaddr_in6 *s = (struct sockaddr_in6 *)&addr;
        port = ntohs(s->sin6_port);
        char buf[INET6_ADDRSTRLEN] = {0};
        inet_ntop(AF_INET6, &(s->sin6_addr), buf, INET6_ADDRSTRLEN);
        addrStr = std::string(buf);
    } else {
        throw std::runtime_error("unknown socket family");
    }
    return std::make_pair(addrStr, port);
}

template<typename T>
bool validateOriginHeader(T * req) {
    auto origin = req->getHeader("origin");
    return origin == "https://app.wavtool.com"
        || origin == "https://appbeta.wavtool.com"
        || origin == "https://appstaging.wavtool.com"
        || origin == "http://localhost:3002"
        || origin == "https://localhost:3002";
}

template<typename T>
T readFromBuf(const void* buf) {
    T res;
    memcpy(&res, buf, sizeof(T));
    return res;
}

#ifdef _MSC_VER
using platform_thread_t = DWORD;

inline platform_thread_t platformGetCurrentThread() {
    return GetCurrentThreadId();
}
#else
using platform_thread_t = pthread_t;

inline platform_thread_t platformGetCurrentThread() {
    return pthread_self();
}
#endif

#endif /* BridgePluginHost_config_h */
