use std::error::Error;
use std::fs;
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;

use bstr::{ByteSlice, ByteVec};
use inotify::{Inotify, WatchDescriptor, WatchMask};

pub(crate) struct Processor {
    pub path: PathBuf,
    pub command: String,
    pub args: Vec<String>,
    pub debounce: u64,
    pub keep_file: bool,
}

impl Processor {
    pub(crate) fn register(&self, inotify: &mut Inotify) -> Option<WatchDescriptor> {
        println!(
            "Registering {} => {} {}",
            self.path.display(),
            self.command,
            self.args.join(" ")
        );
        inotify
            .watches()
            .add(
                &self.path,
                WatchMask::CLOSE_WRITE
                    | WatchMask::MOVED_TO
                    | WatchMask::MOVED_FROM
                    | WatchMask::CREATE
                    | WatchMask::DELETE,
            )
            .map_err(|e| eprintln!("Error: {}", e))
            .ok()
    }

    pub(crate) fn process(&self, filename: &Path) -> Result<(), Box<dyn Error>> {
        let bfilename = <[u8]>::from_path(filename).unwrap();
        let args = self.args.iter().map(|arg| {
            arg.as_bytes()
                .replace("%f", bfilename)
                .into_os_string()
                .unwrap()
        });
        Command::new(&self.command).args(args).status()?.exit_ok()?;
        if !self.keep_file {
            println!("Removing {}", filename.display());
            fs::remove_file(filename)?;
        }
        Ok(())
    }
}