import math
from WonderPy.core.wwConstants import WWRobotConstants
from .wwSensorBase import WWSensorBase
from WonderPy.util import wwMath

_rcv = WWRobotConstants.RobotComponentValues
_expected_json_fields = (
    _rcv.WW_SENSOR_VALUE_AXIS_X,
    _rcv.WW_SENSOR_VALUE_AXIS_Y,
    _rcv.WW_SENSOR_VALUE_AXIS_Z,
)


class WWSensorAccelerometer(WWSensorBase):
    """
    Measures linear acceleration along the 3 primary axes. Units are in gravities (9.8m/s/s).
    A robot sitting flat and at rest will have an acceleration of positive 1 on the Z axis.
    A robot being pushed suddenly forward will register a positive acceleration on the Y axis.
    (followed by a negative acceleration when it stops accelerating)

    This sensor can be fairly noisy, and naturally includes the 'acceleration' due to gravity.
    Extensions to this might include low-pass filtering to smooth the data,
    and possibly subtracting out the low-pass value to subtract out gravity.
    """

    def __init__(self, robot):
        super(WWSensorAccelerometer, self).__init__(robot)
        self._x = 0
        self._y = 0
        self._z = 0

    def _important_field_names(self):
        return 'x', 'y', 'z'

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

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

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

    @staticmethod
    def one_gravity_cm_s_s():
        return 980.665

    def parse(self, single_component_dictionary):
        if not self.check_fields_exist(single_component_dictionary, _expected_json_fields):
            return

        x = single_component_dictionary[_rcv.WW_SENSOR_VALUE_AXIS_X]
        y = single_component_dictionary[_rcv.WW_SENSOR_VALUE_AXIS_Y]
        z = single_component_dictionary[_rcv.WW_SENSOR_VALUE_AXIS_Z]

        x, y = wwMath.coords_json_to_api_pos(x, y)

        self._x = x
        self._y = y
        self._z = z

        self._valid = True

    def degrees_z_yz(self):
        """
        degrees away from z axis in the yz plane. note undefined if yz is horizontal
        """
        return math.atan2(self.y, self.z) * math.degrees(1)

    def degrees_y_yz(self):
        """
        degrees away from y axis in the yz plane. note undefined if yz is horizontal
        """
        return math.atan2(self.z, self.y) * math.degrees(1)

    def degrees_z_xz(self):
        """
        degrees away from z axis in the xz plane. note undefined if xz is horizontal
        """
        return math.atan2(self.x, self.z) * math.degrees(1)

    def degrees_x_xz(self):
        """
        degrees away from x axis in the xz plane. note undefined if xz is horizontal
        """
        return math.atan2(self.z, self.x) * math.degrees(1)

    def degrees_y_xy(self):
        """
        degrees away from y axis in the xy plane. note undefined if xy is horizontal
        """
        return math.atan2(self.x, self.y) * math.degrees(1)

    def degrees_x_xy(self):
        """
        degrees away from y axis in the xy plane. note undefined if xy is horizontal
        """
        return math.atan2(self.y, self.x) * math.degrees(1)