#![feature(proc_macro_hygiene, decl_macro)]

pub mod models;
pub mod schema;

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

use self::diesel::prelude::*;
use self::models::*;
use comrak::{markdown_to_html, ComrakOptions};
use diesel::pg::PgConnection;
use dotenv::dotenv;
use rand::Rng;
use rocket_contrib::serve::StaticFiles;
use rocket_contrib::templates::Template;
use std::env;
use std::vec::Vec;
use tera::Context;

const TITLES: [&str; 4] = ["/* {} */", "# {}", "// {}", "<!-- {} -->"];
const MAX_POSTS_PER_PAGE: i64 = 20;

fn establish_connection() -> PgConnection {
    dotenv().ok();

    let database_url = env::var("DATABASE_URL").expect("DATABASE_URL not setted");
    PgConnection::establish(&database_url).expect(&format!("Error connecting to {}", database_url))
}

fn get_posts(page: i64) -> (Vec<Post>, i64) {
    use schema::posts::dsl::*;

    let connection = establish_connection();
    let visible_posts = posts
        .filter(published.eq(true))
        .limit(MAX_POSTS_PER_PAGE)
        .offset(MAX_POSTS_PER_PAGE * (page - 1))
        .load::<Post>(&connection)
        .expect("Error loading posts");

    let number_of_posts: i64 = posts
        .filter(published.eq(true))
        .count()
        .get_result(&connection)
        .expect("Error counting the posts");

    (visible_posts, number_of_posts)
}

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

    let mut context = Context::new();

    let mut rng = rand::thread_rng();
    let title_fmt = TITLES[rng.gen_range(0, TITLES.len())];
    let title = str::replace(title_fmt, "{}", "CódigoComentado");

    let (posts, n_posts) = get_posts(page as i64);

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

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

fn get_post(id_number: i32) -> Post {
    use schema::posts::dsl::*;

    let connection = establish_connection();
    let post = posts.find(id_number).first(&connection);

    post.unwrap()
}

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

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

    let mut rng = rand::thread_rng();
    let title_fmt = TITLES[rng.gen_range(0, TITLES.len())];
    let title = str::replace(title_fmt, "{}", "CódigoComentado");

    let mut post = get_post(id);
    let content = markdown_to_html(&post.content, &ComrakOptions::default());
    post.content = content;

    context.insert("title", &title);
    context.insert("post", &post);
    Template::render("post", context)
}

fn main() {
    rocket::ignite()
        .attach(Template::fairing())
        .mount("/", routes![index, show_post])
        .mount("/static", StaticFiles::from("static"))
        .launch();
}