# Copyright (c) 2021, Matthew Booker
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License. Reserved.

import numpy as np


def normalize_angle(angle):
    """
    Normalize the angle to between [0, 2pi).

    Args:
    angle: float
        The angle to normalize in radians

    Returns
    -------
    The normalized angle in the range [0,2pi)

    """
    while angle >= 2*np.pi:
        angle -= 2*np.pi

    while angle < 0:
        angle += 2*np.pi

    return angle


def angle_difference(angle_1, angle_2, left_turn=None):
    """
    Calculate the difference between two angles based on a given direction.

    Args:
    angle_1: float
        The starting angle in radians
    angle_2: float
        The ending angle in radians
    left_turn: bool
        The direction of turn. True if left, false if right
        and None if smallest angular difference should be
        returned

    Returns
    -------
    The angular difference between the two angles according to
    the specified turn direction

    """
    if left_turn is None:
        dif = abs(angle_1 - angle_2)

        return dif if dif <= np.pi else 2 * np.pi - dif

    elif left_turn:

        if angle_2 >= angle_1:
            return abs(angle_1 - angle_2)
        else:
            return 2 * np.pi - abs(angle_1 - angle_2)

    else:
        if angle_1 >= angle_2:
            return abs(angle_1 - angle_2)
        else:
            return 2 * np.pi - abs(angle_1 - angle_2)


def interpolate_yaws(start_angle, end_angle, left_turn, steps):
    """
    Create equally spaced yaws between two angles.

    Args:
    start_angle: float
        The starting angle
    end_angle: float
        The ending angle
    left_turn: bool
        The direction of turn. True if left, False otherwise
    steps: int
        The number of yaws to generate between start and end
        angle

    Returns
    -------
    An array of yaws starting at start angle and ending at end
    angle with steps number of angles between them

    """
    if left_turn:
        if start_angle > end_angle:
            end_angle += 2 * np.pi
    else:
        if end_angle > start_angle:
            end_angle -= 2 * np.pi

    yaws = np.linspace(start_angle, end_angle, steps)
    yaws = np.vectorize(normalize_angle)(yaws)

    return yaws


def get_rotation_matrix(angle):
    """
    Return a rotation matrix that is equivalent to a 2D rotation of angle.

    Args:
    angle: float
        The angle to create a rotation matrix for

    Returns
    -------
    A 2x2 matrix representing a 2D rotation by angle

    """
    return np.array([[np.cos(angle), -np.sin(angle)],
                    [np.sin(angle), np.cos(angle)]])
