use std::fmt::Write as _;
use std::time::Duration;
use crate::utils::round_precision;
#[derive(Debug, Clone, PartialEq)]
pub struct G0 {
    pub x: Option<f64>,
    pub y: Option<f64>,
    pub z: Option<f64>,
}
impl G0 {
    pub fn to_gcode(&self) -> String {
        let mut command = "G0".to_string();
        if let Some(x) = self.x {
            let _ = write!(command, " X{}", round_precision(x));
        }
        if let Some(y) = self.y {
            let _ = write!(command, " Y{}", round_precision(y));
        }
        if let Some(z) = self.z {
            let _ = write!(command, " Z{}", round_precision(z));
        }
        command
    }
}
#[derive(Debug, Clone, PartialEq)]
pub struct G1 {
    pub x: Option<f64>,
    pub y: Option<f64>,
    pub z: Option<f64>,
    pub f: Option<f64>,
}
impl G1 {
    pub fn to_gcode(&self) -> String {
        let mut command = "G1".to_string();
        if let Some(x) = self.x {
            let _ = write!(command, " X{}", round_precision(x));
        }
        if let Some(y) = self.y {
            let _ = write!(command, " Y{}", round_precision(y));
        }
        if let Some(z) = self.z {
            let _ = write!(command, " Z{}", round_precision(z));
        }
        if let Some(f) = self.f {
            let _ = write!(command, " F{}", round_precision(f));
        }
        command
    }
}
#[derive(Debug, Clone, PartialEq)]
pub struct G2 {
    pub x: Option<f64>,
    pub y: Option<f64>,
    pub z: Option<f64>,
    pub i: Option<f64>,
    pub j: Option<f64>,
    pub k: Option<f64>,
    pub r: Option<f64>,
    pub p: Option<u32>,
    pub f: Option<f64>,
}
impl G2 {
    pub fn to_gcode(&self) -> String {
        let mut command = "G2".to_string();
        if let Some(x) = self.x {
            let _ = write!(command, " X{}", round_precision(x));
        }
        if let Some(y) = self.y {
            let _ = write!(command, " Y{}", round_precision(y));
        }
        if let Some(z) = self.z {
            let _ = write!(command, " Z{}", round_precision(z));
        }
        if let Some(r) = self.r {
            let _ = write!(command, " R{}", round_precision(r));
        } else {
            if let Some(i) = self.i {
                let _ = write!(command, " I{}", round_precision(i));
            }
            if let Some(j) = self.j {
                let _ = write!(command, " J{}", round_precision(j));
            }
            if let Some(k) = self.k {
                let _ = write!(command, " K{}", round_precision(k));
            }
        }
        if let Some(p) = self.p {
            let _ = write!(command, " P{}", round_precision(p.into()));
        }
        if let Some(f) = self.f {
            let _ = write!(command, " F{}", round_precision(f));
        }
        command
    }
}
#[derive(Debug, Clone, PartialEq)]
pub struct G3 {
    pub x: Option<f64>,
    pub y: Option<f64>,
    pub z: Option<f64>,
    pub i: Option<f64>,
    pub j: Option<f64>,
    pub k: Option<f64>,
    pub r: Option<f64>,
    pub p: Option<u32>,
    pub f: Option<f64>,
}
impl G3 {
    pub fn to_gcode(&self) -> String {
        let mut command = "G3".to_string();
        if let Some(x) = self.x {
            let _ = write!(command, " X{}", round_precision(x));
        }
        if let Some(y) = self.y {
            let _ = write!(command, " Y{}", round_precision(y));
        }
        if let Some(z) = self.z {
            let _ = write!(command, " Z{}", round_precision(z));
        }
        if let Some(r) = self.r {
            let _ = write!(command, " R{}", round_precision(r));
        } else {
            if let Some(i) = self.i {
                let _ = write!(command, " I{}", round_precision(i));
            }
            if let Some(j) = self.j {
                let _ = write!(command, " J{}", round_precision(j));
            }
            if let Some(k) = self.k {
                let _ = write!(command, " K{}", round_precision(k));
            }
        }
        if let Some(p) = self.p {
            let _ = write!(command, " P{}", round_precision(p.into()));
        }
        if let Some(f) = self.f {
            let _ = write!(command, " F{}", round_precision(f));
        }
        command
    }
}
#[derive(Debug, Clone, PartialEq)]
pub struct G4 {
    pub p: Duration,
}
impl G4 {
    pub fn to_gcode(&self) -> String {
        format!("G4 P{}", round_precision(self.p.as_secs_f64()))
    }
}
#[derive(Debug, Clone, PartialEq)]
pub struct G17 {}
impl G17 {
    pub fn to_gcode(&self) -> String {
        "G17".to_string()
    }
}
#[derive(Debug, Clone, PartialEq)]
pub struct G18 {}
impl G18 {
    pub fn to_gcode(&self) -> String {
        "G18".to_string()
    }
}
#[derive(Debug, Clone, PartialEq)]
pub struct G19 {}
impl G19 {
    pub fn to_gcode(&self) -> String {
        "G19".to_string()
    }
}
#[derive(Debug, Clone, PartialEq)]
pub struct G20 {}
impl G20 {
    pub fn to_gcode(&self) -> String {
        "G20".to_string()
    }
}
#[derive(Debug, Clone, PartialEq)]
pub struct G21 {}
impl G21 {
    pub fn to_gcode(&self) -> String {
        "G21".to_string()
    }
}
#[derive(Debug, Clone, PartialEq)]
pub struct G43 {
    pub h: u32,
}
impl G43 {
    pub fn to_gcode(&self) -> String {
        format!("G43 H{}", self.h)
    }
}
#[derive(Debug, Clone, PartialEq)]
pub struct F {
    pub x: f64,
}
impl F {
    pub fn to_gcode(&self) -> String {
        format!("F{}", round_precision(self.x))
    }
}
#[derive(Debug, Clone, PartialEq)]
pub struct S {
    pub x: f64,
}
impl S {
    pub fn to_gcode(&self) -> String {
        format!("S{}", round_precision(self.x))
    }
}
#[derive(Debug, Clone, PartialEq)]
pub struct M0 {}
impl M0 {
    pub fn to_gcode(&self) -> String {
        "M0".to_string()
    }
}
#[derive(Debug, Clone, PartialEq)]
pub struct M2 {}
impl M2 {
    pub fn to_gcode(&self) -> String {
        "M2".to_string()
    }
}
#[derive(Debug, Clone, PartialEq)]
pub struct M3 {}
impl M3 {
    pub fn to_gcode(&self) -> String {
        "M3".to_string()
    }
}
#[derive(Debug, Clone, PartialEq)]
pub struct M4 {}
impl M4 {
    pub fn to_gcode(&self) -> String {
        "M4".to_string()
    }
}
#[derive(Debug, Clone, PartialEq)]
pub struct M5 {}
impl M5 {
    pub fn to_gcode(&self) -> String {
        "M5".to_string()
    }
}
#[derive(Debug, Clone, PartialEq)]
pub struct M6 {
    pub t: u8,
}
impl M6 {
    pub fn to_gcode(&self) -> String {
        format!("T{} M6", self.t)
    }
}
#[derive(Debug, Clone, PartialEq)]
pub struct Empty {}
impl Empty {
    pub fn to_gcode(&self) -> String {
        "".to_string()
    }
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct Comment {
    pub text: String,
}
impl Comment {
    pub fn to_gcode(&self) -> String {
        if self.text.is_empty() {
            return String::new();
        }
        format!(";({})", self.text)
    }
}
#[derive(Debug, Clone, PartialEq)]
pub struct Message {
    pub text: String,
}
impl Message {
    pub fn to_gcode(&self) -> String {
        format!("(MSG,{})", self.text)
    }
}
#[derive(Debug, Clone, PartialEq)]
pub enum Instruction {
    G0(G0),
    G1(G1),
    G2(G2),
    G3(G3),
    G4(G4),
    G17(G17),
    G18(G18),
    G19(G19),
    G20(G20),
    G21(G21),
    G43(G43),
    F(F),
    S(S),
    M0(M0),
    M2(M2),
    M3(M3),
    M4(M4),
    M5(M5),
    M6(M6),
    Empty(Empty),
    Comment(Comment),
    Message(Message),
}
impl Instruction {
    pub fn to_gcode(&self) -> String {
        match self {
            Instruction::G0(instruction) => instruction.to_gcode(),
            Instruction::G1(instruction) => instruction.to_gcode(),
            Instruction::G2(instruction) => instruction.to_gcode(),
            Instruction::G3(instruction) => instruction.to_gcode(),
            Instruction::G4(instruction) => instruction.to_gcode(),
            Instruction::G17(instruction) => instruction.to_gcode(),
            Instruction::G18(instruction) => instruction.to_gcode(),
            Instruction::G19(instruction) => instruction.to_gcode(),
            Instruction::G20(instruction) => instruction.to_gcode(),
            Instruction::G21(instruction) => instruction.to_gcode(),
            Instruction::G43(instruction) => instruction.to_gcode(),
            Instruction::F(instruction) => instruction.to_gcode(),
            Instruction::S(instruction) => instruction.to_gcode(),
            Instruction::M0(instruction) => instruction.to_gcode(),
            Instruction::M2(instruction) => instruction.to_gcode(),
            Instruction::M3(instruction) => instruction.to_gcode(),
            Instruction::M4(instruction) => instruction.to_gcode(),
            Instruction::M5(instruction) => instruction.to_gcode(),
            Instruction::M6(instruction) => instruction.to_gcode(),
            Instruction::Empty(instruction) => instruction.to_gcode(),
            Instruction::Comment(instruction) => instruction.to_gcode(),
            Instruction::Message(instruction) => instruction.to_gcode(),
        }
    }
}