/*
 * Copyright (c) 2022, Umut İnan Erdoğan <umutinanerdogan@pm.me>
 *
 * SPDX-License-Identifier: MPL-2.0
 */

use ext2::fs::{sync::Synced, Ext2};
use ext2::sector::SectorSize;
use ext2::volume::Volume;

use super::errors::FsError;
use super::{DirectoryEntry, FsNode, FsResult as Result, StorageDevice};

pub struct Ext2StorageDevice<S, V>
where
    S: SectorSize,
    V: Volume<u8, S>,
{
    fs: Synced<Ext2<S, V>>,
}

impl<S, V> Ext2StorageDevice<S, V>
where
    S: SectorSize,
    V: Volume<u8, S>,
{
    pub fn new(volume: V) -> Result<Self> {
        Ok(Self {
            fs: Synced::new(volume).map_err(|e| e.into())?,
        })
    }
}

impl<S, V> StorageDevice for Ext2StorageDevice<S, V>
where
    S: SectorSize + Send,
    V: Volume<u8, S> + Send,
{
    fn read(&self, node: &FsNode, offset: usize, size: usize, buffer: &mut Vec<u8>) -> Result<()> {
        let inode = self
            .fs
            .inode_nth(node.inode as usize)
            .ok_or_else(|| FsError::InodeNotFound)?;

        // FIXME: I don't really know how Ext2 works, so for now we don't
        //        support non-zero offsets and buffer sizes that don't match
        //        the file length. We always read the whole file.
        if offset > 0 || size != inode.size() {
            Err(FsError::UnsupportedOperation)?;
        }

        inode.read_to_end(buffer).map_err(|e| e.into())?;
        Ok(())
    }

    fn write(&self, _node: &FsNode, _offset: usize, _buffer: &[u8]) -> Result<()> {
        todo!()
    }

    fn read_dir(&self, _node: &FsNode, _index: usize) -> Result<DirectoryEntry> {
        todo!()
    }

    fn find_dir(&self, _node: &FsNode, _name: &str) -> Result<FsNode> {
        todo!()
    }
}

// fn load_fs() -> Arc<Mutex<Ext2<Size1024, Vec<u8>>>> {
//     let mut volume = Vec::new();
//     volume.extend_from_slice(include_bytes!("../../../userland/root_fs/ext2.img"));

//     Arc::<Ext2<Size1024, _>>::new(volume).unwrap()
// }

// pub fn walk<S: SectorSize, V: Volume<u8, S>>(
//     fs: &Synced<Ext2<S, V>>,
//     inode: Inode<S, V>,
//     name: String,
// ) {
//     if let Some(dir) = inode.directory() {
//         for entry in dir {
//             assert!(entry.is_ok());
//             let entry = entry.unwrap();
//             let entry_name = String::from_utf8_lossy(&entry.name);

//             println!("{}/{} => {}", name, entry_name, entry.inode,);
//             if entry_name != "." && entry_name != ".." {
//                 walk(
//                     fs,
//                     fs.inode_nth(entry.inode).unwrap(),
//                     format!("{}/{}", name, entry_name),
//                 );
//             }
//         }
//     }
// }