use std::{
    fs::{File, OpenOptions},
    io::{self, Read, Write},
    os::fd::{AsRawFd, IntoRawFd, RawFd},
    path::Path,
};

use runtime::{abi::io::TerminalOptions, rt::io::terminal::set_terminal_options};

use crate::{io::SerialPortSettings, sys::SerialPort};

pub struct SerialPortImpl(File);

impl SerialPort for SerialPortImpl {
    fn open<P: AsRef<Path>>(device: P, options: &SerialPortSettings) -> io::Result<Self> {
        let file = OpenOptions::new().read(true).write(true).open(device)?;

        if let Some(baud_rate) = options.baud_rate {
            let termios = TerminalOptions {
                baud_rate,
                ..TerminalOptions::raw_input()
            };

            set_terminal_options(file.as_raw_fd(), &termios)?;
        }

        Ok(Self(file))
    }
}

impl Read for SerialPortImpl {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.0.read(buf)
    }
}

impl Write for SerialPortImpl {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.0.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.0.flush()
    }
}

impl AsRawFd for SerialPortImpl {
    fn as_raw_fd(&self) -> RawFd {
        self.0.as_raw_fd()
    }
}

impl IntoRawFd for SerialPortImpl {
    fn into_raw_fd(self) -> RawFd {
        self.0.into_raw_fd()
    }
}
