/*
SPDX-License-Identifier: GPL-3.0-or-later
Copyright © 2026 Mike Robeson [dijksterhuis]
*/

use std::fs::remove_file;
use std::path::{Path, PathBuf};

use ot_tools_io::markers::SlotMarkers;
use ot_tools_io::projects::SlotAttributes;
use ot_tools_io::types::ActiveSlot;
use ot_tools_io::{OctatrackFileIO, SampleSettingsFile};
use ot_tools_io_derive::{AsMutDerive, AsRefDerive};

use crate::audio::AudioFile;
use crate::sample_files::{
    scan_dir_path_for_audio_files, CommitFiles, Files, ReadFile, ReadFiles, SampleFilesErrors,
    WriteFile,
};
use crate::{OtToolsInterfacesError, R};
/// A generic type to lazily read some type of file.
/// The inner type must implement the [`ReadFile`] and [`WriteFile`] traits.
///
/// File data is read when first accessed via one of the
/// [`Self::data`], [`Self::data_ref`] or [`Self::data_mut`] methods
/// -- [`Self::load`] and [`Self::new`] just initialize the instance.
/// Cached data can be dropped with the [`Self::uncache`] method.
/// Write your changes to a file with the [`Self::commit`] method.
/// On write, the data is cleared again to reduce memory footprint.
///
/// For eager reading, refer to the [`crate::sample_files::EagerFile`]
/// type and other and related types.
///
/// # Example: Write new file
/// ```
/// # use ot_tools_interfaces::sample_files::LazyAudioFile;
/// # use ot_tools_interfaces::OtToolsInterfacesError;
/// # use std::path::PathBuf;
/// # fn main() -> Result<(), OtToolsInterfacesError> {
/// let mut audio_file = LazyAudioFile::load("./test-data/sample-files/sample.wav");
///
/// let new_dirpath = std::env::temp_dir()
///     .join("ot-tools-interfaces")
///     .join("sample-files")
///     .join("lazy")
///     .join("lazy-file")
///     .join("commit-new");
///
/// let mut new = audio_file.to_new(&new_dirpath.join("sample.wav"))?;
/// std::fs::create_dir_all(&new_dirpath)?;
/// new.commit()?;
///
/// assert!(new_dirpath.join("sample.wav").exists());
/// # Ok(())}
/// ```
///
/// # Example: Skip writing over same data
/// ```
/// use std::fs::metadata;
///
/// # use ot_tools_interfaces::sample_files::LazyAudioFile;
/// # use ot_tools_interfaces::OtToolsInterfacesError;
/// # fn main() -> Result<(), OtToolsInterfacesError> {
/// let original = metadata("test-data/sample-files/sample.wav")?
///     .modified()
///     .unwrap();
///
/// // todo: this only really works because `data_ref()` isn't called here?
/// let mut audio = LazyAudioFile::load("test-data/sample-files/sample.wav");
/// audio.commit()?;
///
/// // file has not changed
/// assert_eq!(
///    metadata("test-data/sample-files/sample.wav")?
///        .modified()
///        .unwrap(),
///    original,
/// );
/// # Ok(()) }
/// ```
///
/// # Example: Write over existing file with changed data
/// ```
/// use std::fs::metadata;
///
/// # use ot_tools_interfaces::sample_files::LazyAudioFile;
/// # use ot_tools_interfaces::OtToolsInterfacesError;
/// # fn main() -> Result<(), OtToolsInterfacesError> {
/// let dirpath = std::env::temp_dir()
///     .join("ot-tools-interfaces")
///     .join("sample-files")
///     .join("lazy")
///     .join("lazy-file")
///     .join("commit-writeover");
///
/// # if dirpath.exists() { std::fs::remove_dir_all(&dirpath)?; };
/// # std::fs::create_dir_all(&dirpath.parent().unwrap())?;
/// # copy_dir::copy_dir("test-data/sample-files", &dirpath)?;
/// let original = metadata(&dirpath.join("sample.wav"))?
///     .modified()
///     .unwrap();
///
/// // load, modify, write
/// let mut x = LazyAudioFile::load(&dirpath.join("sample.wav"));
/// x.data_mut()?.linear_fade_in(0.2);
/// x.commit()?;
///
/// // file has changed
/// assert_ne!(
///     metadata(&dirpath.join("sample.wav"))?
///         .modified()
///         .unwrap(),
///     original,
/// );
///
/// # Ok(()) }
/// ```
/// # Example: Deleting a file
/// ```
/// # use ot_tools_interfaces::sample_files::LazyAudioFile;
/// # use ot_tools_interfaces::OtToolsInterfacesError;
/// # fn main() -> Result<(), OtToolsInterfacesError> {
/// let dirpath = std::env::temp_dir()
///     .join("ot-tools-interfaces")
///     .join("sample-files")
///     .join("lazy")
///     .join("lazy-file")
///     .join("commit-delete");
///
/// # if dirpath.exists() { std::fs::remove_dir_all(&dirpath)?; };
/// # std::fs::create_dir_all(&dirpath.parent().unwrap())?;
/// # copy_dir::copy_dir("test-data/sample-files", &dirpath)?;
/// let mut x = LazyAudioFile::load(dirpath.join("sample.wav"));
/// x.set_delete_marker(true);
/// x.commit()?;
///
/// // file was deleted
/// assert!(!dirpath.join("sample.wav").exists());
///
/// // calling `commit()` **does not** reset the delete marker
/// assert!(x.delete_marker());
///
/// // the file doesn't exist anymore, calling `commit` again does nothing
/// // todo: should this be an error?
/// assert!(x.commit().is_ok());
/// # Ok(()) }
/// ```
#[derive(Clone, Debug, PartialEq, PartialOrd)]
pub struct LazyFile<T: Clone + ReadFile<T> + WriteFile<T>> {
    /// path to the data file
    source: PathBuf,
    /// data which may or may not have been loaded yet
    data: Option<T>,
    /// whether to delete the file or not
    delete_marker: bool,
}

impl<T: std::fmt::Debug + Clone + ReadFile<T> + WriteFile<T>> LazyFile<T> {
    /// Creates a new instance of the type; maybe with data available, maybe not.
    pub fn new<P: AsRef<Path>>(path: P, data: Option<T>) -> Self {
        Self {
            source: path.as_ref().to_path_buf(),
            data,
            delete_marker: false,
        }
    }

    /// Creates a new instance of the type, without reading any data.
    pub fn load<P: AsRef<Path>>(path: P) -> Self {
        Self {
            source: path.as_ref().to_path_buf(),
            data: None,
            delete_marker: false,
        }
    }

    /// Owned access to the registered path of the source file
    pub fn pathbuf(&self) -> PathBuf {
        self.source.to_path_buf()
    }

    /// Reference access to the registered path of the source file
    pub fn path(&self) -> &Path {
        &self.source
    }

    /// Owned access to the data.
    ///
    /// Will read the file on the filesystem if data has not been loaded previously.
    /// Subsequently calling this method will access the cached data,
    /// unless you call [`Self::uncache`] at some point.
    ///
    /// Note that this is a `mut self` method to enable lazy loading.
    pub fn data(mut self) -> R<T> {
        if self.data.as_ref().is_none() {
            self.data = Some(T::read_file(&self.source)?);
        }
        self.data.ok_or(
            SampleFilesErrors::LazyRead {
                path: self.source.to_path_buf(),
            }
            .into(),
        )
    }

    /// Reference access to the data,
    ///
    /// Will read the file on the filesystem if data has not been loaded previously.
    /// Subsequently calling this method will access the cached data,
    /// unless you call [`Self::uncache`] at some point.
    ///
    /// # WARNING: panics!
    ///
    /// This method will panic if there is an error during reading the file.
    pub fn data_ref(&mut self) -> R<&T> {
        if self.data.as_ref().is_none() {
            self.data = Some(T::read_file(&self.source)?);
        }
        self.data.as_ref().ok_or(
            SampleFilesErrors::LazyRead {
                path: self.source.to_path_buf(),
            }
            .into(),
        )
    }

    /// Mutable reference access to the data,
    ///
    /// Will read the file on the filesystem if data has not been loaded previously.
    /// Subsequently calling this method will access the cached data,
    /// unless you call [`Self::uncache`] at some point.
    pub fn data_mut(&mut self) -> R<&mut T> {
        if self.data.as_ref().is_none() {
            self.data = Some(T::read_file(&self.source)?);
        }
        self.data.as_mut().ok_or(
            SampleFilesErrors::LazyRead {
                path: self.source.to_path_buf(),
            }
            .into(),
        )
    }

    /// Setter for delete marker.
    /// If this is set to `true`, the file will be deleted from the filesystem
    /// when the [`Self::commit`] method is called.
    ///
    /// See the relevant main example for [`Self`].
    pub fn set_delete_marker(&mut self, delete_marker: bool) {
        self.delete_marker = delete_marker;
    }

    /// Getter for current delete marker state.
    pub fn delete_marker(&self) -> bool {
        self.delete_marker
    }

    /// Creates a new copy targeted at a different path on the filesystem.
    /// Use this to create copies of a file.
    ///
    /// See the relevant example for [`Self`].
    #[allow(clippy::wrong_self_convention)]
    pub fn to_new<P: AsRef<Path>>(&mut self, path: P) -> R<Self> {
        // read the data from file if not already read
        if self.data.is_none() {
            self.data_ref()?;
        }
        let mut new = self.clone();
        new.source = path.as_ref().to_path_buf();
        Ok(new)
    }

    /// Drops cached data, free-ing up memory.
    pub fn uncache(&mut self) {
        self.data.take();
    }

    /// Makes changes to the file at the currently registered path.
    ///
    /// Calling this method will
    /// - delete the file from the filesystem if the file exists and the delete marker is set to `true`.
    /// - write a new file if the file does not currently exist.
    /// - write over the existing file if the current stored data differs to the data on the filesystem
    ///
    /// Additionally, any cached data is uncached via [`Self::uncache`],
    /// freeing up memory previously used for data we've just written to the filesystem.
    pub fn commit(&mut self) -> R<()> {
        // deletes or writes file
        if self.delete_marker {
            if self.path().exists() {
                remove_file(self.path())?;
            }
        } else {
            if let Some(x) = &self.data {
                T::write_file(x.clone(), self.path())?;
                // todo: skip writing over same data, always write new file
                //       see EagerFile
            }
        }
        // always uncache data, read again if we need to re-access
        self.uncache();
        Ok(())
    }
}

impl ReadFile<AudioFile> for LazySampleFilePair {
    fn read_file<P: AsRef<Path>>(path: P) -> R<AudioFile> {
        AudioFile::read_by_ext(path.as_ref())
    }
}

impl ReadFile<SampleSettingsFile> for LazySampleFilePair {
    fn read_file<P: AsRef<Path>>(path: P) -> R<SampleSettingsFile> {
        Ok(SampleSettingsFile::from_data_file(path.as_ref())?)
    }
}

impl WriteFile<AudioFile> for LazySampleFilePair {
    fn write_file<P: AsRef<Path>>(data: AudioFile, path: P) -> R<()> {
        data.write_by_ext(path.as_ref())?;
        Ok(())
    }
}

impl WriteFile<SampleSettingsFile> for LazySampleFilePair {
    fn write_file<P: AsRef<Path>>(data: SampleSettingsFile, path: P) -> R<()> {
        data.to_data_file(path.as_ref())?;
        Ok(())
    }
}

// todo: this needs updating/checking
// todo: also, need examples
// todo: also needs thinking about whether just PathBufs work well enough for
// sample pools.
/// Lazily read a pair of audio and sample settings files as one entitity.
/// This type is useful for accessing a large number of files in a sample pool
/// -- you don't need to read all file data into memory to manage said files.
/// Note that the lazy-loading implementation generally means this type needs to
/// be initialized as mutable, while eager types do not.
///
/// File data is read when first accessed via one of the data accessor methods
/// -- [`Self::load`] and [`Self::new`] just initialize the instance.
/// Any cached data can be dropped with the [`Self::uncache`] method.
/// Write your changes to a file with the [`Self::commit`] method.
/// On write, the data is cleared again to reduce memory footprint.
///
/// For eager reading, refer to the [`crate::sample_files::EagerSampleFilePair`]
/// type and other and related types.
///
/// # Example: Write new file pair
/// ```
/// # use ot_tools_interfaces::sample_files::LazySampleFilePair;
/// # use ot_tools_interfaces::OtToolsInterfacesError;
/// # use std::path::PathBuf;
/// # fn main() -> Result<(), OtToolsInterfacesError> {
/// let mut pair = LazySampleFilePair::load("./test-data/sample-files/sample.wav");
///
/// let new_dirpath = std::env::temp_dir()
///     .join("ot-tools-interfaces")
///     .join("sample-files")
///     .join("lazy")
///     .join("lazy-sample-file-pair")
///     .join("commit-new");
///
/// let mut new_pair = pair.to_new(&new_dirpath.join("sample.wav"))?;
/// std::fs::create_dir_all(&new_dirpath)?;
/// new_pair.commit()?;
///
/// // copies both files
/// assert!(new_dirpath.join("sample.wav").exists());
/// assert!(new_dirpath.join("sample.ot").exists());
/// # Ok(())}
/// ```
///
/// # Example: Skip writing unchanged data
/// ```
/// use std::fs::metadata;
///
/// # use ot_tools_interfaces::sample_files::LazySampleFilePair;
/// # use ot_tools_interfaces::OtToolsInterfacesError;
/// # fn main() -> Result<(), OtToolsInterfacesError> {
/// let original = metadata("test-data/sample-files/sample.wav")?
///     .modified()
///     .unwrap();
///
/// // todo: this only really works because `data_ref()` isn't called here?
/// let mut pair = LazySampleFilePair::load("test-data/sample-files/sample.wav");
/// pair.commit()?;
///
/// // file has not changed
/// assert_eq!(
///    metadata("test-data/sample-files/sample.wav")?
///        .modified()
///        .unwrap(),
///    original,
/// );
/// # Ok(()) }
/// ```
///
/// # Example: Write over existing file with changed data
/// ```
/// use std::fs::metadata;
///
/// # use ot_tools_interfaces::sample_files::LazySampleFilePair;
/// # use ot_tools_interfaces::OtToolsInterfacesError;
/// # fn main() -> Result<(), OtToolsInterfacesError> {
/// let dirpath = std::env::temp_dir()
///     .join("ot-tools-interfaces")
///     .join("sample-files")
///     .join("lazy")
///     .join("lazy-sample-file-pair")
///     .join("commit-writeover");
///
/// # if dirpath.exists() { std::fs::remove_dir_all(&dirpath)?; };
/// # std::fs::create_dir_all(&dirpath.parent().unwrap())?;
/// # copy_dir::copy_dir("test-data/sample-files", &dirpath)?;
/// let original = metadata(&dirpath.join("sample.wav"))?
///     .modified()
///     .unwrap();
///
/// // load, modify, write
/// let mut pair = LazySampleFilePair::load(&dirpath.join("sample.wav"));
/// pair.audio_file_mut().data_mut()?.linear_fade_in(0.2);
/// pair.commit()?;
///
/// // file has changed
/// assert_ne!(
///     metadata(&dirpath.join("sample.wav"))?
///         .modified()
///         .unwrap(),
///     original,
/// );
///
/// # Ok(()) }
/// ```
///
/// # Example: Deleting a file pair
/// ```
/// # use ot_tools_interfaces::sample_files::LazySampleFilePair;
/// # use ot_tools_interfaces::OtToolsInterfacesError;
/// # fn main() -> Result<(), OtToolsInterfacesError> {
/// let dirpath = std::env::temp_dir()
///     .join("ot-tools-interfaces")
///     .join("sample-files")
///     .join("lazy")
///     .join("lazy-sample-file-pair")
///     .join("commit-delete");
///
/// # if dirpath.exists() { std::fs::remove_dir_all(&dirpath)?; };
/// # std::fs::create_dir_all(&dirpath.parent().unwrap())?;
/// # copy_dir::copy_dir("test-data/sample-files", &dirpath)?;
/// // both files exist
/// assert!(dirpath.join("sample.wav").exists());
/// assert!(dirpath.join("sample.ot").exists());
///
/// let mut pair = LazySampleFilePair::load(dirpath.join("sample.wav"));
/// pair.audio_file_mut().set_delete_marker(true);
/// pair.settings_file_mut().unwrap().set_delete_marker(true);
/// pair.commit()?;
///
/// // both files were deleted
/// assert!(!dirpath.join("sample.wav").exists());
/// assert!(!dirpath.join("sample.ot").exists());
///
/// // calling `commit()` **does not** reset the delete marker
/// assert!(pair.audio_file_ref().delete_marker());
/// assert!(pair.settings_file_ref().unwrap().delete_marker());
///
/// // the file pair doesn't exist anymore, calling `commit` again does nothing
/// // todo: should this be an error?
/// assert!(pair.commit().is_ok());
/// # Ok(()) }
/// ```

#[derive(Clone, Debug, PartialEq, PartialOrd, AsMutDerive, AsRefDerive)]
pub struct LazySampleFilePair {
    audio_file: LazyFile<AudioFile>,
    settings_file: Option<LazyFile<SampleSettingsFile>>,
}

impl LazySampleFilePair {
    /// Create a new instance, given all the required data.
    /// No validation is performed by this method.
    pub fn new(
        audio_file: LazyFile<AudioFile>,
        settings_file: Option<LazyFile<SampleSettingsFile>>,
    ) -> Self {
        Self {
            audio_file,
            settings_file,
        }
    }

    pub fn audio_file(self) -> LazyFile<AudioFile> {
        self.audio_file
    }

    pub fn audio_file_ref(&self) -> &LazyFile<AudioFile> {
        &self.audio_file
    }

    pub fn audio_file_mut(&mut self) -> &mut LazyFile<AudioFile> {
        &mut self.audio_file
    }

    pub fn settings_file(self) -> Option<LazyFile<SampleSettingsFile>> {
        self.settings_file
    }

    pub fn settings_file_ref(&self) -> Option<&LazyFile<SampleSettingsFile>> {
        self.settings_file.as_ref()
    }

    pub fn settings_file_mut(&mut self) -> Option<&mut LazyFile<SampleSettingsFile>> {
        self.settings_file.as_mut()
    }

    /// Creates a new instance of the type, without reading any data.
    /// The audio file path must be passed for the `path` argument
    /// (the audio file *must* exist, the settings file may not).
    pub fn load<P: AsRef<Path>>(path: P) -> Self {
        let mut ot_filepath = path.as_ref().to_path_buf();
        ot_filepath.set_extension("ot");

        let settings_file = if ot_filepath.exists() {
            Some(LazyFile::<SampleSettingsFile>::load(ot_filepath))
        } else {
            None
        };

        Self {
            audio_file: LazyFile::<AudioFile>::load(path.as_ref()),
            settings_file,
        }
    }

    /// Creates a new copy targeted at a different path on the filesystem.
    /// Use this to create copies of a file.
    ///
    /// See the relevant example in [`Self::commit`].
    pub fn to_new<P: AsRef<Path>>(&mut self, path: P) -> R<Self> {
        let audio_file = self.audio_file.to_new(path.as_ref())?;
        let settings_file = if let Some(s_f) = self.settings_file.as_mut() {
            let mut ot_path = path.as_ref().to_path_buf();
            ot_path.set_extension("ot");
            Some(s_f.to_new(&ot_path)?)
        } else {
            None
        };

        Ok(Self {
            audio_file,
            settings_file,
            // op: None,
        })
    }

    /// Setter for delete marker on **both** files in the file pair.
    /// Calls the [`LazyFile::set_delete_marker`] method on both files.
    ///
    /// This means you can delete the files by calling [`Self::commit`] or by
    /// calling the [`LazyFile::commit`] method for each file.
    ///
    /// Note that we don't provide a getter/accessor for `delete_marker` on this
    /// type -- we're manipulating the inner types with this method.
    pub fn set_delete_marker(&mut self, delete_marker: bool) {
        self.audio_file_mut().set_delete_marker(delete_marker);
        if let Some(x) = self.settings_file_mut() {
            x.set_delete_marker(delete_marker)
        };
    }

    /// Drops cached data, free-ing up memory.
    pub fn uncache(&mut self) {
        self.audio_file_mut().uncache();
        if let Some(x) = self.settings_file_mut() {
            x.uncache()
        };
    }

    /// Will write the data to the file(s) at the paths registered in the
    /// internal fields of the inner types.
    ///
    /// ```
    /// # use ot_tools_interfaces::sample_files::LazySampleFilePair;
    /// # use ot_tools_interfaces::OtToolsInterfacesError;
    /// # use std::path::PathBuf;
    /// # fn main() -> Result<(), OtToolsInterfacesError> {
    /// let dirpath = std::env::temp_dir()
    ///     .join("ot-tools-interfaces")
    ///     .join("sample-files")
    ///     .join("lazy-sample-file-pair")
    ///     .join("commit");
    /// # std::fs::create_dir_all(&dirpath)?;
    /// let mut pair = LazySampleFilePair::load("test-data/sample-files/sample.wav");
    ///
    /// // first copy
    /// let mut new_1 = pair.to_new(&dirpath.join("1.wav"))?;
    /// new_1.commit()?;
    ///
    /// assert!(new_1.audio_file_ref().path().exists());
    /// assert!(new_1.settings_file_ref().unwrap().path().exists());
    ///
    /// // second copy
    /// let mut new_2 = pair.to_new(&dirpath.join("2.wav"))?;
    /// new_2.commit()?;
    ///
    /// assert!(new_2.audio_file_ref().path().exists());
    /// assert!(new_2.settings_file_ref().unwrap().path().exists());
    ///
    /// // deleting second copy files
    /// new_2.set_delete_marker(true);
    /// new_2.commit()?;
    ///
    /// assert!(!new_2.audio_file_ref().path().exists());
    /// assert!(!new_2.settings_file_ref().unwrap().path().exists());
    ///
    /// // third copy
    /// let mut new_3 = pair.to_new(&dirpath.join("3.wav"))?;
    /// new_3.commit()?;
    ///
    /// assert!(new_3.audio_file_ref().path().exists());
    /// assert!(new_3.settings_file_ref().unwrap().path().exists());
    ///
    /// // deleting third copy settings file only
    /// new_3.settings_file_mut().unwrap().set_delete_marker(true);
    /// new_3.commit()?;
    ///
    /// assert!(new_3.audio_file_ref().path().exists());
    /// assert!(!new_3.settings_file_ref().unwrap().path().exists());
    /// # Ok(())}
    /// ```
    pub fn commit(&mut self) -> R<()> {
        self.audio_file.commit()?;
        if let Some(s_f) = self.settings_file.as_mut() {
            s_f.commit()?;
        };
        Ok(())
    }
}

/// Collection of [`LazySampleFilePair`] instances aliased via the [`Files<T>`] type
// #[derive(Clone, Debug, Default, PartialEq, AsMutDerive, AsRefDerive)]
pub type LazySampleFilePairs = Files<LazySampleFilePair>;
//
// impl LazySampleFilePairs {
//     pub fn load<P: AsRef<Path>>(dirpath: P) -> R<Self> {
//         let files = scan_dir_path_for_audio_files(dirpath.as_ref())?;
//         let mut new = Self::new();
//         for file in files {
//             new.push(LazySampleFilePair::load(file));
//         }
//         Ok(new)
//     }
//
//     /// Writes all files with cached changes to the filesystem
//     pub fn commit(&mut self) -> R<()> {
//         for f in self.0.iter_mut() {
//             f.commit()?;
//         }
//         Ok(())
//     }
// }

impl ReadFiles<LazySampleFilePair> for LazySampleFilePairs {
    fn load<P: AsRef<Path>>(dirpath: P) -> R<Self> {
        Ok(scan_dir_path_for_audio_files(dirpath.as_ref())?
            .into_iter()
            .map(LazySampleFilePair::load)
            .collect())
    }
}

impl CommitFiles<LazySampleFilePair> for LazySampleFilePairs {
    fn commit(&mut self) -> R<()> {
        for f in self.0.iter_mut() {
            f.commit()?;
        }
        Ok(())
    }
}

impl TryFrom<ActiveSlot<SlotAttributes, SlotMarkers>> for LazySampleFilePair {
    type Error = OtToolsInterfacesError;

    fn try_from(value: ActiveSlot<SlotAttributes, SlotMarkers>) -> R<Self> {
        let path = value
            .attrs_ref()
            .path
            .clone()
            .ok_or(SampleFilesErrors::EmptySlotPath)?;

        Ok(LazySampleFilePair::load(path))
    }
}

impl TryFrom<Option<ActiveSlot<SlotAttributes, SlotMarkers>>> for LazySampleFilePair {
    type Error = OtToolsInterfacesError;

    fn try_from(value: Option<ActiveSlot<SlotAttributes, SlotMarkers>>) -> R<Self> {
        LazySampleFilePair::try_from(value.ok_or(SampleFilesErrors::InactiveSlot)?)
    }
}

pub type LazyAudioFile = LazyFile<AudioFile>;

pub type LazyAudioFiles = Files<LazyAudioFile>;

impl ReadFiles<LazyAudioFile> for LazyAudioFiles {
    fn load<P: AsRef<Path>>(dirpath: P) -> R<Self> {
        Ok(scan_dir_path_for_audio_files(dirpath.as_ref())?
            .into_iter()
            .map(LazyAudioFile::load)
            .collect())
    }
}

impl CommitFiles<LazyAudioFile> for LazyAudioFiles {
    fn commit(&mut self) -> R<()> {
        for f in self.0.iter_mut() {
            f.commit()?;
        }
        Ok(())
    }
}

pub type LazySampleSettingsFile = LazyFile<SampleSettingsFile>;

pub type LazySampleSettingsFiles = Files<LazySampleSettingsFile>;

impl ReadFiles<LazySampleSettingsFile> for LazySampleSettingsFiles {
    fn load<P: AsRef<Path>>(dirpath: P) -> R<Self> {
        Ok(scan_dir_path_for_audio_files(dirpath.as_ref())?
            .into_iter()
            .map(LazySampleSettingsFile::load)
            .collect())
    }
}

impl CommitFiles<LazySampleSettingsFile> for LazySampleSettingsFiles {
    fn commit(&mut self) -> R<()> {
        for f in self.0.iter_mut() {
            f.commit()?;
        }
        Ok(())
    }
}
