use nom::combinator::complete;
use nom::sequence::separated_pair;
use nom::{character::complete::char, Finish};
use std::{
    io,
    process::{Command, Stdio},
    str::{from_utf8, Utf8Error},
};

use thiserror::Error;

use crate::sway::Rect;

#[derive(Error, Debug)]
pub(crate) enum Error {
    #[error("error running slurp")]
    SlurpFailed(#[from] io::Error),

    #[error("slurp's output is not valid UTF-8")]
    OutputNotUtf8(#[from] Utf8Error),

    /// Error parsing slurp's output.
    #[error("slurp's output contains invalid numbers")]
    InvalidOutput(String),
}

impl From<nom::error::Error<&str>> for Error {
    fn from(value: nom::error::Error<&str>) -> Self {
        Error::InvalidOutput(value.to_string())
    }
}

pub(crate) fn pick_a_region() -> Result<Rect, Error> {
    let output = Command::new("slurp")
        .args(["-f", "%X,%Y %Wx%H"])
        .stdout(Stdio::piped())
        .output()?;
    let output_str = from_utf8(&output.stdout)?;

    let (_i, ((x, y), (width, height))) = complete(separated_pair(
        separated_pair(
            nom::character::complete::u16::<&str, nom::error::Error<_>>,
            char(','),
            nom::character::complete::u16,
        ),
        char(' '),
        separated_pair(
            nom::character::complete::u16,
            char('x'),
            nom::character::complete::u16,
        ),
    ))(output_str)
    .finish()?;

    Ok(Rect {
        x,
        y,
        width,
        height,
    })
}
