use thiserror::Error;

use fsv_sys::{
    get_library_path,
    Error as FsvError,
    SSVLibrary,
    SSVLibraryCommon,
    SupportedFsvVersion,
    V1_40_13,
    V1_40_14
};

mod errors_ssv;
use errors_ssv::SSVErrorCodes;

#[derive(Error, Debug)]
pub enum Error {
    #[error(transparent)]
    FSVSysLibrary(#[from] FsvError),
    #[error(transparent)]
    SSVError(#[from] SSVErrorCodes),
}

/// Enum to hold the different versions of the SSV library
enum SsvLibraryVersion {
    V1_40_13(SSVLibrary<V1_40_13>),
    V1_40_14(SSVLibrary<V1_40_14>),
}

/// Struct to hold the SSV library and access its functions
pub struct SSV {
    library: SsvLibraryVersion,
}

impl SSV {
    fn new(version: SupportedFsvVersion) -> Result<Self, Error> {
        let library = match version {
            SupportedFsvVersion::V1_40_13 => {
                let lib_path = get_library_path(&version);
                let library = SSVLibrary::<V1_40_13>::new(&lib_path)?;
                SsvLibraryVersion::V1_40_13(library)
            },
            SupportedFsvVersion::V1_40_14 => {
                let lib_path = get_library_path(&version);
                let library = SSVLibrary::<V1_40_14>::new(&lib_path)?;
                SsvLibraryVersion::V1_40_14(library)
            },
        };
        Ok(Self { 
            library,
        })
    }

    /// # Initialize the SSV library
    /// Implement: SSV_InitLIB2
    pub fn init_library(&self, sesam_ini_path: &str) -> Result<(), Error> {
        let sesam_ini_path = std::ffi::CString::new(sesam_ini_path).expect("CString::new failed");
        let result = match &self.library {
            SsvLibraryVersion::V1_40_13(library) => {
                unsafe { library.ssv_init_lib2(sesam_ini_path.as_ptr()) }?
            },
            SsvLibraryVersion::V1_40_14(library) => {
                unsafe { library.ssv_init_lib2(sesam_ini_path.as_ptr()) }?
            },
        };
        if result != 0 {
            let error = SSVErrorCodes::from(result);
            return Err(Error::SSVError(error));
        }
        Ok(())
    }
}

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

    use utils::config::load_config;
    use anyhow::Result;

    use super::*;

    fn init() -> Result<SSV> {
        load_config().unwrap();
        Ok(SSV::new(SupportedFsvVersion::V1_40_13)?)
    }

    #[test]
    fn test_init_library() -> Result<()> {
        let lib = init()?;
        let sesam_ini_path = env::var("SESAM_INI_PATH").expect("SESAM_INI_PATH must be set");
        lib.init_library(&sesam_ini_path)?;
        Ok(())
    }

    #[test]
    fn test_ssv() {
        assert_eq!("ssv", "ssv");
    }
}