#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate toml;
extern crate regex;
extern crate tera;
extern crate chrono;

#[macro_use]
extern crate errors;

use std::path::Path;
use regex::Regex;
use errors::{Result, ResultExt};

mod page;
mod section;

pub use page::PageFrontMatter;
pub use section::SectionFrontMatter;

lazy_static! {
    static ref PAGE_RE: Regex = Regex::new(r"^[[:space:]]*\+\+\+\r?\n((?s).*?(?-s))\+\+\+\r?\n?((?s).*(?-s))$").unwrap();
}

#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum SortBy {
    /// Most recent to oldest
    Date,
    /// Lower order comes last
    Order,
    /// Lower weight comes first
    Weight,
    /// No sorting
    None,
}

#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum InsertAnchor {
    Left,
    Right,
    None,
}


/// Split a file between the front matter and its content
/// Will return an error if the front matter wasn't found
fn split_content(file_path: &Path, content: &str) -> Result<(String, String)> {
    if !PAGE_RE.is_match(content) {
        bail!("Couldn't find front matter in `{}`. Did you forget to add `+++`?", file_path.to_string_lossy());
    }

    // 2. extract the front matter and the content
    let caps = PAGE_RE.captures(content).unwrap();
    // caps[0] is the full match
    // caps[1] => front matter
    // caps[2] => content
    Ok((caps[1].to_string(), caps[2].to_string()))
}

/// Split a file between the front matter and its content.
/// Returns a parsed `SectionFrontMatter` and the rest of the content
pub fn split_section_content(file_path: &Path, content: &str) -> Result<(SectionFrontMatter, String)> {
    let (front_matter, content) = split_content(file_path, content)?;
    let meta = SectionFrontMatter::parse(&front_matter)
        .chain_err(|| format!("Error when parsing front matter of section `{}`", file_path.to_string_lossy()))?;
    Ok((meta, content))
}

/// Split a file between the front matter and its content
/// Returns a parsed `PageFrontMatter` and the rest of the content
pub fn split_page_content(file_path: &Path, content: &str) -> Result<(PageFrontMatter, String)> {
    let (front_matter, content) = split_content(file_path, content)?;
    let meta = PageFrontMatter::parse(&front_matter)
        .chain_err(|| format!("Error when parsing front matter of page `{}`", file_path.to_string_lossy()))?;
    Ok((meta, content))
}

#[cfg(test)]
mod tests {
    use std::path::Path;

    use super::{split_section_content, split_page_content};

    #[test]
    fn can_split_page_content_valid() {
        let content = r#"
+++
title = "Title"
description = "hey there"
date = 2002-10-12
+++
Hello
"#;
        let (front_matter, content) = split_page_content(Path::new(""), content).unwrap();
        assert_eq!(content, "Hello\n");
        assert_eq!(front_matter.title.unwrap(), "Title");
    }

    #[test]
    fn can_split_section_content_valid() {
        let content = r#"
+++
paginate_by = 10
+++
Hello
"#;
        let (front_matter, content) = split_section_content(Path::new(""), content).unwrap();
        assert_eq!(content, "Hello\n");
        assert!(front_matter.is_paginated());
    }

    #[test]
    fn can_split_content_with_only_frontmatter_valid() {
        let content = r#"
+++
title = "Title"
description = "hey there"
date = 2002-10-12
+++"#;
        let (front_matter, content) = split_page_content(Path::new(""), content).unwrap();
        assert_eq!(content, "");
        assert_eq!(front_matter.title.unwrap(), "Title");
    }

    #[test]
    fn can_split_content_lazily() {
        let content = r#"
+++
title = "Title"
description = "hey there"
date = 2002-10-02T15:00:00Z
+++
+++"#;
        let (front_matter, content) = split_page_content(Path::new(""), content).unwrap();
        assert_eq!(content, "+++");
        assert_eq!(front_matter.title.unwrap(), "Title");
    }

    #[test]
    fn errors_if_cannot_locate_frontmatter() {
        let content = r#"
+++
title = "Title"
description = "hey there"
date = 2002-10-12"#;
        let res = split_page_content(Path::new(""), content);
        assert!(res.is_err());
    }

}
