# Copyright (C) 2012-2016 Julie Marchant <onpon4@riseup.net>
# 
# This file is part of the Pygame SGE.
# 
# The Pygame SGE is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# The Pygame SGE is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
# 
# You should have received a copy of the GNU Lesser General Public License
# along with the Pygame SGE.  If not, see <http://www.gnu.org/licenses/>.

"""
This module provides functions related to joystick input.
"""

from __future__ import division
from __future__ import absolute_import
from __future__ import print_function
from __future__ import unicode_literals

import pygame
import six

import sge
from sge import r


__all__ = ["refresh", "get_axis", "get_hat_x", "get_hat_y",
           "get_pressed", "get_value", "get_joysticks", "get_name",
           "get_id", "get_axes", "get_hats", "get_trackballs", "get_buttons"]


def refresh():
    """
    Refresh the SGE's knowledge of joysticks.

    Call this method to allow the SGE to use joysticks that were plugged
    in while the game was running.
    """
    r.game_joysticks = []
    r.game_js_names = {}
    r.game_js_ids = {}
    pygame.joystick.quit()
    pygame.joystick.init()

    if pygame.joystick.get_init():
        for i in six.moves.range(pygame.joystick.get_count()):
            joy = pygame.joystick.Joystick(i)
            joy.init()
            n = joy.get_name()
            r.game_joysticks.append(joy)
            r.game_js_names[i] = n
            if n not in r.game_js_ids:
                r.game_js_ids[n] = i


def get_axis(joystick, axis):
    """
    Return the position of a joystick axis as a float from ``-1`` to
    ``1``, where ``0`` is centered, ``-1`` is all the way to the left or
    up, and ``1`` is all the way to the right or down.  Return ``0`` if
    the requested joystick or axis does not exist.

    Arguments:

    - ``joystick`` -- The number of the joystick to check, where ``0``
      is the first joystick, or the name of the joystick to check.
    - ``axis`` -- The number of the axis to check, where ``0`` is the
      first axis of the joystick.
    """
    joystick = get_id(joystick)

    if (joystick is not None and joystick < len(r.game_joysticks) and
            axis < r.game_joysticks[joystick].get_numaxes()):
        return max(-1.0, min(r.game_joysticks[joystick].get_axis(axis), 1.0))
    else:
        return 0


def get_hat_x(joystick, hat):
    """
    Return the horizontal position of a joystick hat (d-pad).  Can be
    ``-1`` (left), ``0`` (centered), or ``1`` (right).  Return ``0`` if
    the requested joystick or hat does not exist.

    Arguments:

    - ``joystick`` -- The number of the joystick to check, where ``0``
      is the first joystick, or the name of the joystick to check.
    - ``hat`` -- The number of the hat to check, where ``0`` is the
      first hat of the joystick.
    """
    return r._get_hat(get_id(joystick), hat)[0]


def get_hat_y(joystick, hat):
    """
    Return the vertical position of a joystick hat (d-pad).  Can be
    ``-1`` (up), ``0`` (centered), or ``1`` (down).  Return ``0`` if the
    requested joystick or hat does not exist.

    Arguments:

    - ``joystick`` -- The number of the joystick to check, where ``0``
      is the first joystick, or the name of the joystick to check.
    - ``hat`` -- The number of the hat to check, where ``0`` is the
      first hat of the joystick.
    """
    return -r._get_hat(get_id(joystick), hat)[1]


def get_pressed(joystick, button):
    """
    Return whether or not a joystick button is pressed, or
    :const:`False` if the requested joystick or button does not exist.

    Arguments:

    - ``joystick`` -- The number of the joystick to check, where ``0``
      is the first joystick, or the name of the joystick to check.
    - ``button`` -- The number of the button to check, where ``0`` is
      the first button of the joystick.
    """
    joystick = get_id(joystick)

    if (joystick is not None and joystick < len(r.game_joysticks) and
            button < r.game_joysticks[joystick].get_numbuttons()):
        return r.game_joysticks[joystick].get_button(button)
    else:
        return False


def get_value(joystick, input_type, input_id):
    """
    Return the value of any joystick control.  This function makes it
    possible to treat all joystick inputs the same way, which can be
    used to simplify things like control customization.

    Arguments:

    - ``joystick`` -- The number of the joystick to check, where ``0``
      is the first joystick, or the name of the joystick to check.
    - ``input_type`` -- The type of joystick control to check.  Can be
      one of the following:

      - ``"axis-"`` -- The tilt of an axis to the left or up as a float,
        where ``0`` is not tilted in this direction at all and ``1`` is
        tilted entirely in this direction.
      - ``"axis+"`` -- The tilt of an axis to the right or down as a
        float, where ``0`` is not tilted in this direction at all and
        ``1`` is tilted entirely in this direction.
      - ``"axis0"`` -- The distance of the tilt of an axis from the
        nearest extreme edge, where ``0`` is tilted entirely in a
        direction and ``1`` is completely centered.
      - ``"hat_left"`` -- Whether or not the left direction of a
        joystick hat (d-pad) is pressed.
      - ``"hat_right"`` -- Whether or not the right direction of a
        joystick hat (d-pad) is pressed.
      - ``"hat_center_x" -- Whether or not a joystick hat (d-pad) is
        horizontally centered.
      - ``"hat_up"`` -- Whether or not the up direction of a joystick
        hat (d-pad) is pressed.
      - ``"hat_down"`` -- Whether or not the down direction of a
        joystick hat (d-pad) is pressed.
      - ``"hat_center_y" -- Whether or not a joystick hat (d-pad) is
        vertically centered.
      - ``"button"`` -- Whether or not a joystick button is pressed.

      If an invalid type is specified, :const:`None` is returned.

    - ``input_id`` -- The number of the control to check, where ``0`` is
      the first control of its type on the joystick.
    """
    if input_type == "axis-":
        return abs(min(0, get_axis(joystick, input_id)))
    elif input_type == "axis+":
        return abs(max(0, get_axis(joystick, input_id)))
    elif input_type == "axis0":
        return 1 - abs(get_axis(joystick, input_id))
    elif input_type == "hat_left":
        return get_hat_x(joystick, input_id) == -1
    elif input_type == "hat_right":
        return get_hat_x(joystick, input_id) == 1
    elif input_type == "hat_center_x":
        return get_hat_x(joystick, input_id) == 0
    elif input_type == "hat_up":
        return get_hat_y(joystick, input_id) == -1
    elif input_type == "hat_down":
        return get_hat_y(joystick, input_id) == 1
    elif input_type == "hat_center_y":
        return get_hat_y(joystick, input_id) == 0
    elif input_type == "button":
        return get_pressed(joystick, input_id)
    else:
        return None


def get_joysticks():
    """Return the number of joysticks available."""
    return len(r.game_joysticks)


def get_name(joystick):
    """
    Return the name of a joystick, or :const:`None` if the requested
    joystick does not exist.

    Arguments:

    - ``joystick`` -- The number of the joystick to check, where ``0``
      is the first joystick, or the name of the joystick to check.
    """
    if isinstance(joystick, six.integer_types):
        return r.game_js_names.setdefault(joystick)
    elif joystick in r.game_js_names.values():
        return joystick
    else:
        return None


def get_id(joystick):
    """
    Return the number of a joystick, where ``0`` is the first joystick,
    or :const:`None` if the requested joystick does not exist.

    Arguments:

    - ``joystick`` -- The number of the joystick to check, where ``0``
      is the first joystick, or the name of the joystick to check.
    """
    if not isinstance(joystick, six.integer_types):
        return r.game_js_ids.setdefault(joystick)
    elif joystick in r.game_js_names:
        return joystick
    else:
        return None


def get_axes(joystick):
    """
    Return the number of axes available on a joystick, or ``0`` if the
    requested joystick does not exist.

    Arguments:

    - ``joystick`` -- The number of the joystick to check, where ``0``
      is the first joystick, or the name of the joystick to check.
    """
    joystick = get_id(joystick)

    if joystick is not None and joystick < len(r.game_joysticks):
        return r.game_joysticks[joystick].get_numaxes()
    else:
        return 0


def get_hats(joystick):
    """
    Return the number of hats (d-pads) available on a joystick, or ``0``
    if the requested joystick does not exist.

    Arguments:

    - ``joystick`` -- The number of the joystick to check, where ``0``
      is the first joystick, or the name of the joystick to check.
    """
    joystick = get_id(joystick)

    if joystick is not None and joystick < len(r.game_joysticks):
        return r.game_joysticks[joystick].get_numhats()
    else:
        return 0


def get_trackballs(joystick):
    """
    Return the number of trackballs available on a joystick, or ``0`` if
    the requested joystick does not exist.

    Arguments:

    - ``joystick`` -- The number of the joystick to check, where ``0``
      is the first joystick, or the name of the joystick to check.
    """
    joystick = get_id(joystick)

    if joystick is not None and joystick < len(r.game_joysticks):
        return r.game_joysticks[joystick].get_numballs()
    else:
        return 0


def get_buttons(joystick):
    """
    Return the number of buttons available on a joystick, or ``0`` if
    the requested joystick does not exist.

    Arguments:

    - ``joystick`` -- The number of the joystick to check, where ``0``
      is the first joystick, or the name of the joystick to check.
    """
    joystick = get_id(joystick)

    if joystick is not None and joystick < len(r.game_joysticks):
        return r.game_joysticks[joystick].get_numbuttons()
    else:
        return 0
