use crate::{ReadSeek, DataObject, DataError, DataStore, DataStoreOk, TrustPolicy};
use crate::source::{Source, SourceManager, FilesystemSource, TrustLevel};

use std::cell::RefCell;
use std::rc::Rc;

struct TextData
{
    text : String
}

impl DataObject for TextData
{
    fn folder_name() -> &'static str where Self : Sized
    {
        "text"
    }
    fn trust_policy() -> TrustPolicy { TrustPolicy::UntrustedOk }
    fn want_file(pathname : &str) -> bool where Self : Sized
    {
        pathname.ends_with(".txt")
    }
    fn from_io(mut reader: Box<dyn ReadSeek>, full_pathname : &str, source : &mut Box<dyn Source>) -> Result<Self, DataError> where Self : Sized
    {
        let mut text = String::new();
        
        let result = reader.read_to_string(&mut text);
        
        if result.is_err()
        {
            return Err(DataError::BadData("Couldn't read string".to_string()));
        }
        
        let mut new_path = String::from(full_pathname);
        new_path.push_str(".append");
        
        if let Ok(mut append_read) = source.read_file(&new_path)
        {
            let append_result = append_read.read_to_string(&mut text);
            
            if append_result.is_err()
            {
                return Err(DataError::BadData("Couldn't append string".to_string()));
            }
        }
        
        Ok(TextData{ text })
    }
}

fn make_store(source : Box<dyn Source>) -> DataStore<TextData>
{
    let source_manager = Rc::new(RefCell::new(SourceManager::new()));
    source_manager.borrow_mut().add_source(source);
    
    DataStore::<TextData>::new(source_manager)
}

fn test_unloaded_package(store : &mut DataStore<TextData>)
{
    assert_eq!(store.package_loaded("testpackage"), false);
    assert!(store.load_package("wrongtpackage").is_err());
    assert_eq!(store.package_loaded("wrongpackage"), false);
    assert_eq!(store.unload_package("wrongpackage"), false);
    
    assert!(store.get(0).is_none());
    assert!(store.get_mut(0).is_none());
    assert!(store.get_id("testpackage", "item1.txt").is_err());
    assert!(store.get_id_mut("testpackage", "item1.txt").is_ok());
}

fn test_loaded_package(store : &mut DataStore<TextData>)
{
    let expected_files = [("item1.txt", "contents1"),
        ("item2.txt", "contents2"),
        ("item3.txt", "contents3 appendcontents"),
        ("directory/anotherdirectory/subitem.txt", "contentssub")];
    
    assert_eq!(store.package_loaded("testpackage"), false);
    
    let load_result = store.load_package("testpackage").expect("Package load failed");
    assert_eq!(load_result, DataStoreOk::Loaded);
    assert_eq!(store.package_loaded("testpackage"), true);
    
    let load_result = store.load_package("testpackage").expect("Package load failed");
    assert_eq!(load_result, DataStoreOk::AlreadyLoaded);
    assert_eq!(store.package_loaded("testpackage"), true);
    
    assert!(store.get(999999).is_none());
    assert!(store.get_mut(999999).is_none());
    
    for (name, expected_data) in &expected_files
    {
        let id = store.get_id("testpackage", name).expect("ID not found");
        let data = store.get(id).expect("Data not found");
        assert_eq!(data.text, expected_data.to_string());
        
        let id = store.get_id_mut("testpackage", name).expect("ID not found");
        let data = store.get_mut(id).expect("Data not found");
        assert_eq!(data.text, expected_data.to_string());
    }
    
    for (name, expected_data) in &expected_files
    {
        let id = store.get_id_mut("testpackage", name).expect("ID not found");
        let data = store.get_mut(id).expect("Data not found");
        
        assert_eq!(data.text, expected_data.to_string());
    }
    
    assert!(store.get_id("testpackage", "skippeditem.ini").is_err());
    assert!(store.get_id("testpackage", "missingfile.txt").is_err());
}

fn test_loaded_unloaded_package(store : &mut DataStore<TextData>)
{
    assert_eq!(store.package_loaded("testpackage"), false);
    
    let load_result = store.load_package("testpackage").expect("Package load failed");
    assert_eq!(load_result, DataStoreOk::Loaded);
    assert_eq!(store.package_loaded("testpackage"), true);
    
    assert_eq!(store.unload_package("testpackage"), true);
    assert_eq!(store.package_loaded("testpackage"), false);
    assert_eq!(store.unload_package("testpackage"), false);
    
    for name in &["item1.txt",
        "item2.txt",
        "item3.txt",
        "directory/anotherdirectory/subitem.txt"]
    {
        // IDs should not resolve when using the non-mut version
        assert!(store.get_id("testpackage", name).is_err());
    }
    
    let load_result = store.load_package("testpackage").expect("Package load failed");
    assert_eq!(load_result, DataStoreOk::Loaded);
    assert_eq!(store.package_loaded("testpackage"), true);
    
    store.unload_all();
    
    assert_eq!(store.package_loaded("testpackage"), false);
    
    for name in &["item1.txt",
        "item2.txt",
        "item3.txt",
        "directory/anotherdirectory/subitem.txt"]
    {
        // IDs should get loaded on resolve with the mute version
        let id = store.get_id_mut("testpackage", name).expect("ID not found");
        assert!(store.get(id).is_some());
        assert!(store.get_mut(id).is_some());
    }
    
    store.unload_all();
    
    assert_eq!(store.package_loaded("testpackage"), false);
    
    test_loaded_package(store);
}

fn test_id_persistence(store : &mut DataStore<TextData>)
{
    store.load_package("testpackage").expect("Package load failed");
    
    let mut ids = Vec::new();
    
    for name in &["item1.txt",
        "item2.txt",
        "item3.txt",
        "directory/anotherdirectory/subitem.txt"]
    {
        let id = store.get_id("testpackage", name).expect("ID not found");
        let data = store.get(id).expect("Data not found");
        
        ids.push((id, data.text.clone()));
    }
    
    store.unload_package("testpackage");
    store.load_package("testpackage").expect("Package load failed");
    
    for (id, expected_text) in &ids
    {
        let data = store.get(*id).expect("Data not found");
        assert_eq!(data.text, *expected_text);
    }
}

#[test]
fn filesystem_unloaded_package()
{
    let source = FilesystemSource::new("testdata", TrustLevel::TrustedSource);
    let mut store = make_store(Box::new(source));
    
    test_unloaded_package(&mut store);
}

#[test]
fn filesystem_loaded_package()
{
    let source = FilesystemSource::new("testdata", TrustLevel::TrustedSource);
    let mut store = make_store(Box::new(source));
    
    test_loaded_package(&mut store);
}

#[test]
fn filesystem_loaded_unloaded_package()
{
    let source = FilesystemSource::new("testdata", TrustLevel::TrustedSource);
    let mut store = make_store(Box::new(source));
    
    test_loaded_unloaded_package(&mut store);
}

#[test]
fn filesystem_id_persistence()
{
    let source = FilesystemSource::new("testdata", TrustLevel::TrustedSource);
    let mut store = make_store(Box::new(source));
    
    test_id_persistence(&mut store);
}
