#include <sqlite3.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>

#define BASE "https://jtvjan.nl/microblog/"

/*
CREATE TABLE posts (
	content text NOT NULL,
	media text,
	caption text,
	creation integer DEFAULT (strftime('%s', datetime('now'))),
	topic TEXT
	CHECK ((media IS NULL AND caption IS NULL) OR (media IS NOT NULL AND caption IS NOT NULL))
);
*/
enum {
	HTML,
	RSS
} mode;

void htmlprint(const char str[]);
void htmlescape(const char str[]);
void htmlescape_cats(const char str[]);
void attrprint(const char str[]);
const char *determinemime(const char file[]);

int main(int argc, char *argv[])
{
	time_t timenum;
	char timestr[32];
	sqlite3 *db;
	sqlite3_stmt *get_posts;

	mode = argc >= 2 && strcmp(argv[1], "-f") == 0 ? RSS : HTML;

	if (sqlite3_open_v2("database.sqlite3", &db, SQLITE_OPEN_READONLY, NULL) != SQLITE_OK) {
		printf("Failed to connect: %s\n", sqlite3_errmsg(db));
		return 2;
	}

	if (sqlite3_prepare_v2(db, "SELECT content, media, caption, creation, topic FROM posts ORDER BY creation DESC", -1, &get_posts, NULL) != SQLITE_OK) {
		printf("Failed to prepare statement: %s\n", sqlite3_errmsg(db));
		return 2;
	}

	if (mode == RSS)
		fputs(
			"<?xml version=\"1.0\"?>"
			"<rss version=\"2.0\">"
			"<channel>"
			"<title>My Microblog</title>"
			"<description>Like a regular blog, but with no effort.</description>"
			"<generator>"BASE"gen.c</generator>"
			"<link>"BASE"</link>",
			stdout
		);
	else if (mode == HTML)
		fputs(
			"<!DOCTYPE html>"
			"<html lang=\"en\">"
			"<head>"
			"<meta charset=\"utf-8\"/>"
			"<meta name=\"viewport\" content=\"width=device-width\"/>"
			"<meta name=\"generator\" content=\""BASE"gen.c\"/>"
			"<title>My Microblog</title>"
			"<link rel=\"stylesheet\" href=\"style.css\"/>"
			"<link rel=\"alternate\" type=\"application/rss+xml\" href=\"feed.rss\"/>"
			"</head>"
			"<body>"
			"<a href=\"feed.rss\" class=\"feedicon\"><img src=\"feed.png\" alt=\"RSS icon\" title=\"RSS icon from http://www.fatcow.com/free-icons\"/></a>"
			"<h1>Microblog</h1>",
			stdout
		);

	while (sqlite3_step(get_posts) == SQLITE_ROW) {
		timenum = sqlite3_column_int64(get_posts, 3);

		if (mode == RSS) {
			strftime(timestr, 32, "%a, %d %b %Y %H:%M:%S", gmtime(&timenum));
			fputs("<item><description>", stdout);
			htmlescape(sqlite3_column_text(get_posts, 0));
			printf("</description><pubDate>%s GMT</pubDate>", timestr);
			if (sqlite3_column_type(get_posts, 1) != SQLITE_NULL)
				printf("<enclosure url=\""BASE"images/%s\" type=\"image/%s\"/>", sqlite3_column_text(get_posts, 1), determinemime(sqlite3_column_text(get_posts, 1))); /* todo: length argument (size in bytes) */
			if (sqlite3_column_type(get_posts, 4) != SQLITE_NULL)
				htmlescape_cats(sqlite3_column_text(get_posts, 4));
			printf("<link>%s#p%ld</link></item>", BASE, timenum);
		} else if (mode == HTML) {
			strftime(timestr, 32, "%Y-%m-%dT%H:%M:%SZ", gmtime(&timenum));
			printf("<article id=\"p%ld\"><p>", timenum);
			fputs(sqlite3_column_text(get_posts, 0), stdout);
			if (sqlite3_column_type(get_posts, 1) != SQLITE_NULL) {
				printf("<a href=\"images/%s\"><img alt=\"", sqlite3_column_text(get_posts, 1));
				attrprint(sqlite3_column_text(get_posts, 2));
				printf("\" src=\"images/%s\"/></a>", sqlite3_column_text(get_posts, 1));
			}
			printf("<a href=\"#p%ld\"><time>%s</time></a>", timenum, timestr);
			if (sqlite3_column_type(get_posts, 4) != SQLITE_NULL)
				printf("<span class=\"topic\">%s</span>", sqlite3_column_text(get_posts, 4));
			fputs("</p></article>", stdout);
		}
	}
	
	sqlite3_finalize(get_posts);
	sqlite3_close(db);

	if (mode == RSS)
		fputs(
			"</channel>"
			"</rss>",
			stdout
		);
	else if (mode == HTML)
		fputs(
			"</body>"
			"</html>",
			stdout
		);

	return 0;
}

void htmlprint(const char str[])
{
	for (; *str != '\0'; ++str)
		putchar(*str);
}

void htmlescape(const char str[])
{
	for (; *str != '\0'; ++str)
		if (*str == '<')
			fputs("&lt;", stdout);
		else if (*str == '>')
			fputs("&gt;", stdout);
		else if (*str == '&')
			fputs("&amp;", stdout);
		else
			putchar(*str);
}

void htmlescape_cats(const char str[])
{
	fputs("<category>", stdout);

	for (; *str != '\0'; ++str)
		if (*str == '<')
			fputs("&lt;", stdout);
		else if (*str == '>')
			fputs("&gt;", stdout);
		else if (*str == '&')
			fputs("&amp;", stdout);
		else if (*str == ' ')
			fputs("</category><category>", stdout);
		else
			putchar(*str);

	fputs("</category>", stdout);
}

void attrprint(const char str[])
{
	for (; *str != '\0'; ++str)
		if (*str == '"')
			fputs("&quot;", stdout);
		else
			putchar(*str);
}

const char *determinemime(const char file[])
{
	const char *extensions[] = {
		"png",
		"jpeg",
		"gif",
		"webp"
	};
	
	if (strcmp(strrchr(file, '.'), ".png") == 0)
		return extensions[0];
	else if (strcmp(strrchr(file, '.'), ".jpg") == 0)
		return extensions[1];
	else if (strcmp(strrchr(file, '.'), ".jpeg") == 0)
		return extensions[1];
	else if (strcmp(strrchr(file, '.'), ".gif") == 0)
		return extensions[2];
	else if (strcmp(strrchr(file, '.'), ".webp") == 0)
		return extensions[3];
	else return NULL;
}
