pub mod parse;

use endpoint::message::parse::parse_Command;
use utility::binary::{decode_little_endian_u32, encode_little_endian_u32};

use smallvec::{Array, SmallVec};

use std::io;
use std::iter;
use std::str;

/// A command for querying or modifying a database or the system.
#[derive(Clone, Copy, Debug)]
pub enum Command<'a> {
    Append{
        journal: &'a [u8],
        entry: &'a [u8],
    },
    CreateJournal {
        journal: &'a [u8],
    },
    Enumerate {
        journal: &'a [u8],
    },
    Foldl {
        journal: &'a [u8],
        using: &'a [u8],
        extension: &'a [u8],
    },
    Load {
        extension: &'a [u8],
        path: &'a [u8],
    },
    Version,
}

/// The status that a response part carries.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ResponsePartStatus {
    /// The response part includes successfully retrieved data.
    Success,

    /// The response part includes an error.
    Error,

    /// The response part indicates the end of the response.
    Finish,
}

aggregate_errors!(
    ReadCommandError {
        IoError(io::Error),
        Utf8Error(str::Utf8Error),
    }

    ReadResponsePartError {
        IoError(io::Error),
        UnknownStatusError(UnknownStatusError),
    }
);

unit_errors!(
    UnknownStatusError = "Unknown status",
);

/// Read a command.
pub fn read_command<'a, R, A>(r: &mut R, buffer: &'a mut SmallVec<A>)
                              -> Result<Command<'a>, ReadCommandError>
    where R: io::Read, A: Array<Item=u8> {
    read_string(r, buffer)?;

    // BUG: Support non-UTF-8 inputs.
    let buffer_str = str::from_utf8(buffer)?;
    // BUG: Return the error instead of panicing.
    let command = parse_Command(buffer_str).unwrap();

    Ok(command)
}

/// Write a command.
pub fn write_command<W>(w: &mut W, command: &[u8]) -> io::Result<()>
    where W: io::Write {
    write_string(w, command)?;
    Ok(())
}

/// Read a response part.
pub fn read_response_part<R, A>(r: &mut R, buffer: &mut SmallVec<A>)
                                -> Result<ResponsePartStatus,
                                          ReadResponsePartError>
    where R: io::Read, A: Array<Item=u8> {
    let status = match read_u8(r)? {
        0 => ResponsePartStatus::Success,
        1 => ResponsePartStatus::Error,
        2 => ResponsePartStatus::Finish,
        _ => {
            let error = UnknownStatusError;
            let error = ReadResponsePartError::UnknownStatusError(error);
            return Err(error);
        },
    };
    read_string(r, buffer)?;
    Ok(status)
}

/// Write a response part.
pub fn write_response_part<W>(w: &mut W, status: ResponsePartStatus,
                              buffer: &[u8]) -> io::Result<()>
    where W: io::Write {
    let status_code = match status {
        ResponsePartStatus::Success => 0,
        ResponsePartStatus::Error => 1,
        ResponsePartStatus::Finish => 2,
    };
    write_u8(w, status_code)?;
    write_string(w, buffer)?;
    Ok(())
}

/// Read a length-prefixed byte string, replacing the given buffer.
pub fn read_string<R, A>(r: &mut R, buffer: &mut SmallVec<A>) -> io::Result<()>
    where R: io::Read, A: Array<Item=u8> {
    let length = read_u32(r)? as usize;
    buffer.clear();
    buffer.extend(iter::repeat(0).take(length));
    r.read_exact(buffer)?;
    Ok(())
}

/// Write a length-prefixed byte string.
pub fn write_string<W>(w: &mut W, string: &[u8]) -> io::Result<()>
    where W: io::Write {
    write_u32(w, string.len() as u32)?;
    w.write_all(string)?;
    Ok(())
}

/// Read an 8-bit unsigned integer.
pub fn read_u8<R>(r: &mut R) -> io::Result<u8>
    where R: io::Read {
    let mut buffer = [0];
    r.read_exact(&mut buffer[..])?;
    Ok(buffer[0])
}

/// Write an 8-bit unsigned integer.
pub fn write_u8<W>(w: &mut W, value: u8) -> io::Result<()>
    where W: io::Write {
    w.write_all(&[value])?;
    Ok(())
}

/// Read a 32-bit unsigned integer.
pub fn read_u32<R>(r: &mut R) -> io::Result<u32>
    where R: io::Read {
    let mut buffer = [0; 4];
    r.read_exact(&mut buffer[..])?;
    let value = decode_little_endian_u32(&buffer);
    Ok(value)
}

/// Write a 32-bit unsigned integer.
pub fn write_u32<W>(w: &mut W, value: u32) -> io::Result<()>
    where W: io::Write {
    let buffer = encode_little_endian_u32(value);
    w.write_all(&buffer)?;
    Ok(())
}
