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

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

from Scalar import Scalar


# the_2D 向量类
class Vector2D:
    """二维向量类"""

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

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

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

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

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

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

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

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

    def __pos__(self) -> 'Vector2D':
        return Vector2D(+self.x, +self.y)

    def __neg__(self) -> 'Vector2D':
        return Vector2D(-self.x, -self.y)

    def __iadd__(self, other: 'Vector2D') -> 'Vector2D':
        return self + other

    def __isub__(self, other: 'Vector2D') -> 'Vector2D':
        return self - other

    def __imul__(self, scalar: Scalar) -> 'Vector2D':
        return self * scalar

    def __itruediv__(self, scalar: Scalar) -> 'Vector2D':
        return self / scalar

    def __ifloordiv__(self, scalar: Scalar) -> 'Vector2D':
        return self // scalar

    def __imod__(self, scalar: Scalar) -> 'Vector2D':
        return self % scalar

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

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

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

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

    def __len__(self) -> int:
        return 2

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

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

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

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

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

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

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

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


# the_2D 基向量
base_vector2D_i = Vector2D(Scalar(1), Scalar(0))
base_vector2D_j = Vector2D(Scalar(0), Scalar(1))


def create_vector2D(x: Scalar, y: Scalar) -> Vector2D:
    """创建二维向量的工厂函数"""
    return Vector2D(x, y)