use std::io::{ErrorKind, Read, Seek, SeekFrom};
use std::fs::{File, OpenOptions};

pub struct FileDevice {
    pub name: Vec<u8>,
    pub rename: Vec<u8>,

    pub file: Option<File>,
    pub operation_state: bool,
    pub pointer: u32,
    pub file_size: u32,
}

impl FileDevice {
    pub fn new() -> Self {
        Self {
            name: Vec::new(),
            rename: Vec::new(),

            file: None,
            operation_state: false,
            pointer: 0,
            file_size: 0,
        }
    }

    pub fn push_name_byte(&mut self, byte: u8) {
        if byte != 0 { self.name.push(byte); return }
        // If char was null, attempt to open the file and read file size.
        let path: std::ffi::OsString = std::os::unix::ffi::OsStringExt::from_vec(
                std::mem::take(&mut self.name));
        let try_open = OpenOptions::new().read(true).write(true).open(path);
        if let Ok(file) = try_open {
            if let Ok(metadata) = file.metadata() {
                // Success.
                self.file = Some(file);
                match u32::try_from(metadata.len()) {
                    Ok(len) => self.file_size = len,
                    Err(_)  => self.file_size = u32::MAX,
                }
                return;
            }
        }
        // Failure.
        self.close();
    }

    pub fn set_file_size(&mut self) {
        if let Some(file) = &self.file {
            if let Ok(()) = file.set_len(self.file_size.into()) {
                return;
            };
        }
        self.close();
    }

    pub fn read(&mut self) -> u8 {
        if let Some(file) = &mut self.file {
            let mut buffer: [u8; 1] = [0; 1];
            match file.read_exact(&mut buffer) {
                Ok(()) => return buffer[0],
                Err(err) => match err.kind() {
                    ErrorKind::UnexpectedEof => return 0,
                    _ => unimplemented!("File read error: ErrorKind::{:?}", err.kind()),
                }
            }
        }
        self.close();
        return 0;
    }

    pub fn seek(&mut self) {
        if let Some(file) = &mut self.file {
            let point = SeekFrom::Start(self.pointer.into());
            if let Ok(pointer) = file.seek(point) {
                if let Ok(pointer_u32) = pointer.try_into() {
                    self.pointer = pointer_u32;
                    return;
                }
            };
        }
        self.close();
    }

    pub fn close(&mut self) {
        self.file = None;
        self.pointer = 0;
        self.file_size = 0;
    }
}