#![feature(yggdrasil_os)]

use std::{
    io::{self, stdout, Read, Write},
    os::fd::AsRawFd,
    path::PathBuf,
    process::ExitCode,
};

use clap::Parser;
use cross::io::{Poll, RawStdin, SerialPort, SerialPortSettings};

#[derive(Parser)]
struct Args {
    #[clap(short = 'B', default_value_t = 115200)]
    baud_rate: u32,
    device: PathBuf,
}

fn run(args: Args) -> io::Result<()> {
    let serial_options = SerialPortSettings {
        baud_rate: Some(args.baud_rate),
    };

    let mut poll = Poll::new()?;
    let mut serial = SerialPort::open(args.device, &serial_options)?;
    let mut stdout = stdout();
    let mut stdin = RawStdin::open()?;
    let mut buffer = [0; 1];

    let mut ctrl_a = false;

    poll.add(&serial)?;
    poll.add(&stdin)?;

    loop {
        let fd = poll.wait(None)?.unwrap();

        if fd == stdin.as_raw_fd() {
            if stdin.read(&mut buffer)? != 1 {
                break;
            }

            if ctrl_a {
                match buffer[0] {
                    b'x' => break,
                    _ => ctrl_a = false,
                }
            } else {
                if buffer[0] == 0x01 {
                    // Ctrl+A
                    ctrl_a = true;
                } else {
                    serial.write_all(&buffer)?;
                }
            }
        } else {
            if serial.read(&mut buffer)? != 1 {
                break;
            }

            stdout.write_all(&buffer).ok();
            stdout.flush().ok();
        }
    }

    Ok(())
}

fn main() -> ExitCode {
    let args = Args::parse();

    match run(args) {
        Ok(()) => ExitCode::SUCCESS,
        Err(error) => {
            eprintln!("{error}");
            ExitCode::FAILURE
        }
    }
}
