from scc.actions import Action, AxisAction
from scc.constants import HapticPos, SCButtons
from scc.macros import Macro
from scc.modifiers import DoubleclickModifier
from scc.parser import ActionParser
from scc.special_actions import MenuAction
from scc.uinput import Axes, Keys, Rels

parser = ActionParser()

CASES = {
	# Contains all test cases that are tested by
	# test_sensitivity and test_feedback
	# This should contain key for every Action that supports setting feedback
	# or sensitivity. test_tests method tests whether it realy does.
	'axis'  : {
		'action' : 'axis(ABS_RX)',
		'sensitivity' : (2.0,)
	},
	'raxis'  : {
		'action' : 'axis(ABS_RX)',
		'sensitivity' : (2.0,)
	},
	'mouse' : {
		'action' : 'mouse',
		'sensitivity' : (2.0, 3.0,),
		'feedback' : ('BOTH',)
	},
	'mouseabs' : {
		'action' : 'mouseabs(REL_X)',
		'sensitivity' : (2.0, 3.0,)
	},
	'gyro' : {
		'action' : 'gyro(ABS_X, ABS_Y, ABS_Z)',
		'sensitivity' : (2.0, 3.0, 4.0,)
	},
	'tilt' : {
		'action' : 'tilt( button(KEY_D), button(KEY_U), button(KEY_L), button(KEY_R) )',
		'sensitivity' : (2.0, 3.0, 4.0,)
	},
	'gyroabs' : {
		'action' : 'gyroabs(ABS_X, ABS_Y, ABS_Z)',
		'sensitivity' : (2.0, 3.0, 4.0),
		'feedback' : ('BOTH',)
	},
	'hatup' :    { 'action' : 'hatup(ABS_X)',    'sensitivity' : (2.0,) },
	'hatdown' :  { 'action' : 'hatdown(ABS_X)',  'sensitivity' : (2.0,) },
	'hatleft' :  { 'action' : 'hatleft(ABS_X)',  'sensitivity' : (2.0,) },
	'hatright' : { 'action' : 'hatright(ABS_X)', 'sensitivity' : (2.0,) },
	'button' : {
		'action' : 'button(KEY_X)',
		'feedback' : ('BOTH',)
	},
	'circular' : {
		'action' : 'mouse(REL_HWHEEL)',
		'circular' : True,
		'sensitivity' : (2.0,),
		'feedback' : ('BOTH',)
	},
	'circularabs' : {
		'action' : 'mouse(REL_HWHEEL)',
		'circularabs' : True,
		'sensitivity' : (2.0,),
		'feedback' : ('BOTH',)
	},
	'XY' : {
		'X' : { 'action' : 'axis(ABS_X)' },
		'Y' : { 'action' : 'axis(ABS_Y)' },
		'sensitivity' : (2.0, 3.0,),
		'feedback' : ('BOTH',)
	},
	'relXY' : {
		'X' : { 'action' : 'axis(ABS_RX)' },
		'Y' : { 'action' : 'axis(ABS_RY)' },
		'sensitivity' : (2.0, 3.0,),
		'feedback' : ('BOTH',)
	},
	'trigger' : {
		'action' : 'button(KEY_X)',
		'levels' : [ 10, 80 ],
		'feedback' : ('BOTH',)
	},
	'tilt' : {
		'action' : 'tilt( button(KEY_D), button(KEY_U), button(KEY_L), button(KEY_R) )',
		'sensitivity' : (2.0, 3.0, 4.0,)
	},
	'ball' : {
		'action' : 'ball(XY(axis(Axes.ABS_RX), axis(Axes.ABS_RY)))',
		'sensitivity' : (2.0, 3.0),
		'feedback' : ('BOTH',)
	},
	"dpad" : {
		"dpad": [
			{ "action": "button(Keys.KEY_W)" },
			{ "action": "button(Keys.KEY_S)" },
			{ "action": "button(Keys.KEY_A)" },
			{ "action": "button(Keys.KEY_D)" }
		],
		"feedback": ["LEFT", 32640]
	},
	"dpad8" : {
		"dpad": [
			{ "action": "button(Keys.KEY_1)" },
			{ "action": "button(Keys.KEY_2)" },
			{ "action": "button(Keys.KEY_3)" },
			{ "action": "button(Keys.KEY_4)" },
			{ "action": "button(Keys.KEY_5)" },
			{ "action": "button(Keys.KEY_6)" },
			{ "action": "button(Keys.KEY_7)" },
			{ "action": "button(Keys.KEY_8)" }
		],
		"feedback": ["LEFT", 32640]
	},
	"menu" : {
		"action": "menu('Default.menu')",
		"feedback": ["LEFT", 32640]
	},
	"hold": {
		"action": "button(Keys.KEY_W)",
		"hold": {
			"action": "menu('Default.menu')"
		},
		"feedback": ["LEFT", 32640]
	},
	"hipfire": {
                'action': 'hipfire( button(KEY_A), button(KEY_B) )',
                "feedback": ['BOTH']
	}
}

class TestCompress:
	"""
	Tests Aciton.compress method.
	Basically, tests how various combinations of modifiers interacts together.
	"""

	def test_tests(self):
		# Test if there is key in CASES for every action that suppports
		# setting feedback or sensitivity.
		for cls in Action.ALL.values():
			if Macro in cls.__bases__:
				# Skip macros, they are handled separately
				continue
			if MenuAction in cls.__bases__ and cls != MenuAction:
				# Skip alternate menu types, they all behave in same way
				continue
			if cls == DoubleclickModifier:
				# Tested along with hold
				continue
			if hasattr(cls, "set_speed"):
				assert cls.COMMAND in CASES, (
					"%s supports setting sensitivity, but "
					"there is no test case it" % (cls.COMMAND,))
				assert 'sensitivity' in CASES[cls.COMMAND], (
					"%s supports setting sensitivity, but "
					"case for it has no 'sensitivity' key it" % (
					cls.COMMAND,))
			if hasattr(cls, "set_haptic"):
				assert cls.COMMAND in CASES, (
					"%s supports feedback, but there is "
					"no test case it" % (cls.COMMAND,))
				assert 'feedback' in CASES[cls.COMMAND], (
					"%s supports feedback, but case for it has "
					"no 'feedback' key it" % (
					cls.COMMAND,))


	def test_hold_doubleclick(self):
		"""
		Tests parsing of hold & doubleclick combination.
		"""
		a = parser.from_json_data({
			'action' : 'axis(ABS_RX)',
			'hold' : { 'action' : "axis(ABS_X)" },
			'doubleclick' : { 'action' : "axis(ABS_Z)" }
		}).compress()

		assert isinstance(a, DoubleclickModifier)
		assert isinstance(a.normalaction, AxisAction)
		assert isinstance(a.action, AxisAction)
		assert isinstance(a.holdaction, AxisAction)
		assert a.normalaction.id == Axes.ABS_RX
		assert a.action.id == Axes.ABS_Z
		assert a.holdaction.id == Axes.ABS_X


	def test_sensitivity(self):
		"""
		Tests if all sensitivity setting are parsed and applied
		after .compress() is called.
		"""
		for case in CASES:
			if 'sensitivity' in CASES[case]:
				print("Testing 'sensitivity' on %s" % (case,))
				a = parser.from_json_data(CASES[case]).compress()
				assert (
					a.get_speed() == CASES[case]['sensitivity']
					or
					a.strip().get_speed() == CASES[case]['sensitivity']
				)


	def test_feedback(self):
		"""
		Tests if all feedback setting are parsed and applied
		after .compress() is called.
		"""
		for case in CASES:
			if 'feedback' in CASES[case]:
				print("Testing 'feedback' on %s" % (case,))
				a = parser.from_json_data(CASES[case]).compress()
				assert a.get_haptic().get_position().name == CASES[case]['feedback'][0]


	def test_multi(self):
		"""
		Tests if feedback and sensitivity setting are parsed and applied
		to actions in multiaciton.
		"""
		a = parser.from_json_data({
			'action' : 'circular(REL_HWHEEL) and gyroabs(None, ABS_Y, ABS_Z)',
			'sensitivity' : (2.0, 3.0, 4.0),
			'feedback' : ('BOTH',)
		}).compress()
		assert a.actions[0].get_haptic().get_position().name == "BOTH"
		for action in a.actions:
			assert action.get_speed()[0] == 2.0


	def test_macro(self):
		"""
		Tests if feedback and sensitivity setting are parsed and applied
		to actions in basic macro.
		"""
		a = parser.from_json_data({
			'action' : 'circular(REL_HWHEEL) ; gyroabs(None, ABS_Y, ABS_Z)',
			'sensitivity' : (2.0, 3.0, 4.0),
			'feedback' : ('BOTH',)
		}).compress()
		for action in a.actions:
			assert action.get_haptic().get_position().name == "BOTH"
			assert action.get_speed()[0] == 2.0
