use super::{
    image::Image,
    symbol::{Symbol, SymbolType},
    sys, Config,
};

#[derive(thiserror::Error, Debug)]
pub enum ImageScannerError {
    #[error("Unable to set Image Scanner configuration")]
    UnableToSetConfig,
}

pub struct ImageScanner {
    inner: *mut sys::zbar_image_scanner_t,
}

impl ImageScanner {
    /// Link: [`sys::zbar_image_scanner_create`]
    pub fn new() -> Self {
        Self {
            inner: unsafe { sys::zbar_image_scanner_create() },
        }
    }

    /// Link: [`sys::zbar_image_scanner_set_config`]
    pub fn set_config(
        &mut self,
        symbol: SymbolType,
        config: Config,
        value: i32,
    ) -> Result<(), ImageScannerError> {
        let result =
            unsafe { sys::zbar_image_scanner_set_config(self.inner, symbol, config, value) };

        if result != 0 {
            return Err(ImageScannerError::UnableToSetConfig);
        }

        Ok(())
    }

    /// Link: [`sys::zbar_scan_image`]
    ///
    /// TODO: move `image` to newtype, offering conversions
    /// to and from image::Image
    ///
    /// TODO: return an iterator over scanned values
    pub fn scan_image(
        &mut self,
        image: &Image,
    ) -> Vec<Symbol> {
        unsafe { sys::zbar_scan_image(self.inner, image.inner) };
        let mut result = vec![];
        let mut symbol = unsafe { sys::zbar_image_first_symbol(image.inner) };
        while !symbol.is_null() {
            let symbol_type = unsafe { sys::zbar_symbol_get_type(symbol) };
            let symbol_data = unsafe { sys::zbar_symbol_get_data(symbol) };
            let symbol_data_len = unsafe { sys::zbar_symbol_get_data_length(symbol) };
            let symbol_slice = unsafe {
                std::slice::from_raw_parts(symbol_data as *const u8, symbol_data_len as usize)
            };
            result.push(Symbol::new(symbol_type, symbol_slice));
            symbol = unsafe { sys::zbar_symbol_next(symbol) };
        }

        result
    }
}

impl Default for ImageScanner {
    fn default() -> Self {
        Self::new()
    }
}

impl Drop for ImageScanner {
    fn drop(&mut self) {
        unsafe { sys::zbar_image_scanner_destroy(self.inner) }
    }
}