use std::collections::HashMap;
use std::fmt::Write;
use std::fs;
use std::io;
use std::path::PathBuf;
use std::rc::Rc;
use anyhow::anyhow;
use log::{debug, warn};
use serde::{Serialize, Deserialize};
use tempfile::{tempdir, TempDir};
use crate::input::Config;
use crate::markdown::RenderedContent;
const MAIN_JS:    &str  = include_str!("../res/js/main.js");
const MAIN_CSS:   &str  = include_str!("../res/style/main.css");
const GITHUB_CSS: &str  = include_str!("../res/style/github.css");
const ICON_PNG:   &[u8] = include_bytes!("../res/icon.png");
pub const HIGHLIGHT_JS_VERSION: &str = "9.18.1";
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct PageState {
        pub scroll_top: f64,
        pub image_widths: HashMap<String, f64>,
        pub image_heights: HashMap<String, f64>,
}
#[derive(Debug, Clone)]
pub struct Assets {
    real_dir: Option<PathBuf>,
    temp_dir: Option<Rc<TempDir>>,
}
impl Assets {
                                    pub fn init(output_dir: Option<PathBuf>) -> Result<Self, io::Error> {
        let assets =
            if let Some(real_dir) = output_dir {
                if !real_dir.is_dir() {
                    fs::create_dir_all(&real_dir)?;
                }
                let real_dir = Some(real_dir.canonicalize()?);
                Assets { real_dir, temp_dir: None }
            } else {
                let temp_dir = tempdir()?;
                let temp_dir = Some(Rc::new(temp_dir));
                Assets { temp_dir, real_dir: None }
            };
                let output_path = assets.output_path().unwrap();
        fs::write(output_path.join("main.js"), MAIN_JS).
            unwrap_or_else(|e| warn!("{}", e));
        fs::write(output_path.join("main.css"), MAIN_CSS).
            unwrap_or_else(|e| warn!("{}", e));
        fs::write(output_path.join("github.css"), GITHUB_CSS).
            unwrap_or_else(|e| warn!("{}", e));
        fs::write(output_path.join("icon.png"), ICON_PNG).
            unwrap_or_else(|e| warn!("{}", e));
        Ok(assets)
    }
                                                pub fn build(&self, content: &RenderedContent, page_state: &PageState) -> anyhow::Result<PathBuf> {
        let output_path     = self.output_path()?;
        let custom_css_path = Config::css_path();
        let json_state = serde_json::to_string(page_state).
            unwrap_or_else(|e| {
                warn!("Couldn't build JSON state from {:?}: {:?}", page_state, e);
                String::from("{}")
            });
        let mut hl_tags = String::new();
        if !content.code_languages.is_empty() {
            let root_url = format!(
                "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/{}",
                HIGHLIGHT_JS_VERSION
            );
                        writeln!(hl_tags, r#"<link rel="stylesheet" href="{}/styles/github.min.css" />"#, root_url).
                unwrap();
            writeln!(hl_tags, r#"<script src="{}/highlight.min.js"></script>"#, root_url).
                unwrap();
            for language in &content.code_languages {
                writeln!(
                    hl_tags,
                    r#"<script src="{}/languages/{}.min.js"></script>"#,
                    root_url, language
                ).unwrap();
            }
            writeln!(hl_tags, r#"<script>hljs.initHighlighting()</script>"#).unwrap();
        }
        debug!("Building HTML:");
        debug!(" > custom_css_path = {:?}", custom_css_path);
        debug!(" > page_state      = {:?}", json_state);
        debug!(" > code languages  = {:?}", content.code_languages);
        let page = format! {
            include_str!("../res/layout.html"),
            custom_css_path = custom_css_path.display(),
            body            = content.html,
            hl_tags         = hl_tags,
            page_state      = json_state,
        };
        let html_path = output_path.join("index.html");
        fs::write(&html_path, page.as_bytes())?;
        Ok(html_path)
    }
                pub fn output_path(&self) -> anyhow::Result<PathBuf> {
        match (&self.real_dir, &self.temp_dir) {
            (Some(path_buf), _) => Ok(path_buf.clone()),
            (_, Some(temp_dir)) => Ok(temp_dir.path().to_path_buf()),
            _ => Err(anyhow!("Assets don't have an output dir, there might be a synchronization error"))
        }
    }
                                pub fn clean_up(&mut self) {
        if let Some(temp_dir) = self.temp_dir.take() {
            let path = temp_dir.path();
            fs::remove_dir_all(path).unwrap_or_else(|_| {
                warn!("Couldn't delete temporary dir: {}", path.display());
            });
        }
    }
}