#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

#include "gmni_parser.h"
#include "url.h"


/* Wrapper around read to plug in to gemini_parser_init */
static int read_stdin(void *dummy, void *buf, size_t nbyte)
{
    return read(0, buf, nbyte);
}

static char *trim_ws(char *in)
{
    while (*in && isspace(*in)) ++in;
    return in;
}

/* Escape '<','>','&', suitable for use in a text element.
 * Writes at most n bytes. Always terminates with a null byte. */
static void html_escape(char *out, const char *in, int n)
{
    int i = 0;

    while (*in && i+6 < n) {
        if (*in == '<') {
            strcpy(out+i, "&lt;");
            i += 4;
        } else if (*in == '>') {
            strcpy(out+i, "&gt;");
            i += 4;
        } else if (*in == '&') {
            strcpy(out+i, "&amp;");
            i += 5;
        } else {
            *(out+i) = *in;
            ++i;
        }
        ++in;
    }

    *(out+i) = 0;
}

int main(int argc, char **argv)
{
    struct Curl_URL *base_url = curl_url();
    if (argc < 2) {
        fprintf(stderr, "Usage: %s URL\n", argv[0]);
        exit(1);
    }

    if (curl_url_set(base_url, CURLUPART_URL, argv[1], 0) != CURLUE_OK) {
        fprintf(stderr, "Bad URL\n");
        exit(1);
    }

    printf("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
            "<feed xmlns=\"http://www.w3.org/2005/Atom\">\n\n");

    struct gemini_parser p;
    gemini_parser_init(&p, &read_stdin, NULL);
    struct gemini_token tok;

    char title[256] = "";
    char link_title[256];

    unsigned int updated_year = 0, updated_month = 0, updated_day = 0;

    while (gemini_parser_next(&p, &tok) == 0) {
        switch (tok.token) {
            case GEMINI_HEADING:
                if (*title == 0 && tok.heading.level == 1) {
                    html_escape(title, trim_ws(tok.heading.title), sizeof(title));
                }
                break;
            case GEMINI_LINK:
                {
                    if (!tok.link.text) {
                        break;
                    }

                    const char *text = trim_ws(tok.link.text);
                    unsigned int year, month, day;

                    if (strlen(text) >= 10 &&
                            text[4] == '-' && text[7] == '-' &&
                            sscanf(text, "%4u-%2u-%2u", &year, &month, &day) == 3 &&
                            month > 0 && month < 13 && day > 0 && day < 32) {
                        const char* desc = text + 10;
                        while (isspace(*desc) || *desc == '-' || *desc == ':') ++desc;

                        struct Curl_URL *link_url = curl_url_dup(base_url);
                        if (curl_url_set(link_url, CURLUPART_URL, tok.link.url, 0) != CURLUE_OK) {
                            break;
                        }

                        html_escape(link_title, desc, sizeof(link_title));

                        char *u;
                        curl_url_get(link_url, CURLUPART_URL, &u, 0);

                        printf("  <entry>\n"
                                "    <title>%s</title>\n"
                                "    <link rel=\"alternate\" href=\"%s\"/>\n"
                                "    <id>%s</id>\n"
                                "    <updated>%.10sT12:00:00Z</updated>\n"
                                "  </entry>\n\n"
                                , link_title, u, u, text);

                        free(u);
                        curl_url_cleanup(link_url);

                        if (year > updated_year ||
                                (year == updated_year &&
                                 (month > updated_month ||
                                  (month == updated_month &&
                                   day >= updated_day)))) {
                            updated_year = year;
                            updated_month = month;
                            updated_day = day;
                        }
                    }
                }
                break;
            default:
                break;
        }
        gemini_token_finish(&tok);
    }

    gemini_token_finish(&tok);
    gemini_parser_finish(&p);

    char iso8601buf[4+1+2+1+2+1+2+1+2+1+2+1+1]; // YYYY-MM-DDTHH:MM:SSZ
    if (updated_year == 0) {
        time_t now;
        time(&now);
        strftime(iso8601buf, sizeof(iso8601buf), "%Y-%m-%dT%H:%M:%SZ", gmtime(&now));
    } else {
        snprintf(iso8601buf, sizeof(iso8601buf), "%04u-%02u-%02uT12:00:00Z",
                updated_year, updated_month, updated_day);
    }

    if (*title == 0) {
        strcpy(title, "Untitled");
    }

    printf("  <title>%s</title>\n"
            "  <link href=\"%s\"/>\n"
            "  <updated>%s</updated>\n"
            "  <id>%s</id>\n\n"
            "</feed>\n", title, argv[1], iso8601buf, argv[1]);

    curl_url_cleanup(base_url);
}
