/src/console/serial.rs
use arraydeque::ArrayDeque;
use core::fmt;
use nb::block;
use embedded_hal::serial::*;
use atsam3xa::interrupt;
use cortex_m::interrupt as cm_interrupt;

use crate::console::key::*;
use crate::console::traits::*;
use crate::console::get_global_console;

const BUFFER_SIZE: usize = 16;

pub struct SerialTerminal {
    input_buffer: ArrayDeque<[u8; BUFFER_SIZE], arraydeque::Saturating>,
    uart: atsam3xa_hal::uart::UART,
}

impl SerialTerminal {
    pub fn new(uart: atsam3xa_hal::uart::UART) -> SerialTerminal {
        SerialTerminal {
            input_buffer: ArrayDeque::new(),
            uart,
        }
    }

    pub fn handle_interrupt(&mut self) {
        if let Ok(b) = self.uart.read() {
            self.input_buffer.push_back(b).ok();
        }
    }
}

impl ConsoleIO for SerialTerminal {
    fn readkey(&mut self) -> Option<Key> {
        cm_interrupt::free(|_| {
            let b = self.input_buffer.pop_front()?;
            match b {
                0x1b => {
                    if self.input_buffer.len() >= 2 {
                        // escape sequence incoming
                        let b = self.input_buffer.pop_front().unwrap();
                        if b != 0x5b {
                            return None;
                        }

                        let b = self.input_buffer.pop_front().unwrap();
                        match b {
                            0x41 => Some(Key::Command(Command::Up)),
                            0x42 => Some(Key::Command(Command::Down)),
                            0x43 => Some(Key::Command(Command::Right)),
                            0x44 => Some(Key::Command(Command::Left)),
                            _ => None,
                        }
                    } else {
                        // not enough characters yet, put the ESC back in the buffer
                        self.input_buffer.push_front(0x1b).unwrap();
                        None
                    }
                },
                0x0d => Some(Key::Command(Command::Enter)),
                0x08 => Some(Key::Command(Command::Backspace)),
                0x09 => Some(Key::Command(Command::Tab)),
                0x03 => Some(Key::Command(Command::Break)),
                b => Some(Key::Char(b as char)),
            }
        })
    }

    fn readline(&mut self, buf: &mut [u8]) -> usize {
        let mut c = 0;
        let mut cursor = 0;

        loop {
            match self.readkey() {
                Some(k) => match k {
                    Key::Char(x) => {
                        if c == buf.len() {
                            // Don't overflow the buffer
                            continue;
                        }

                        // FIXME proper unicode output
                        self.writeb(x as u8);
                        if cursor < c {
                            // reprint everything to the right of the cursor
                            for i in cursor..c {
                                self.writeb(buf[i]);
                            }
                            for i in (cursor..c).rev() {
                                buf[i + 1] = buf[i];
                                self.writeb(0x08);
                            }
                        }
                        buf[cursor] = x as u8;

                        c += 1;
                        cursor += 1;
                    },
                    Key::Command(x) => match x {
                        Command::Backspace => {
                            if c == 0 || cursor == 0 {
                                // buffer empty
                                continue;
                            }
                            self.writeb(0x08);
                            for i in cursor..c {
                                // reprint everything from the cursor rightward
                                self.writeb(buf[i]);
                            }
                            self.writeb(0x20);
                            for i in cursor..c {
                                buf[i - 1] = buf[i];
                                self.writeb(0x08);
                            }
                            self.writeb(0x08);
                            c -= 1;
                            cursor -= 1;
                        },
                        Command::Enter => {
                            break;
                        },
                        Command::Left => {
                            if cursor == 0 {
                                continue;
                            }
                            cursor -= 1;
                            self.write("\x1b[1D");
                        },
                        Command::Right => {
                            if cursor == c {
                                continue;
                            }
                            cursor += 1;
                            self.write("\x1b[1C");
                        },
                        _ => continue,
                    }
                },
                None => continue,
            };

        }

        c
    }

    fn discard(&mut self) {
        cm_interrupt::free(|_| {
            self.input_buffer.clear();
        });
    }

    fn pos(&mut self, x: usize, y: usize) {
        use core::fmt::Write;
        assert!(x < 40 && y < 25);
        write!(self, "\x1b[{};{}H", y, x).ok();
    }

    fn writeb(&mut self, b: u8) {
        block!(self.uart.write(b)).ok();
    }

    fn write<T: AsRef<[u8]>>(&mut self, a: T) {
        let bytes = a.as_ref();
        for b in bytes {
            self.writeb(*b);
        }
    }

    fn clear(&mut self) {
        self.write(&[0x1b, b'[', b'2', b'J', 0x1b, b'[', b'H']);
    }
}

impl fmt::Write for SerialTerminal {
    fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
        self.write(s);
        Ok(())
    }
}

#[interrupt]
fn UART() {
    get_global_console().handle_interrupt();
}