#[derive(Debug)]
pub enum Ast {
    AssignVar(AssignVar),
    Pipes(Pipes),
}

#[derive(Debug)]
pub struct AssignVar {
    pub to: String,
    // TODO: body
}

#[derive(Debug)]
pub struct Pipes {
    pub cmds: Vec<Command>,
}

#[derive(Debug)]
pub struct Command {
    pub cmd: Vec<u8>,
    pub args: Vec<Vec<u8>>,
}

#[derive(Debug)]
pub enum ParseError {
    /// "clean" EOF, i.e. not in the middle of something
    Eof,

    /// "unclean" EOF, i.e. EOF after beginning a quoted string
    Incomplete,

    UnexpectedPipe,

    Unknown(u8),
}

type Result<T> = std::result::Result<T, ParseError>;

pub fn do_parse(mut x: &[u8]) -> Result<Ast> {
    Ast::parse(&mut x)
}

trait Parse: Sized {
    fn parse(b: &mut &[u8]) -> Result<Self>;
}

#[inline(always)]
fn parse<T: Parse>(b: &mut &[u8]) -> Result<T> {
    T::parse(b)
}

fn spaces(b: &mut &[u8]) {
    while let Some(b' ' | b'\t') = b.get(0) {
        *b = &b[1..];
    }
}

#[inline(always)]
fn adv(b: &mut &[u8]) {
    *b = &b[1..]
}

fn parse_quoted_string(b: &mut &[u8], delim: u8) -> Result<Vec<u8>> {
    // TODO: escape sequence stuff

    let mut s = Vec::new();
    while b.len() > 0 {
        if b[0] == delim {
            adv(b);
            return Ok(s);
        }

        s.push(b[0]);
        adv(b);
    }

    if delim == b' ' {
        Ok(s)
    } else {
        Err(ParseError::Incomplete)
    }
}

impl Parse for Vec<u8> {
    fn parse(b: &mut &[u8]) -> Result<Self> {
        spaces(b);
        if b.is_empty() {
            return Err(ParseError::Eof);
        }
        let c = b[0];
        if c == b'|' {
            Err(ParseError::UnexpectedPipe)
        } else if c == b'\'' || c == b'"' {
            adv(b);
            parse_quoted_string(b, c)
        } else if c.is_ascii_graphic() {
            parse_quoted_string(b, b' ')
        } else {
            Err(ParseError::Unknown(c))
        }
    }
}

impl Parse for Ast {
    fn parse(b: &mut &[u8]) -> Result<Self> {
        Ok(Self::Pipes(parse(b)?))
    }
}

impl Parse for Command {
    fn parse(b: &mut &[u8]) -> Result<Self> {
        let path: Vec<u8> = parse(b)?;
        let mut args = Vec::new();
        loop {
            let arg: Result<Vec<u8>> = parse(b);
            match arg {
                Ok(arg) => args.push(arg),
                Err(ParseError::Eof | ParseError::UnexpectedPipe) => break,
                Err(e) => Err(e)?,
            }
        }
        Ok(Self { cmd: path, args })
    }
}

impl Parse for Pipes {
    fn parse(b: &mut &[u8]) -> Result<Self> {
        let mut cmds: Vec<Command> = vec![parse(b)?];

        loop {
            spaces(b);
            if b.is_empty() {
                return Ok(Pipes { cmds });
            }

            let c = b[0];
            if c == b'|' {
                adv(b);
                cmds.push(parse(b)?);
            } else {
                Err(ParseError::Unknown(c))?;
            }
        }
    }
}
