#![feature(proc_macro_hygiene, decl_macro)]

pub mod admin;
pub mod controllers;
pub mod misc;
pub mod models;
mod schema;

#[macro_use]
extern crate rocket;
#[macro_use]
extern crate diesel;
extern crate comrak;
extern crate dotenv;
extern crate rocket_contrib;
extern crate tera;

use comrak::{markdown_to_html, ComrakOptions};
use controllers::posts;
use dotenv::dotenv;
use misc::get_context;
use rocket::response::content;
use rocket::response::NamedFile;
use rocket::Request;
use rocket_contrib::serve::StaticFiles;
use rocket_contrib::templates::Template;
use std::path::Path;
use std::vec::Vec;

#[get("/?<page>")]
fn index(page: Option<u64>) -> Template {
    let page: u64 = page.unwrap_or(1);

    let mut context = get_context();

    let (posts, n_posts) = posts::get_posts(Some(page));

    let total_pages = (n_posts as f64 / posts::MAX_POSTS_PER_PAGE as f64).ceil() as i64;

    context.insert("posts", &posts);
    context.insert("total_pages", &total_pages);
    context.insert("actual_page", &page);
    Template::render("index", context)
}

#[get("/feed.xml")]
fn rss_feed() -> content::Xml<Template> {
    let mut context = get_context();
    let (mut posts, _) = posts::get_posts(None);

    let comrak_options = ComrakOptions {
        ext_table: true,
        ext_autolink: true,
        ext_tasklist: true,
        unsafe_: true,
        ..ComrakOptions::default()
    };

    posts.iter_mut().for_each(|post| {
        let content = markdown_to_html(&post.content, &comrak_options);
        post.content = content;
    });

    context.insert("posts", &posts);

    content::Xml(Template::render("rss", context))
}

#[get("/atom.xml")]
fn atom_feed() -> content::Xml<Template> {
    let mut context = get_context();
    let (mut posts, _) = posts::get_posts(None);

    let comrak_options = ComrakOptions {
        ext_table: true,
        ext_autolink: true,
        ext_tasklist: true,
        unsafe_: true,
        ..ComrakOptions::default()
    };

    posts.iter_mut().for_each(|post| {
        let content = markdown_to_html(&post.content, &comrak_options);
        post.content = content;
    });

    context.insert("posts", &posts);

    content::Xml(Template::render("atom", context))
}

#[get("/post/<title>")]
fn show_post(title: String) -> Template {
    let mut context = get_context();

    let title_splited: Vec<&str> = title.split('-').collect();
    let id = title_splited.last().unwrap().parse().unwrap_or(-1);

    match posts::get_post(id) {
        Ok(mut post) => {
            let comrak_options = ComrakOptions {
                ext_table: true,
                ext_autolink: true,
                ext_tasklist: true,
                unsafe_: true,
                ..ComrakOptions::default()
            };

            let content = markdown_to_html(&post.content, &comrak_options);
            post.content = content;

            context.insert("post", &post);
            Template::render("post", context)
        }
        Err(_) => {
            let uri = format!("/post/{}", title_splited.join("-"));
            context.insert("url", &uri);
            Template::render("404", context)
        }
    }
}

#[catch(404)]
fn not_found_404(req: &Request) -> Template {
    let mut context = get_context();

    let uri = format!("{}", req.uri());

    context.insert("url", &uri);
    Template::render("404", context)
}

#[get("/favicon.ico")]
fn favicon() -> Option<NamedFile> {
    NamedFile::open(Path::new("static/favicon.ico")).ok()
}

fn main() {
    dotenv().ok();

    rocket::ignite()
        .attach(Template::fairing())
        .mount("/", routes![index, show_post, favicon, rss_feed, atom_feed])
        .mount("/admin", admin::get_routes())
        .mount("/static", StaticFiles::from("static"))
        .register(catchers![not_found_404])
        .launch();
}