from math import sqrt, ceil, floor
from typing import Any
from sys import path
from os.path import abspath, dirname

path.append(dirname(abspath("")))

from Scalar import Scalar


# the_3D 向量类
class Vector3D:
    """三维向量类"""

    def __init__(self, x: Scalar, y: Scalar, z: Scalar) -> None:
        if not (isinstance(x, Scalar) and isinstance(y, Scalar) and isinstance(z, Scalar)):
            raise TypeError("向量分量必须是Scalar类型")
        self.x = x
        self.y = y
        self.z = z

    def __add__(self, other: 'Vector3D') -> 'Vector3D':
        if not isinstance(other, Vector3D):
            raise TypeError("只能与Vector3D类型相加")
        return Vector3D(self.x + other.x, self.y + other.y, self.z + other.z)

    def __sub__(self, other: 'Vector3D') -> 'Vector3D':
        if not isinstance(other, Vector3D):
            raise TypeError("只能与Vector3D类型相减")
        return Vector3D(self.x - other.x, self.y - other.y, self.z - other.z)

    def __mul__(self, scalar: Scalar) -> 'Vector3D':
        if not isinstance(scalar, Scalar):
            raise TypeError("只能与Scalar类型相乘")
        return Vector3D(self.x * scalar, self.y * scalar, self.z * scalar)

    def __rmul__(self, scalar: Scalar) -> 'Vector3D':
        return self.__mul__(scalar)

    def __truediv__(self, scalar: Scalar) -> 'Vector3D':
        if not isinstance(scalar, Scalar):
            raise TypeError("只能与Scalar类型相除")
        if scalar == 0:
            raise ZeroDivisionError("除数不能为0")
        return Vector3D(self.x / scalar, self.y / scalar, self.z / scalar)

    def __floordiv__(self, scalar: Scalar) -> 'Vector3D':
        if not isinstance(scalar, Scalar):
            raise TypeError("只能与Scalar类型进行整除")
        if scalar == 0:
            raise ZeroDivisionError("除数不能为0")
        return Vector3D(self.x // scalar, self.y // scalar, self.z // scalar)

    def __mod__(self, scalar: Scalar) -> 'Vector3D':
        if not isinstance(scalar, Scalar):
            raise TypeError("只能与Scalar类型取模")
        if scalar == 0:
            raise ZeroDivisionError("除数不能为0")
        return Vector3D(self.x % scalar, self.y % scalar, self.z % scalar)

    def __str__(self) -> str:
        return f"Vector3D({self.x}, {self.y}, {self.z})"

    def __repr__(self) -> str:
        return f"Vector3D(x={self.x!r}, y={self.y!r}, z={self.z!r})"

    def __eq__(self, other: Any) -> bool:
        if not isinstance(other, Vector3D):
            return False
        return self.x == other.x and self.y == other.y and self.z == other.z

    def __ne__(self, other: Any) -> bool:
        return not self.__eq__(other)

    def __len__(self) -> int:
        return 3

    def __abs__(self) -> float:
        """计算向量模长"""
        return sqrt(float(self.x ** 2 + self.y ** 2 + self.z ** 2))

    @property
    def mode(self) -> float:
        """向量模长属性"""
        return self.__abs__()

    def __bool__(self) -> bool:
        return self.x != 0 or self.y != 0 or self.z != 0

    def ceil(self) -> 'Vector3D':
        """对各分量向上取整"""
        return Vector3D(
            Scalar(ceil(self.x)),
            Scalar(ceil(self.y)),
            Scalar(ceil(self.z))
        )

    def floor(self) -> 'Vector3D':
        """对各分量向下取整"""
        return Vector3D(
            Scalar(floor(self.x)),
            Scalar(floor(self.y)),
            Scalar(floor(self.z))
        )

    def normalize(self) -> 'Vector3D':
        """向量归一化"""
        mod = self.mode
        if mod == 0:
            raise ValueError("零向量无法归一化")
        return self / Scalar(mod)

    def dot(self, other: 'Vector3D') -> Scalar:
        """向量点积"""
        if not isinstance(other, Vector3D):
            raise TypeError("点积运算需要两个Vector3D对象")
        return self.x * other.x + self.y * other.y + self.z * other.z

    def cross(self, other: 'Vector3D') -> 'Vector3D':
        """向量叉积"""
        if not isinstance(other, Vector3D):
            raise TypeError("叉积运算需要两个Vector3D对象")
        return Vector3D(
            self.y * other.z - self.z * other.y,
            self.z * other.x - self.x * other.z,
            self.x * other.y - self.y * other.x
        )

    def __iter__(self):
        yield self.x
        yield self.y
        yield self.z


# the_3D 基向量
base_vector3D_i = Vector3D(Scalar(1), Scalar(0), Scalar(0))
base_vector3D_j = Vector3D(Scalar(0), Scalar(1), Scalar(0))
base_vector3D_k = Vector3D(Scalar(0), Scalar(0), Scalar(1))


def create_vector3D(x: Scalar, y: Scalar, z: Scalar) -> Vector3D:
    """创建三维向量的工厂函数"""
    return Vector3D(x, y, z)