"""
Elements
--------

These are the different Elements a field can contain.
"""
from dataclasses import dataclass

import numpy as np

from plofeld.utils.classes import Vector
from abc import abstractmethod


@dataclass
class Element:
    """Base Class for all elements."""
    location: Vector

    @abstractmethod
    def field_at(self, location: Vector):
        pass

    @abstractmethod
    def __hash__(self):
        pass

    @property
    def x(self):
        return self.location.x

    @property
    def y(self):
        return self.location.y

    @property
    def z(self):
        return self.location.z


@dataclass
class PointCharge(Element):
    """Description of a point charge at a certain location."""
    q: int

    # Plot args
    radius: float = 0.1  # also used as starting of field-lines
    color: str = None
    marker: str = 'default'
    linewidth: float = 0.

    def field_at(self, location: Vector) -> Vector:
        """Calculate the field generated by this charge at the desired location.
        With 1/(4*pi*eps0) = 1.
        """
        diff = location - self.location
        return self.q * diff / np.sum(np.square(diff))**1.5

    def is_within_radius(self, location: Vector, radius: float = None) -> bool:
        """Tells if location is within the drawing radius of the charge."""
        if radius is None:
            radius = self.radius
        return self.location.distance(location) < radius

    def __hash__(self):
        return hash((self.q, self.location))

    def __str__(self):
        return f"q: {self.q} at ({str(self.location)})"

    def __repr__(self):
        return f"PointCharge({str(self)})"
