/*
 * Copyright (C) 2018-2020 std::gregwar, Solarus - http://www.solarus-games.org
 *
 * Solarus is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Solarus 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include "solarus/lua/ExportableToLua.h"
#include "solarus/core/EnumInfo.h"

#include <map>
#include <memory>
#include <SDL_events.h>
#include <SDL_joystick.h>

namespace Solarus {
struct SDL_Controller_Deleter {
    void operator()(SDL_GameController* controller) {
        SDL_GameControllerClose(controller);
    }
};

using SDL_GameControllerUniquePtr = std::unique_ptr<SDL_GameController,SDL_Controller_Deleter>;

struct SDL_Joystick_Deleter {
    void operator()(SDL_Joystick* joystick) {
        SDL_JoystickClose(joystick);
    }
};

using SDL_JoystickUniquePtr = std::unique_ptr<SDL_Joystick,SDL_Joystick_Deleter>;

enum class JoyPadAxis {
    INVALID = SDL_CONTROLLER_AXIS_INVALID,
    LEFT_X = SDL_CONTROLLER_AXIS_LEFTX,
    LEFT_Y = SDL_CONTROLLER_AXIS_LEFTY,
    RIGHT_X = SDL_CONTROLLER_AXIS_RIGHTX,
    RIGHT_Y = SDL_CONTROLLER_AXIS_RIGHTY,
    TRIGGER_LEFT = SDL_CONTROLLER_AXIS_TRIGGERLEFT,
    TRIGGER_RIGHT = SDL_CONTROLLER_AXIS_TRIGGERRIGHT,
    MAX = SDL_CONTROLLER_AXIS_MAX
};

enum class JoyPadButton {
    INVALID = SDL_CONTROLLER_BUTTON_INVALID,
    A = SDL_CONTROLLER_BUTTON_A,
    B = SDL_CONTROLLER_BUTTON_B,
    X = SDL_CONTROLLER_BUTTON_X,
    Y = SDL_CONTROLLER_BUTTON_Y,
    BACK = SDL_CONTROLLER_BUTTON_BACK,
    GUIDE = SDL_CONTROLLER_BUTTON_GUIDE,
    START = SDL_CONTROLLER_BUTTON_START,
    LEFT_STICK = SDL_CONTROLLER_BUTTON_LEFTSTICK,
    RIGHT_STICK = SDL_CONTROLLER_BUTTON_RIGHTSTICK,
    LEFT_SHOULDER = SDL_CONTROLLER_BUTTON_LEFTSHOULDER,
    RIGHT_SHOULDER = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER,
    DPAD_UP = SDL_CONTROLLER_BUTTON_DPAD_UP,
    DPAD_DOWN = SDL_CONTROLLER_BUTTON_DPAD_DOWN,
    DPAD_LEFT = SDL_CONTROLLER_BUTTON_DPAD_LEFT,
    DPAD_RIGHT = SDL_CONTROLLER_BUTTON_DPAD_RIGHT,
    MISC_1 = SDL_CONTROLLER_BUTTON_MISC1,
    PADDLE_1 = SDL_CONTROLLER_BUTTON_PADDLE1,
    PADDLE_2 = SDL_CONTROLLER_BUTTON_PADDLE2,
    PADDLE_3 = SDL_CONTROLLER_BUTTON_PADDLE3,
    PADDLE_4 = SDL_CONTROLLER_BUTTON_PADDLE4,
    TOUCH_PAD = SDL_CONTROLLER_BUTTON_TOUCHPAD,
    MAX = SDL_CONTROLLER_BUTTON_MAX
};


class Joypad final : public ExportableToLua
{
public:
  // static information
  static constexpr const char module_name[] = "sol.joypad";
  static std::map<std::string, std::string> legacy_bindings_mapping;
  static std::map<JoyPadButton, int> legacy_button_number_mapping;

  Joypad(SDL_GameController* sdl_gc, SDL_Joystick* sdl_js);
  double get_axis(JoyPadAxis axis) const;
  bool is_button_pressed(JoyPadButton button) const;
  std::string get_name() const;
  void rumble(float low_frequency_intensity, float high_frequency_intensity, uint32_t duration);
  bool has_rumble();
  bool is_attached();
  void reset();
  int get_axis_deadzone(JoyPadAxis axis) const;
  void set_axis_deadzone(JoyPadAxis axis, int deadzone);
  const std::string& get_lua_type_name() const override;
  static double compute_axis_val(int16_t axis, int deadzone);
private:
  SDL_GameControllerUniquePtr controller;
  SDL_JoystickUniquePtr joystick;
  std::map<JoyPadAxis, int> axis_deadzones;
};

template <>
struct SOLARUS_API EnumInfoTraits<JoyPadAxis> {
  static const std::string pretty_name;
  static const EnumInfo<JoyPadAxis>::names_type names;
};

template <>
struct SOLARUS_API EnumInfoTraits<JoyPadButton> {
  static const std::string pretty_name;
  static const EnumInfo<JoyPadButton>::names_type names;
};


using JoypadPtr = std::shared_ptr<Joypad>;

}
