import Foundation
import SDL2


public class GamePad
{
	public enum Axes
	{
		case leftStickX, leftStickY
		case rightStickX, rightStickY
		case leftTrigger, rightTrigger
	}

	public enum Buttons
	{
		case east, south, north, west
		case select, start, guide, mic, touchPad
		case leftStick, rightStick
		case leftBumper, rightBumper
		case dpadLeft, dpadRight, dpadUp, dpadDown
		case paddle1, paddle2, paddle3, paddle4
	}

	public struct State
	{
		public func down(_ btn: Buttons) -> Bool { btnState(btn) & GamePad._DOWN == GamePad._DOWN }
		public func pressed(_ btn: Buttons) -> Bool { btnState(btn) == GamePad._PRESS }
		public func released(_ btn: Buttons) -> Bool { btnState(btn) == GamePad._RELEASE }

		public func axis(_ axis: Axes) -> Float
		{
			let axisRescale = 1.0 / Float(Int16.max)
			return Float(rawAxis(axis)) * axisRescale
		}

		@inline(__always) func rawAxis(_ axis: Axes) -> Int16
		{
			_axes[Int(axis.sdlEnum.rawValue)]
		}

		@inline(__always) private func btnState(_ btn: Buttons) -> UInt8
		{
			_btns[Int(btn.sdlEnum.rawValue)]
		}

		private let _btns: [UInt8], _axes: [Int16]

		internal init(btns: [UInt8], axes: [Int16])
		{
			self._btns = btns
			self._axes = axes
		}
	}

	public var state: State
	{
		.init(btns: _btns, axes: _axes)
	}

	@inline(__always) public static var current: GamePad?
	{
		let input = Input.instance
		return if let id = input.firstPadID { input.getPad(id: id) } else { nil }
	}

	private static let _UP = UInt8(0b00), _DOWN = UInt8(0b10), _IMPULSE = UInt8(0b01)
	private static let _PRESS = _DOWN | _IMPULSE
	private static let _RELEASE = _UP | _IMPULSE

	private var _btns = [UInt8](repeating: 0, count: Int(SDL_CONTROLLER_BUTTON_MAX.rawValue))
	private var _axes = [Int16](repeating: 0, count: Int(SDL_CONTROLLER_AXIS_MAX.rawValue))
	private var _sdlPad: OpaquePointer

	internal init(pad: OpaquePointer)
	{
		self._sdlPad = pad
	}

	internal func newTick()
	{
		_btns = _btns.map({ $0 & ~Self._IMPULSE })
	}

	internal func close()
	{
		SDL_GameControllerClose(_sdlPad)
	}

	internal func buttonPressEvent(_ btn: UInt8)
	{
		_btns[Int(btn)] = Self._PRESS
	}

	internal func buttonReleaseEvent(_ btn: UInt8)
	{
		_btns[Int(btn)] = Self._RELEASE
	}

	internal func axisEvent(_ axis: UInt8, _ value: Int16)
	{
		_axes[Int(axis)] = value
	}
}

public extension GamePad.State
{
	var leftStick: Vec2f { Vec2f(axis(.leftStickX), axis(.leftStickY)) }
	var rightStick: Vec2f { Vec2f(axis(.rightStickX), axis(.rightStickY)) }
}


internal extension GamePad.Axes
{
	var sdlEnum: SDL_GameControllerAxis
	{
		switch self
		{
		case .leftStickX:   SDL_CONTROLLER_AXIS_LEFTX
		case .leftStickY:   SDL_CONTROLLER_AXIS_LEFTY
		case .rightStickX:  SDL_CONTROLLER_AXIS_RIGHTX
		case .rightStickY:  SDL_CONTROLLER_AXIS_RIGHTY
		case .leftTrigger:  SDL_CONTROLLER_AXIS_TRIGGERLEFT
		case .rightTrigger: SDL_CONTROLLER_AXIS_TRIGGERRIGHT
		}
	}
}

internal extension GamePad.Buttons
{
	var sdlEnum: SDL_GameControllerButton
	{
		switch self
		{
		case .east:        SDL_CONTROLLER_BUTTON_B
		case .south:       SDL_CONTROLLER_BUTTON_A
		case .west:        SDL_CONTROLLER_BUTTON_Y
		case .north:       SDL_CONTROLLER_BUTTON_X
		case .start:       SDL_CONTROLLER_BUTTON_START
		case .select:      SDL_CONTROLLER_BUTTON_BACK
		case .guide:       SDL_CONTROLLER_BUTTON_GUIDE
		case .mic:         SDL_CONTROLLER_BUTTON_MISC1
		case .touchPad:    SDL_CONTROLLER_BUTTON_TOUCHPAD
		case .leftStick:   SDL_CONTROLLER_BUTTON_LEFTSTICK
		case .rightStick:  SDL_CONTROLLER_BUTTON_RIGHTSTICK
		case .leftBumper:  SDL_CONTROLLER_BUTTON_LEFTSHOULDER
		case .rightBumper: SDL_CONTROLLER_BUTTON_RIGHTSHOULDER
		case .dpadUp:      SDL_CONTROLLER_BUTTON_DPAD_UP
		case .dpadDown:    SDL_CONTROLLER_BUTTON_DPAD_DOWN
		case .dpadLeft:    SDL_CONTROLLER_BUTTON_DPAD_LEFT
		case .dpadRight:   SDL_CONTROLLER_BUTTON_DPAD_RIGHT
		case .paddle1:     SDL_CONTROLLER_BUTTON_PADDLE1
		case .paddle2:     SDL_CONTROLLER_BUTTON_PADDLE2
		case .paddle3:     SDL_CONTROLLER_BUTTON_PADDLE3
		case .paddle4:     SDL_CONTROLLER_BUTTON_PADDLE4
		}
	}
}
