Documentation
use rustot::ota::pal::{OtaPal, OtaPalError, PalImageState};
use std::fs::File;
use std::io::{Cursor, Write};

pub struct FileHandler {
    filebuf: Option<Cursor<Vec<u8>>>,
}

impl FileHandler {
    pub fn new() -> Self {
        FileHandler { filebuf: None }
    }
}

impl OtaPal for FileHandler {
    type Error = ();

    fn abort(
        &mut self,
        _file: &rustot::ota::encoding::FileContext,
    ) -> Result<(), OtaPalError<Self::Error>> {
        Ok(())
    }

    fn create_file_for_rx(
        &mut self,
        file: &rustot::ota::encoding::FileContext,
    ) -> Result<(), OtaPalError<Self::Error>> {
        self.filebuf = Some(Cursor::new(Vec::with_capacity(file.filesize)));
        Ok(())
    }

    fn get_platform_image_state(&mut self) -> Result<PalImageState, OtaPalError<Self::Error>> {
        Ok(PalImageState::Valid)
    }

    fn set_platform_image_state(
        &mut self,
        _image_state: rustot::ota::pal::ImageState<()>,
    ) -> Result<(), OtaPalError<Self::Error>> {
        Ok(())
    }

    fn reset_device(&mut self) -> Result<(), OtaPalError<Self::Error>> {
        Ok(())
    }

    fn close_file(
        &mut self,
        file: &rustot::ota::encoding::FileContext,
    ) -> Result<(), OtaPalError<Self::Error>> {
        if let Some(ref mut buf) = &mut self.filebuf {
            log::debug!(
                "Closing completed file. Len: {}/{} -> {}",
                buf.get_ref().len(),
                file.filesize,
                file.filepath.as_str()
            );
            let mut file =
                File::create(file.filepath.as_str()).map_err(|_| OtaPalError::FileWriteFailed)?;
            file.write_all(buf.get_ref())
                .map_err(|_| OtaPalError::FileWriteFailed)?;

            Ok(())
        } else {
            Err(OtaPalError::BadFileHandle)
        }
    }

    fn write_block(
        &mut self,
        _file: &rustot::ota::encoding::FileContext,
        block_offset: usize,
        block_payload: &[u8],
    ) -> Result<usize, OtaPalError<Self::Error>> {
        if let Some(ref mut buf) = &mut self.filebuf {
            buf.set_position(block_offset as u64);
            buf.write(block_payload)
                .map_err(|_e| OtaPalError::FileWriteFailed)?;
            Ok(block_payload.len())
        } else {
            Err(OtaPalError::BadFileHandle)
        }
    }

    fn get_active_firmware_version(
        &self,
    ) -> Result<rustot::ota::pal::Version, OtaPalError<Self::Error>> {
        Ok(rustot::ota::pal::Version::new(0, 1, 0))
    }
}