#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>

#include "htparse.h"

static int
_on_msg_start(htparser * p) {
    printf("START {\n");
    return 0;
}

static int
_on_msg_end(htparser * p) {
    printf("}\n");
    return 0;
}

static int
_path(htparser * p, const char * data, size_t len) {
    printf("\tpath = '%.*s'\n", (int)len, data);
    return 0;
}

static int
_method(htparser * p, const char * data, size_t len) {
    printf("\tmethod = '%.*s'\n", (int)len, data);
    return 0;
}

static int
_uri(htparser * p, const char * data, size_t len) {
    printf("\turi = '%.*s'\n", (int)len, data);
    return 0;
}

static int
_args(htparser * p, const char * data, size_t len) {
    printf("\targs = '%.*s'\n", (int)len, data);
    return 0;
}

static int
_hdrs_end(htparser * p) {
    printf("\t}\n");
    return 0;
}

static int
_hdrs_start(htparser * p) {
    printf("\thdrs {\n");
    return 0;
}

static int
_hdr_key(htparser * p, const char * data, size_t len) {
    printf("\t\thdr_key = '%.*s'\n", (int)len, data);
    return 0;
}

static int
_hdr_val(htparser * p, const char * data, size_t len) {
    printf("\t\thdr_val = '%.*s'\n", (int)len, data);
    return 0;
}

static int
_read_body(htparser * p, const char * data, size_t len) {
    printf("\t'%.*s'\n", (int)len, data);
    return 0;
}

static int
_on_new_chunk(htparser * p) {
    printf("\t--chunk payload (%zu)--\n", htparser_get_content_length(p));
    /* printf("..chunk..\n"); */
    return 0;
}

static void
_test(htparser * p, htparse_hooks * hooks, const char * l, htp_type type) {
    printf("---- test ----\n");
    printf("%zu, %s\n", strlen(l), l);

    htparser_init(p, type);
    printf("%zu == %zu\n", htparser_run(p, hooks, l, strlen(l)), strlen(l));

    if (htparser_get_error(p)) {
        printf("ERROR: %s\n", htparser_get_strerror(p));
    }

    printf("\n");
}

static void
_test_fragments(htparser * p, htparse_hooks * hooks, const char ** fragments,
                htp_type type) {
    int i = 0;

    printf("---- test fragment ----\n");
    htparser_init(p, type);

    while (1) {
        const char * l = fragments[i++];

        if (l == NULL) {
            break;
        }

        htparser_run(p, hooks, l, strlen(l));

        if (htparser_get_error(p)) {
            printf("ERROR: %s\n", htparser_get_strerror(p));
        }
    }

    printf("\n");
}

static const char * test_fragment_1[] = {
    "GET \0",
    "  /fjdksf\0",
    "jfkdslfds H\0",
    "TTP/1.\0",
    "1\r\0",
    "\n\0",
    "\r\0",
    "\n\0",
    NULL
};

static const char * test_fragment_2[] = {
    "POST /\0",
    "h?a=b HTTP/1.0\r\n\0",
    "Content-Len\0",
    "gth\0",
    ": 1\0",
    "0\r\n\0",
    "\r\n\0",
    "12345\0",
    "67890\0",
    NULL
};

static const char * test_chunk_fragment_1[] = {
    "POST /stupid HTTP/1.1\r\n",
    "Transfer-Encoding: chunked\r\n",
    "\r\n",
    "25\r\n",
    "This is the data in the first chunk\r\n",
    "\r\n",
    "1C\r\n",
    "and this is the second one\r\n",
    "\r\n",
    "3\r\n",
    "con\r\n",
    "8\r\n",
    "sequence\r\n",
    "0\r\n",
    "\r\n",
    NULL
};

static const char * test_chunk_fragment_2[] = {
    "POST /stupid HTTP/1.1\r\n",
    "Transfer-Encoding: chunked\r\n",
    "\r\n",
    "25\r\n",
    "This is the data in the first chunk\r\n",
    "\r\n",
    "1C\r\n",
    "and this is the second one\r\n",
    "\r\n",
    "3\r\n",
    "c",
    "on\r\n",
    "8\r\n",
    "sequence\r\n",
    "0\r\n",
    "\r\n",
    "GET /foo?bar/baz? HTTP/1.0\r\n",
    "Host: stupid.com\r\n",
    "\r\n",
    NULL
};
int
main(int argc, char ** argv) {
    htparser    * p     = htparser_new();
    htparse_hooks hooks = {
        .on_msg_begin       = _on_msg_start,
        .method             = _method,
        .scheme             = NULL,
        .host               = NULL,
        .port               = NULL,
        .path               = _path,
        .args               = _args,
        .uri                = _uri,
        .on_hdrs_begin      = _hdrs_start,
        .hdr_key            = _hdr_key,
        .hdr_val            = _hdr_val,
        .on_hdrs_complete   = _hdrs_end,
        .on_new_chunk       = _on_new_chunk,
        .on_chunk_complete  = NULL,
        .on_chunks_complete = NULL,
        .body               = _read_body,
        .on_msg_complete    = _on_msg_end
    };

    const char  * test_1 = "GET / HTTP/1.0\r\n\r\n";
    const char  * test_2 = "GET /hi?a=b&c=d HTTP/1.1\r\n\r\n";
    const char  * test_3 = "GET /hi/die/?a=b&c=d HTTP/1.1\r\n\r\n";
    const char  * test_4 = "POST /fjdls HTTP/1.0\r\n"
                           "Content-Length: 4\r\n"
                           "\r\n"
                           "abcd";
    const char * test_7 = "POST /derp HTTP/1.1\r\n"
                          "Transfer-Encoding: chunked\r\n\r\n"
                          "1e\r\nall your base are belong to us\r\n"
                          "0\r\n"
                          "\r\n\0";
    const char * test_8 = "GET /DIE HTTP/1.1\r\n"
                          "HERP: DE\r\n"
                          "\tRP\r\nthings:stuff\r\n\r\n";
    const char * test_9 = "GET /big_content_len HTTP/1.1\r\n"
                          "Content-Length: 18446744073709551615\r\n\r\n";

    const char * test_fail   = "GET /JF HfD]\r\n\r\n";
    const char * test_resp_1 = "HTTP/1.0 200 OK\r\n"
                               "Stuff: junk\r\n\r\n";

    _test(p, &hooks, test_resp_1, htp_type_response);
    _test(p, &hooks, test_1, htp_type_request);
    _test(p, &hooks, test_2, htp_type_request);
    _test(p, &hooks, test_3, htp_type_request);
    _test(p, &hooks, test_4, htp_type_request);
    _test(p, &hooks, test_7, htp_type_request);
    _test(p, &hooks, test_8, htp_type_request);
    _test(p, &hooks, test_9, htp_type_request);
    _test(p, &hooks, test_fail, htp_type_request);

    _test_fragments(p, &hooks, test_fragment_1, htp_type_request);
    _test_fragments(p, &hooks, test_fragment_2, htp_type_request);
    _test_fragments(p, &hooks, test_chunk_fragment_1, htp_type_request);
    _test_fragments(p, &hooks, test_chunk_fragment_2, htp_type_request);

    return 0;
} /* main */