/**
 * @file WSServer.h
 * @brief Multi-client WebSocket server using MARTe2 BasicTCPSocket.
 *
 * Accepts WebSocket connections over HTTP/1.1 (RFC 6455 upgrade).
 * - One accept thread (started by Start()).
 * - One read thread per connected client (started when client upgrades).
 * - BroadcastText / BroadcastBinary called from any thread (push loop or
 *   command handlers); per-client write is protected by a mutex.
 *
 * Maximum concurrent clients: WS_MAX_CLIENTS (16).
 */

#ifndef STREAMHUB_WSSERVER_H_
#define STREAMHUB_WSSERVER_H_

#include "BasicTCPSocket.h"
#include "FastPollingMutexSem.h"

namespace StreamHub {

using MARTe::uint8;
using MARTe::uint16;
using MARTe::uint32;
using MARTe::BasicTCPSocket;
using MARTe::FastPollingMutexSem;

/** Maximum simultaneously connected WebSocket clients. */
static const uint32 WS_MAX_CLIENTS = 16u;

/** Maximum WebSocket frame payload we will receive (commands are JSON, small). */
static const uint32 WS_MAX_RECV_PAYLOAD = 65536u;

/** Maximum WebSocket frame payload we will send (data frames can be large). */
static const uint32 WS_MAX_SEND_PAYLOAD = 4u * 1024u * 1024u; /* 4 MiB */

/**
 * @brief Callback interface — implemented by StreamHub.
 */
class WSCommandCallback {
public:
    virtual ~WSCommandCallback() {}
    /** Called from client read thread with a fully-received text frame.
     *  @param slotIdx Client slot index — usable with WSServer::SendText for
     *  unicast replies. */
    virtual void OnWSCommand(const char *json, uint32 len, uint32 slotIdx) = 0;
    /** Called from accept thread when a new client upgrades. */
    virtual void OnWSClientConnected() = 0;
    /** Called from client read thread when the connection closes. */
    virtual void OnWSClientDisconnected() = 0;
};

/**
 * @brief One connected WebSocket client slot.
 */
struct WSClientSlot {
    BasicTCPSocket      *sock;          ///< Owned pointer; NULL when slot is free
    bool                 active;
    MARTe::ThreadIdentifier readTid;
    FastPollingMutexSem  writeMutex;    ///< Serialises writes from push + pong threads
};

/**
 * @brief Minimal HTTP/1.1 + WebSocket server.
 */
class WSServer {
public:

    WSServer();
    ~WSServer();

    /**
     * @brief Open listen socket and start the accept thread.
     * @param port  TCP port to listen on.
     * @param cb    Command callback (StreamHub); must outlive WSServer.
     * @return true on success.
     */
    bool Start(uint16 port, WSCommandCallback *cb);

    /**
     * @brief Stop accept thread; close all client connections; close listener.
     */
    bool Stop();

    /**
     * @brief Broadcast a text (JSON) frame to all connected clients.
     * Safe to call from any thread.
     */
    void BroadcastText(const char *json, uint32 len);

    /**
     * @brief Broadcast a binary frame to all connected clients.
     * Safe to call from any thread.
     */
    void BroadcastBinary(const uint8 *buf, uint32 len);

    /**
     * @brief Send a text frame to a single client slot.
     * Used internally to send pong / direct responses.
     */
    void SendText(uint32 slotIdx, const char *json, uint32 len);

    /** @return Number of currently active client connections. */
    uint32 ClientCount() const;

    /* ---- Internal thread entry points (public for Threads trampoline) ---- */
    void AcceptLoop();
    void ClientReadLoop(uint32 slotIdx);

private:

    bool UpgradeHTTP(BasicTCPSocket *sock);
    bool SendFrame(WSClientSlot &slot, uint8 opcode,
                   const uint8 *payload, uint32 payloadLen);

    uint32 AllocSlot(BasicTCPSocket *sock);
    void   FreeSlot(uint32 idx);

    BasicTCPSocket      tcpListener;
    WSClientSlot        clients[WS_MAX_CLIENTS];
    uint32              numClients;
    mutable FastPollingMutexSem clientsMutex;   ///< Protects numClients and clients[] array

    WSCommandCallback  *callback;
    volatile bool       running;

    MARTe::ThreadIdentifier acceptTid;
};

} /* namespace StreamHub */

#endif /* STREAMHUB_WSSERVER_H_ */
