use std::{
    env,
    io::{Read, Write},
    time::Duration,
};

use rand::{rngs::ThreadRng, thread_rng, Rng};
use serial::SerialPort;

struct TriangleIterator {
    points: [(u32, u32); 3],
    cur_x: u32,
    cur_y: u32,
    // used only so we always output the points first
    i: usize,

    rng: ThreadRng,
}

impl TriangleIterator {
    pub fn new(width: u32, height: u32) -> Self {
        let p1 = (0, 0);
        let p2 = (width, 0);
        let p3 = (width / 2, height);

        Self {
            points: [p1, p2, p3],
            cur_x: p1.0,
            cur_y: p1.1,
            i: 0,
            rng: thread_rng(),
        }
    }
}

impl Iterator for TriangleIterator {
    type Item = (u32, u32);

    fn next(&mut self) -> Option<Self::Item> {
        if self.i < self.points.len() {
            let ret = self.points[self.i];
            self.i += 1;

            Some(ret)
        } else {
            let p = self.points[self.rng.gen_range(0..self.points.len())];
            self.cur_x = (self.cur_x + p.0) / 2;
            self.cur_y = (self.cur_y + p.1) / 2;

            Some((self.cur_x, self.cur_y))
        }
    }
}

fn main() -> anyhow::Result<()> {
    let args: Vec<_> = env::args().collect();
    if args.len() != 2 {
        println!("Usage: {} /dev/tty***", args[0]);
        return Ok(());
    }

    let mut ser = serial::open(&args[1])?;
    ser.reconfigure(&|settings| {
        settings.set_baud_rate(serial::BaudRate::Baud9600)?;

        Ok(())
    })?;
    ser.set_timeout(Duration::from_secs(3600))?;

    let triangle = TriangleIterator::new(10300, 7650);

    write!(&mut ser, "IN; SP 5; VS 40;")?;

    let mut buf = [0];
    for (i, (x, y)) in triangle.enumerate() {
        println!("{}", i);

        // wait until our buffer is empty
        write!(&mut ser, "OA;")?;
        loop {
            ser.read_exact(&mut buf)?;
            if buf == *b"\r" {
                break;
            }
        }

        write!(&mut ser, "PU {} {}; PD;", x, y)?;
    }

    Ok(())
}