extends Control

# signals for input events
signal controller_input(input)
signal controller_action_pressed

signal controller_escape
signal controller_start
signal controller_enter

signal controller_up
signal controller_down
signal controller_right
signal controller_left

signal controller_fire
signal controller_jump

signal controller_fire_pressed
signal controller_jump_pressed
signal controller_right_pressed
signal controller_left_pressed
signal controller_up_pressed
signal controller_down_pressed

signal controller_connected(controller_name)
signal controller_disconnected

signal controller_easteregg(easteregg)


var touchpad = null


# ----------------------------------------------------------------------------
# Controller mapping, as set here:
# https://godotforums.org/discussion/20635/gamepads-in-android-anyone-got-experience-considering-godot

var mappings = [
"05000000bc20000000550000ffff3f00,GameSir G3w,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,",
"05000000d6020000e5890000dfff3f00,GPD XD Plus,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Android,",
"64633436313965656664373634323364,Microsoft X-Box 360 pad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,platform:Android,",
"050000007e05000009200000ffff0f00,Nintendo Switch Pro Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b16,x:b17,y:b2,platform:Android,",
"050000007e05000009200000ffff0f00,Pro Controller,a:b0,b:b1,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b16,x:b17,y:b2,platform:Android,",
"37336435666338653565313731303834,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,",
"4e564944494120436f72706f72617469,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,",
"61363931656135336130663561616264,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,",
"050000005509000003720000cf7f3f00,NVIDIA Controller v01.01,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,",
"050000005509000010720000ffff3f00,NVIDIA Controller v01.03,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,",
"050000004c05000068020000dfff3f00,PS3 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,",
"050000004c050000c4050000fffe3f00,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android,",
"050000004c050000cc090000fffe3f00,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android,",
"35643031303033326130316330353564,PS4 Controller,a:b1,b:b17,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android,",
"050000003215000000090000bf7f3f00,Razer Serval,a:b0,b:b1,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,platform:Android,",
"05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Android,",
"05000000de2800000611000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Android,",
"5477696e20555342204a6f7973746963,Twin USB Joystick,a:b22,b:b21,back:b28,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b26,leftstick:b30,lefttrigger:b24,leftx:a0,lefty:a1,rightshoulder:b27,rightstick:b31,righttrigger:b25,rightx:a3,righty:a2,start:b29,x:b23,y:b20,platform:Android,",
"050000005e040000e00200000ffe3f00,Xbox One Wireless Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b17,y:b2,platform:Android,",
"050000005e040000fd020000ffff3f00,Xbox One Wireless Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,",
"050000005e04000091020000ff073f00,Xbox Wireless Controller,a:b0,b:b1,back:b4,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android,",
"34356136633366613530316338376136,Xbox Wireless Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,x:b17,y:b2,platform:Android,",
]

func init_mappings():
	for i in range(mappings.size()):
		var newmap=""
		var maparr = mappings[i].split(",")
		if maparr.size()>=2:
			maparr[0]=gen_android_joypad_guid(maparr)
			for j in range(maparr.size()):
				newmap+=maparr[j]+","
			Input.add_joy_mapping(newmap,true)

func gen_android_joypad_guid(maparr):
	if (maparr!=null) and (maparr.size()>=2):
		var nameascii = maparr[1].to_ascii()
		var uid=""
		for i in range(min(nameascii.size(),16)):
			uid+=("%02x" % nameascii[i])
		return uid  
	else:
		print("Error: empty or too small map array passed to gen_android_joypad_guid")
		return ""

# ----------------------------------------------------------------------------


# ---- ENTER is pressed
var pressed_enter = false
func is_enter():
	if !Input.is_action_pressed("ui_enter") and pressed_enter:
		pressed_enter = false
		return false
		
	if Input.is_action_pressed("ui_enter") and !pressed_enter:
		pressed_enter = true
		emit_signal("controller_start")
		emit_signal("controller_enter")
		emit_signal("controller_input", "ENTER")
		return true

# ---- ESCAPE is pressed
var pressed_escape = false
func is_escape():
	if !Input.is_action_pressed("ui_cancel") and pressed_escape:
		pressed_escape = false
		return false
		
	if Input.is_action_pressed("ui_cancel") and !pressed_escape:
		pressed_escape = true
		emit_signal("controller_escape")
		emit_signal("controller_input", "ESCAPE")
		return true


# ---- UP is pressed
var pressed_up = false
func is_up():
	if !Input.is_action_pressed("ui_up") and pressed_up:
		pressed_up = false
		return false
		
	if Input.is_action_pressed("ui_up") and !pressed_up:
		pressed_up = true
		emit_signal("controller_up")
		emit_signal("controller_input", "UP")
		return true

# ---- DOWN is pressed	
var pressed_down = false
func is_down():
	if !Input.is_action_pressed("ui_down") and pressed_down:
		pressed_down = false
		return false
		
	if Input.is_action_pressed("ui_down") and !pressed_down:
		pressed_down = true
		emit_signal("controller_down")
		emit_signal("controller_input", "DOWN")
		return true

# ---- RIGHT is pressed
var pressed_right = false
func is_right():
	if !Input.is_action_pressed("ui_right") and pressed_right:
		pressed_right = false
		return false
		
	if Input.is_action_pressed("ui_right") and !pressed_right:
		pressed_right = true
		emit_signal("controller_right")
		emit_signal("controller_input", "RIGHT")
		return true

# ---- LEFT is pressed
var pressed_left = false
func is_left():
	if !Input.is_action_pressed("ui_left") and pressed_left:
		pressed_left = false
		return false
		
	if Input.is_action_pressed("ui_left") and !pressed_left:
		pressed_left = true
		emit_signal("controller_left")
		emit_signal("controller_input", "LEFT")
		return true


# ---- FIRE is pressed
var pressed_fire = false
func is_fire():
	if !Input.is_action_pressed("ui_fire") and pressed_fire:
		pressed_fire = false
		return false
		
	if Input.is_action_pressed("ui_fire") and !pressed_fire:
		pressed_fire = true
		emit_signal("controller_fire")
		emit_signal("controller_input", "FIRE")
		return true

# ---- JUMP is pressed
var pressed_jump = false
func is_jump():
	if !Input.is_action_pressed("ui_jump") and pressed_jump:
		pressed_jump = false
		return false
		
	if Input.is_action_pressed("ui_jump") and !pressed_jump:
		pressed_jump = true
		emit_signal("controller_jump")
		emit_signal("controller_input", "JUMP")
		return true


# returns whatever the player wanted to jump a cutscene (fire, jump or enter)
# use it with cutscenes, for instance
var pressed_action = false
func is_action_input():
	var input = is_enter() or is_fire() or is_jump()	
	if !pressed_action and input:
		pressed_action = true
		emit_signal("controller_action_pressed")
	
	if pressed_action and !input:
		pressed_action = false
	
	return input


# get horizontal axis, planned for both keyboard and joypad/joystick
func get_horizontal_axis():
	var axis_right = 0
	if joystick_connected:
		axis_right = Input.get_joy_axis(0, JOY_AXIS_0)
		
	if axis_right < 0.2 and axis_right > -0.2:
		axis_right = 0
		
	if Input.is_action_pressed("ui_left"):
		axis_right = -1
	elif Input.is_action_pressed("ui_right"):
		axis_right = 1
	
	return axis_right


# --------------- CONTINUOUS PRESSED EVENTS ------------


func is_pressed_fire():
	var event = Input.is_action_pressed("ui_fire")
	if event: emit_signal("controller_fire_pressed")
	return event

func is_pressed_jump():
	var event = Input.is_action_pressed("ui_jump")
	if event: emit_signal("controller_jump_pressed")
	return event

func is_pressed_right():
	var event = Input.is_action_pressed("ui_right")
	if event: emit_signal("controller_right_pressed")
	return event

func is_pressed_left():
	var event = Input.is_action_pressed("ui_left")
	if event: emit_signal("controller_left_pressed")
	return event

func is_pressed_up():
	var event = Input.is_action_pressed("ui_up")
	if event: emit_signal("controller_up_pressed")
	return event

func is_pressed_down():
	var event = Input.is_action_pressed("ui_down")
	if event: emit_signal("controller_down_pressed")
	return event



# ------------------ TOUCHPAD EVENTS ----------------------


# returns -1 (left), 0 (none), 1 (right) 
func get_touchpad_4axis_horizontal():
	if not touchpad: return 0
	if touchpad.joystick_vector.x < -0.25: return 1
	if touchpad.joystick_vector.x > 0.25: return -1
	return 0
	#if not touchpad.joystick_active: return 0
	pass
	
# returns -1 (down), 0 (no use), 1 (up)
func get_touchpad_4axis_vertical():
	if not touchpad: return 0
	if touchpad.joystick_vector.x < -0.25: return -1
	if touchpad.joystick_vector.x > 0.25: return 1
	return 0
	pass

# ---- TOUCHPAD RIGHT is pressed just once
var pressed_touchpad_right = false
func is_touchpad_right():
	if self.get_touchpad_4axis_horizontal() != 1 and pressed_touchpad_right:
		pressed_touchpad_right = false
		return false
		
	if self.get_touchpad_4axis_horizontal() == 1 and !pressed_touchpad_right:
		pressed_touchpad_right = true
		emit_signal("controller_right")
		emit_signal("controller_input", "RIGHT")
		return true

# ---- TOUCHPAD LEFT is pressed just once
var pressed_touchpad_left = false
func is_touchpad_left():
	if self.get_touchpad_4axis_horizontal() != -1 and pressed_touchpad_left:
		pressed_touchpad_left = false
		return false
		
	if self.get_touchpad_4axis_horizontal() == -1 and !pressed_touchpad_left:
		pressed_touchpad_left = true
		emit_signal("controller_left")
		emit_signal("controller_input", "LEFT")
		return true

# ---- TOUCHPAD UP is pressed just once
var pressed_touchpad_up = false
func is_touchpad_up():
	if self.get_touchpad_4axis_vertical() != 1 and pressed_touchpad_up:
		pressed_touchpad_up = false
		return false
		
	if self.get_touchpad_4axis_vertical() == 1 and !pressed_touchpad_up:
		pressed_touchpad_up = true
		emit_signal("controller_up")
		emit_signal("controller_input", "UP")
		return true

# ---- TOUCHPAD DOWN is pressed just once
var pressed_touchpad_down = false
func is_touchpad_down():
	if self.get_touchpad_4axis_vertical() != -1 and pressed_touchpad_down:
		pressed_touchpad_down = false
		return false
		
	if self.get_touchpad_4axis_vertical() == -1 and !pressed_touchpad_down:
		pressed_touchpad_down = true
		emit_signal("controller_down")
		emit_signal("controller_input", "DOWN")
		return true





# ----------------------------------------------------------------------------


var joystick_connected = false
var last_keyboard = ""


func _on_joy_connection_changed(device_id, connected):
	if connected: last_keyboard = Input.get_joy_name(device_id)
	else: last_keyboard = "Keyboard"



# warning-ignore:unused_argument
func _process(delta):
	
	# JOY_BUTTON_0 = JOY_XBOX_A (XBox) = JOY_SONY_X (PS4) = JOY_DS_B (Nintendo)	
	last_keyboard = PoolStringArray(Input.get_connected_joypads()).join(" ")
	
	# every time verifies if there is a joystick connected if it isn't connected previously
	if Input.get_joy_name(0) and !joystick_connected:
		print("CONTROLLER | Joystick connected!  " + Input.get_joy_name(0))
		emit_signal("controller_connected", Input.get_joy_name(0))
		joystick_connected = true
		
	# ... but if it were disconnected, well.
	if !Input.get_joy_name(0) and joystick_connected:
		print("CONTROLLER | Joystick disconnected from the computer!")
		emit_signal("controller_disconnected")
		joystick_connected = false
	
	is_action_input()
	
	is_escape()
	is_enter()
	
	is_up()
	is_down()
	is_right()
	is_left()
	
	is_fire()
	is_jump()
	
	is_pressed_fire()
	is_pressed_jump()
	is_pressed_right()
	is_pressed_left()
	is_pressed_up()
	is_pressed_down()
	
	is_touchpad_right()
	is_touchpad_left()
	is_touchpad_up()
	is_touchpad_down()


# TODO
func is_jetpack():
	return false


func _ready():

	pause_mode = PAUSE_MODE_PROCESS
	# warning-ignore:return_value_discarded
	Input.connect("joy_connection_changed", self, "_on_joy_connection_changed")
	init_mappings()
	
	init_easteregg()
	
	# verify if joystick is connected ot the device!
	if Input.get_joy_name(0) and !joystick_connected:
		print("CONTROLLER | Joystick found! " + Input.get_joy_name(0))
		emit_signal("controller_connected", Input.get_joy_name(0))
		joystick_connected = true
	elif !Input.get_joy_name(0) and joystick_connected:
		print("CONTROLLER | Joystick removed! " + Input.get_joy_name(0))
		emit_signal("controller_disconnected", Input.get_joy_name(0))
		joystick_connected = false


# ------ Easter Egg code ------

var easteregg_string = ""
var easteregg_list = []

func init_easteregg():
	# warning-ignore:return_value_discarded
	self.connect("controller_down", self, "add_easteregg_key", ["down"])
	# warning-ignore:return_value_discarded
	self.connect("controller_up", self, "add_easteregg_key", ["up"])
	# warning-ignore:return_value_discarded
	self.connect("controller_right", self, "add_easteregg_key", ["right"])
	# warning-ignore:return_value_discarded
	self.connect("controller_left", self, "add_easteregg_key", ["left"])
	# warning-ignore:return_value_discarded
	self.connect("controller_start", self, "add_easteregg_key", ["start"])
	# warning-ignore:return_value_discarded
	self.connect("controller_escape", self, "add_easteregg_key", ["escape"])
	# warning-ignore:return_value_discarded
	self.connect("controller_jump", self, "add_easteregg_key", ["jump"])
	# warning-ignore:return_value_discarded
	self.connect("controller_fire", self, "add_easteregg_key", ["fire"])
	
func add_easteregg_key(input_key):
	if easteregg_string == "":
		easteregg_string = input_key
	else:
		easteregg_string = easteregg_string + "," + input_key
		
	#limit string size to 16
	var teste = easteregg_string.split(",")
	while teste.size() > 16:
		teste.remove(0)
	easteregg_string = PoolStringArray(teste).join(",")
	
	analyze_easteregg()

func analyze_easteregg():
	
	var code_size = 0
	var crop_string = []
	var easteregg_backup = ""
	#var n = {}
	
	for n in easteregg_list:
		if easteregg_string == "": return
		crop_string = n.code.split(",")
		code_size = crop_string.size()
		easteregg_backup = easteregg_string.split(",")
		while easteregg_backup.size() > code_size:
			easteregg_backup.remove(0)
		easteregg_backup = PoolStringArray(easteregg_backup).join(",")
		
		if n.code == easteregg_backup:
			easteregg_string = ""
			if n.object and n.method:
				n.object.call(n.method)
				emit_signal("controller_easteregg", easteregg_backup)
			if n.remove_after:
				var i = easteregg_list.find(n)
				easteregg_list.remove(i)
	pass
	

func register_easteregg(keypad_code, object, method, remove_after = true, limited_to_scene = -1):
	easteregg_list += [{
		"code": keypad_code,
		"object": object,
		"method": method,
		"remove_after": remove_after,
		"scene": limited_to_scene
	}]
