use crate::prelude::*;
use core::panic;
use std::ops::{Add, Index, Sub};

#[derive(Debug, Clone, Copy)]
pub struct Pos3 {
    pub(crate) x: Float,
    pub(crate) y: Float,
    pub(crate) z: Float,
}

impl Pos3 {
    pub fn new(x: Float, y: Float, z: Float) -> Self {
        Self { x, y, z }
    }

    pub fn x(self) -> Float {
        self.x
    }
    pub fn y(self) -> Float {
        self.y
    }
    pub fn z(self) -> Float {
        self.z
    }

    pub fn zero() -> Self {
        Self {
            x: 0.0,
            y: 0.0,
            z: 0.0,
        }
    }

    pub fn as_dir(self) -> Dir3 {
        Dir3 {
            x: self.x,
            y: self.y,
            z: self.z,
        }
    }

    pub fn from_barycentric(vertices: [Self; 3], coordinates: [Float; 3]) -> Self {
        Self {
            x: vertices[0].x() * coordinates[0]
                + vertices[1].x() * coordinates[1]
                + vertices[2].x() * coordinates[2],
            y: vertices[0].y() * coordinates[0]
                + vertices[1].y() * coordinates[1]
                + vertices[2].y() * coordinates[2],
            z: vertices[0].z() * coordinates[0]
                + vertices[1].z() * coordinates[1]
                + vertices[2].z() * coordinates[2],
        }
    }
}

impl Sub for Pos3 {
    type Output = Dir3;

    fn sub(self, rhs: Self) -> Self::Output {
        Dir3 {
            x: self.x - rhs.x,
            y: self.y - rhs.y,
            z: self.z - rhs.z,
        }
    }
}

impl Add<Dir3> for Pos3 {
    type Output = Pos3;

    fn add(self, rhs: Dir3) -> Self::Output {
        Pos3 {
            x: self.x + rhs.x,
            y: self.y + rhs.y,
            z: self.z + rhs.z,
        }
    }
}

impl Sub<Dir3> for Pos3 {
    type Output = Pos3;

    fn sub(self, rhs: Dir3) -> Self::Output {
        Pos3 {
            x: self.x - rhs.x,
            y: self.y - rhs.y,
            z: self.z - rhs.z,
        }
    }
}

impl Index<usize> for Pos3 {
    type Output = Float;

    fn index(&self, index: usize) -> &Self::Output {
        match index {
            0 => &self.x,
            1 => &self.y,
            2 => &self.z,
            _ => panic!("Index out of bound {}", index),
        }
    }
}
