#ifndef WS_H
#define WS_H

#include "http.h"

#include <stdbool.h>
#include <stdint.h>

#define WSD_READ_BUF_SIZE  8192
#define WSD_MAX_FRAME_SIZE (1 << 20) /* 1 MiB max payload */

/* WebSocket opcodes (RFC 6455 §5.2) */
enum ws_opcode : uint64_t {
	WS_OP_CONTINUATION = 0x0,
	WS_OP_TEXT         = 0x1,
	WS_OP_BINARY       = 0x2,
	/* 0x3–0x7 reserved for further non-control frames */
	WS_OP_CLOSE        = 0x8,
	WS_OP_PING         = 0x9,
	WS_OP_PONG         = 0xA,
};

/* Session state */
enum ws_state : uint64_t {
	WS_STATE_OPEN,       /* WebSocket connection established */
	WS_STATE_CLOSING,    /* Close handshake in progress */
	WS_STATE_CLOSED,     /* Connection closed */
};

/* WebSocket close status codes (RFC 6455 §7.4.1) */
enum ws_close_code {
	WS_CLOSE_NORMAL       = 1000,
	WS_CLOSE_GOING_AWAY   = 1001,
	WS_CLOSE_PROTOCOL_ERR = 1002,
	WS_CLOSE_UNSUPPORTED  = 1003,
	WS_CLOSE_NO_STATUS    = 1005,
	WS_CLOSE_ABNORMAL     = 1006,
	WS_CLOSE_INVALID_DATA = 1007,
	WS_CLOSE_TOO_BIG      = 1009,
};

/* Parsed WebSocket frame */
struct ws_frame {
	bool     fin;
	uint8_t  opcode;
	bool     masked;
	uint8_t  mask_key[4];
	uint64_t payload_len;
	uint8_t *payload;  /* heap-allocated, owned by the frame */
};

/* Forward declaration */
struct ws_session;

/* TCP client transport layer */
struct ws_client {
	int                fd;
	uint8_t           *buf;
	struct ws_session *session;  /* WebSocket session (set after handshake) */
};

/* WebSocket session */
struct ws_session {
	enum ws_state     state;

	/* Fragmentation state */
	enum ws_opcode    frag_opcode; /* the opcode of the initial fragment */
	size_t            frag_len;    /* current length of the fragmented message */
	uint8_t          *frag_buf;    /* accumulated payload for fragments */
};

/* Creates a new WebSocket client. */
struct ws_client *ws_client_create(int fd);

/* Frees a client and its session. */
void ws_client_destroy(struct ws_client *client);

/* Handles the WebSocket handshake. Returns 0 on success, -1 on failure. */
int ws_handshake(struct ws_client *client,
		 const struct http_request *req);

/* Processes incoming data on a session. Returns 0 to keep going, -1 to close. */
int ws_on_data(struct ws_client *client, size_t len);

/* Sends a WebSocket frame. */
int ws_send_frame(struct ws_client *client, uint8_t opcode,
		  const void *payload, size_t len);

/* Sends a close frame with a status code. */
int ws_send_close(struct ws_client *client, uint16_t code);

/* Frees a parsed frame. */
void ws_frame_free(struct ws_frame *frame);

#endif /* WS_H */
