use crate::{Session, Value, SJFLError};
use crate::bsjfl::encode_timestamp;
use std::collections::hash_map::DefaultHasher;
use std::fs::{self, File, read};
use std::hash::{Hash, Hasher};
use std::io::Write;
use std::path::PathBuf;
use std::str::FromStr;

#[derive(Clone)]
pub struct SJFLFile {
    pub(crate) path: String,
    pub(crate) is_binary: bool,

    // if both `foo.sjfl` and `foo.json` exist, it prefers `.sfjl`
    pub(crate) is_json: bool,

    // hash value of timestamp
    //
    // it may vary depending on the system and version of SJFL, but that's not a big problem
    // collisions may occur, but that's not a big problem
    // -> parsing a raw-text is not a big deal
    pub(crate) timestamp: u64,
}

impl SJFLFile {

    pub fn get_file_name(&self) -> String {
        PathBuf::from_str(&self.path).unwrap().file_stem().unwrap().to_str().unwrap().to_string()
    }

    pub fn get_content(&self) -> Result<Vec<u8>, SJFLError> {
        match read(&self.path) {
            Ok(b) => Ok(b),
            Err(e) => Err(SJFLError::FileError(format!("{e:?}")))
        }
    }

    pub fn save_binary(&self, value: Value, session: &mut Session, original_timestamp: u64) -> Self {
        let mut binary_buffer = vec![];
        encode_timestamp(original_timestamp, &mut binary_buffer);
        value.to_bin(&mut binary_buffer, session);

        let mut new_path = PathBuf::from_str(&self.path).unwrap();
        assert!(new_path.set_extension("bsjfl"));
        let new_path = new_path.to_str().unwrap().to_string();

        let mut f = File::create(&new_path).unwrap();
        f.write_all(&binary_buffer).unwrap();

        let timestamp = fs::metadata(&new_path).unwrap().modified().unwrap();
        let mut hasher = DefaultHasher::new();
        timestamp.hash(&mut hasher);

        SJFLFile {
            path: new_path,
            is_binary: true, is_json: false,
            timestamp: hasher.finish()
        }
    }

}