#include "WebSocketParser.h"

#include "websocket_parser.h"
#include "hdef.h"

#define MAX_PAYLOAD_LENGTH  (1 << 24)   // 16M

static int on_frame_header(websocket_parser* parser) {
    WebSocketParser* wp = (WebSocketParser*)parser->data;
    int opcode = parser->flags & WS_OP_MASK;
    // printf("on_frame_header opcode=%d\n", opcode);
    if (opcode != WS_OP_CONTINUE) {
        wp->opcode = opcode;
    }
    int length = parser->length;
    int reserve_length = MIN(length + 1, MAX_PAYLOAD_LENGTH);
    if (reserve_length > wp->message.capacity()) {
        wp->message.reserve(reserve_length);
    }
    if (wp->state == WS_FRAME_BEGIN ||
        wp->state == WS_FRAME_FIN) {
        wp->message.clear();
    }
    wp->state = WS_FRAME_HEADER;
    return 0;
}

static int on_frame_body(websocket_parser* parser, const char * at, size_t length) {
    // printf("on_frame_body length=%d\n", (int)length);
    WebSocketParser* wp = (WebSocketParser*)parser->data;
    wp->state = WS_FRAME_BODY;
    if (wp->parser->flags & WS_HAS_MASK) {
        websocket_parser_decode((char*)at, at, length, wp->parser);
    }
    wp->message.append(at, length);
    return 0;
}

static int on_frame_end(websocket_parser* parser) {
    // printf("on_frame_end\n");
    WebSocketParser* wp = (WebSocketParser*)parser->data;
    wp->state = WS_FRAME_END;
    if (wp->parser->flags & WS_FIN) {
        wp->state = WS_FRAME_FIN;
        if (wp->onMessage) {
            wp->onMessage(wp->opcode, wp->message);
        }
    }
    return 0;
}

static websocket_parser_settings cbs = {
    on_frame_header,
    on_frame_body,
    on_frame_end
};

WebSocketParser::WebSocketParser() {
    parser = (websocket_parser*)malloc(sizeof(websocket_parser));
    websocket_parser_init(parser);
    parser->data = this;
    state = WS_FRAME_BEGIN;
    opcode = WS_OP_CLOSE;
}

WebSocketParser::~WebSocketParser() {
    if (parser) {
        free(parser);
        parser = NULL;
    }
}

int WebSocketParser::FeedRecvData(const char* data, size_t len) {
    return websocket_parser_execute(parser, &cbs, data, len);
}
