extern crate serde;
extern crate serde_json;

use ommui_file_loading::{load_json, load_string};
use samplic::LoadableDevice;
use snafu::ResultExt;
use {error, samplic, LoadFromPathPattern, Result, Uuid};

#[derive(Debug)]
pub struct Device {
    pub system_path: String,
    pub config_path: String,
    pub data_path: String,
}

impl LoadableDevice for Device {
    fn load_unit_index(&self) -> Result<samplic::DirectoryListing> {
        samplic::DirectoryListing::load_from_path_pattern(&format!(
            "{}/samplic/unit/*/unit.json",
            self.system_path
        ))
    }

    fn load_unit(
        &self,
        unit_id: &str,
    ) -> Result<samplic::UnitDescription> {
        load_json(&format!(
            "{}/samplic/unit/{}/unit.json",
            self.system_path, unit_id
        ))
        .context(error::OmmuiFileLoading)
    }

    fn load_translation_index(&self) -> Result<samplic::DirectoryListing> {
        samplic::DirectoryListing::load_from_path_pattern(&format!(
            "{}/samplic/translation/*/translation.json",
            self.system_path
        ))
    }

    fn load_translation(
        &self,
        translation_id: &str,
    ) -> Result<samplic::Translation> {
        load_json(&format!(
            "{}/samplic/translation/{}/translation.json",
            self.system_path, translation_id
        ))
        .context(error::OmmuiFileLoading)
    }

    fn load_sample_metadata(
        &self,
        sample_id: &Uuid,
    ) -> Result<samplic::SampleMetadata> {
        load_json(&format!(
            "{}/sample/{}/metadata/metadata.json",
            self.data_path,
            sample_id.to_hyphenated()
        ))
        .context(error::OmmuiFileLoading)
    }

    fn load_measurement_metadata(
        &self,
        sample_id: &Uuid,
        measurement_index: usize,
    ) -> Result<samplic::MeasurementMetadata> {
        load_json(&format!(
            "{}/sample/{}/measurement/{}/metadata/metadata.json",
            self.data_path,
            sample_id.to_hyphenated(),
            measurement_index
        ))
        .context(error::OmmuiFileLoading)
    }

    fn load_curve(
        &self,
        sample_id: &Uuid,
        measurement_index: usize,
        curve_id: &str,
    ) -> Result<samplic::Curve> {
        load_json(&format!(
            "{}/sample/{}/measurement/{}/curve/{}/curve.json",
            self.data_path,
            sample_id.to_hyphenated(),
            measurement_index,
            curve_id
        ))
        .context(error::OmmuiFileLoading)
    }

    fn load_curve_index(
        &self,
        sample_id: &Uuid,
        measurement_index: usize,
    ) -> Result<samplic::DirectoryListing> {
        samplic::DirectoryListing::load_from_path_pattern(&format!(
            "{}/sample/{}/measurement/{}/curve/*/curve.json",
            self.data_path,
            sample_id.to_hyphenated(),
            measurement_index
        ))
    }

    fn load_results(
        &self,
        sample_id: &Uuid,
        measurement_index: usize,
    ) -> Result<samplic::MeasurementResults> {
        load_json(&format!(
            "{}/sample/{}/measurement/{}/results/results.json",
            self.data_path,
            sample_id.to_hyphenated(),
            measurement_index
        ))
        .context(error::OmmuiFileLoading)
    }

    fn load_sampling_index(&self) -> Result<samplic::DirectoryListing> {
        samplic::DirectoryListing::load_from_path_pattern(&format!(
            "{}/description/sampling/*/samplic/sampling.json",
            self.system_path
        ))
    }

    fn load_measurement_index(
        &self,
        sample_id: &Uuid,
    ) -> Result<samplic::DirectoryListing> {
        samplic::DirectoryListing::load_from_path_pattern(&format!(
            "{}/sample/{}/measurement/*",
            self.data_path,
            sample_id.to_hyphenated()
        ))
    }

    fn load_sample_index(&self) -> Result<samplic::DirectoryListing> {
        samplic::DirectoryListing::load_from_path_pattern(&format!(
            "{}/sample/*",
            self.data_path
        ))
    }

    fn load_device_description(
        &self,
    ) -> Result<samplic::DeviceDescription> {
        load_json(&format!(
            "{}/description/device/samplic/device.json",
            self.system_path
        ))
        .context(error::OmmuiFileLoading)
    }

    fn load_profile(
        &self,
        profile_id: &Uuid,
    ) -> Result<samplic::ProfileDescription> {
        let profile_id = profile_id.to_hyphenated().to_string();

        for path in &[&self.system_path, &self.config_path] {
            let index = samplic::DirectoryListing::load_from_path_pattern(
                &format!(
                    "{}/description/profile/*/samplic/profile.json",
                    path
                ),
            );

            if index?.contains_key(&profile_id) {
                return load_json(&format!(
                    "{}/description/profile/{}/samplic/profile.json",
                    path, profile_id
                ))
                .context(error::OmmuiFileLoading);
            }
        }

        error::NotFound {
            kind: "profile",
            id: profile_id.to_string(),
        }
        .fail()?
    }

    fn load_profile_index(&self) -> Result<samplic::DirectoryListing> {
        let mut listing = samplic::DirectoryListing::new();

        for path in &[&self.system_path, &self.config_path] {
            listing.extend(
                samplic::DirectoryListing::load_from_path_pattern(
                    &format!(
                        "{}/description/profile/*/samplic/profile.json",
                        path
                    ),
                )?,
            );
        }

        Ok(listing)
    }

    fn load_sampling(
        &self,
        sampling_id: &str,
    ) -> Result<samplic::SamplingDescription> {
        load_json(&format!(
            "{}/description/sampling/{}/samplic/sampling.json",
            self.system_path, sampling_id
        ))
        .context(error::OmmuiFileLoading)
    }

    fn load_icon(&self, icon_id: &str) -> Result<samplic::Icon> {
        load_string(&format!(
            "{}/image/icon/{}/icon.svg",
            self.system_path, icon_id
        ))
        .context(error::OmmuiFileLoading)
    }

    fn load_icon_index(&self) -> Result<samplic::DirectoryListing> {
        samplic::DirectoryListing::load_from_path_pattern(&format!(
            "{}/image/icon/*/icon.svg",
            self.system_path
        ))
    }
}

impl Device {
    #[deprecated(
        since = "0.5.1",
        note = "please initialize public fields directly"
    )]
    pub fn new(
        system_path: &str,
        config_path: &str,
        data_path: &str,
    ) -> Self {
        Device {
            system_path: system_path.to_string(),
            config_path: config_path.to_string(),
            data_path: data_path.to_string(),
        }
    }
}
