# Fetch the search string and transform it into an object for easy access
arrParams = window.location.search.substring(1).split '&'
oParams = {}
for param in arrParams
	arrKV = param.split '='
	oParams[ arrKV[ 0 ] ] = arrKV[ 1 ]

if oParams.id
	oParams.id = decodeURIComponent oParams.id

strPublicKey = ''
fPlaceAndPaintInterval = () ->
	$( '#input_interval' ).html 'Interval:
		<input id="event_interval" type="text" />
		<b>"days hours:minutes"</b>, default = 10 minutes'

fFailedRequest = ( msg ) ->
	( err ) ->
		if err.status is 401
			window.location.href = 'forge?page=forge_rule'
		else
			$( '#info' ).text msg
			$( '#info' ).attr 'class', 'error'

$.post( '/usercommand', command: 'get_public_key' )
	.done ( data ) ->
		strPublicKey = data.message
	.fail ( err ) ->
		if err.status is 401
			window.location.href = 'forge?page=forge_rule'
		else
			$( '#info' ).text 'Error fetching public key, unable to send user specific parameters securely'
			$( '#info' ).attr 'class', 'error'

fOnLoad = () ->
	document.title = 'Rule Forge!'
	$( '#pagetitle' ).text '{{{user.username}}}, forge your ECA Rule!'

	editor = ace.edit "editor_conditions"
	editor.setTheme "ace/theme/monokai"
	editor.getSession().setMode "ace/mode/json"
	editor.setShowPrintMargin false
	# editor.session.setUseSoftTabs false 

# EVENT
	$.post( '/usercommand', command: 'get_event_pollers' )
		.done ( data ) ->
			try
				oEps = JSON.parse data.message
			catch err
				console.error 'ERROR: non-object received from server: ' + data.message
				return
			
			fAppendEvents = ( id, events ) ->
				fAppendEvent = ( evt ) ->
					$( '#select_event' ).append $( '<option>' ).text id + ' -> ' + evt
				fAppendEvent evt for evt in events
			fAppendEvents id, events for id, events of oEps
			$( '#input_event' ).val $( '#select_event' ).val()
			fFetchEventParams $( '#select_event option:selected' ).text()
		.fail fFailedRequest 'Error fetching event poller'

	$( '#select_event' ).change () ->
		evtFunc = $( this ).val()
		if evtFunc is ''
			$( '#input_interval' ).html ''
		else
			fPlaceAndPaintInterval()
		$( '#input_event' ).val evtFunc
		fFetchEventParams evtFunc

	$( '#input_event' ).change () ->
		$( '#select_event' ).val ''
		$( '#select_event' ).val $( this ).val()
		fFetchEventParams $( '#select_event' ).val()
		if $( '#select_event' ).val() is ''
			$( '#input_interval' ).html ''
		else
			fPlaceAndPaintInterval()

	fFetchEventParams = ( name ) ->
		$( '#event_poller_params *' ).remove()
		if name
			arr = name.split ' -> '
			obj =
				command: 'get_event_poller_params'
				payload: JSON.stringify
					id: arr[ 0 ]
			$.post( '/usercommand', obj )
				.done fAddEventParams arr[ 0 ]
				.fail fFailedRequest 'Error fetching event poller params'
			fFetchEventFunctionArgs arr

	fFetchEventFunctionArgs = ( arrName ) ->
		obj =
			command: 'get_event_poller_function_arguments'
			payload: JSON.stringify
				id: arrName[ 0 ]
		$.post( '/usercommand', obj )
			.done ( data ) ->
				if data.message
					oParams = JSON.parse data.message
					if oParams[ arrName[ 1 ] ]
						if oParams[ arrName[ 1 ] ].length > 0
							$( '#event_poller_params' ).append $( "<b>" ).text 'Required Function Parameters:'
						table = $( '<table>' ).appendTo $( '#event_poller_params' )
						for functionArgument in oParams[ arrName[ 1 ] ]
							tr = $( '<tr>' ).attr( 'class', 'funcMappings' ).appendTo table
							tr.append $( '<td>' ).css 'width', '20px'
							td = $( '<td>' ).appendTo tr
							td.append $( '<div>' ).attr( 'class', 'funcarg' ).text functionArgument
							tr.append td
							tr.append $( '<td>' ).text ' : '
							td = $( '<td>' ).appendTo tr
							td.append $( '<input>' ).attr 'type', 'text'
							tr.append td
			.fail fFailedRequest 'Error fetching action invoker function params'

	fAddEventParams = ( id ) ->
		( data ) ->
			if data.message
				oParams = JSON.parse data.message
				table = $ '<table>'
				i = 0
				fAppendParam = ( name, shielded ) ->
					i++
					tr = $( '<tr>' )
					tr.append $( '<td>' ).css 'width', '20px'
					tr.append $( '<td>' ).attr( 'class', 'key' ).text name
					inp = $( '<input>' )
					if shielded
						inp.attr( 'type', 'password' )
					tr.append $( '<td>' ).text( ' : ' ).append inp
					table.append tr
				fAppendParam name, shielded for name, shielded of oParams
				if i > 0
					$( '#event_poller_params' ).html '<b>Required Global Parameters:</b>'
					$( '#event_poller_params' ).append table

				fFillEventParams id

	fFillEventParams = ( moduleId ) ->
		obj =
			command: 'get_event_poller_user_params'
			payload: JSON.stringify
				id: moduleId
		$.post( '/usercommand', obj )
			.done ( data ) ->
				oParams = JSON.parse data.message
				for param, oParam of oParams
					par = $( "#event_poller_params tr" ).filter () ->
						$( 'td.key', this ).text() is param
					$( 'input', par ).val oParam.value
					$( 'input', par ).attr 'unchanged', 'true'
					$( 'input', par ).change () ->
						$( this ).attr 'unchanged', 'false'

		obj.command = 'get_event_poller_user_arguments'
		obj.payload = JSON.stringify
			ruleId: $( '#input_id' ).val()
			moduleId: moduleId
		$.post( '/usercommand', obj )
			.done fAddEventUserArgs moduleId

	fAddEventUserArgs = ( name ) ->
		( data ) ->
			for key, arrFuncs of data.message
				par = $ "#event_poller_params"
				for oFunc in JSON.parse arrFuncs
					tr = $( "tr", par ).filter () ->
						$( '.funcarg', this ).text() is "#{ oFunc.argument }"
					$( "input[type=text]", tr ).val oFunc.value
					$( "input[type=checkbox]", tr ).prop 'checked', oFunc.jsselector

# ACTIONS
	obj =
		command: 'get_action_invokers'
	$.post( '/usercommand', obj )

		.done ( data ) ->
			try
				oAis = JSON.parse data.message
			catch err
				console.error 'ERROR: non-object received from server: ' + data.message
				return
			fAppendActions = ( module, actions ) ->
				for act in actions
					arrEls = $( "#action_params div" ).filter () ->
						$( this ).text() is "#{ module } -> #{ act }"
					# It could have been loaded async before through the rules ito the action params
					if arrEls.length is 0
						$( '#select_actions' ).append $( '<option>' ).text module + ' -> ' + act
			fAppendActions module, actions for module, actions of oAis
		.fail fFailedRequest 'Error fetching event poller'

	fAddSelectedAction = ( name ) ->
		arrName = name.split ' -> '
		arrEls = $( "#action_params div.modName" ).map( () ->
			$( this ).text()
		).get()
		table = $( '#selected_actions' )
		tr = $( '<tr>' ).appendTo table
		img = $( '<img>' ).attr 'src', 'red_cross_small.png'
		tr.append $( '<td>' ).css( 'width', '20px' ).append img
		tr.append $( '<td>' ).attr( 'class', 'title').text name 
		td = $( '<td>' ).attr( 'class', 'funcMappings').appendTo tr
		fFetchActionFunctionArgs td, arrName
		if arrName[ 0 ] not in arrEls
			div = $( '<div>' ).appendTo $( '#action_params' )
			subdiv = $( '<div> ').appendTo div 
			subdiv.append $( '<div>' )
				.attr( 'class', 'modName underlined' ).text arrName[ 0 ]
			fFetchActionParams div, arrName[ 0 ]
		$( "#select_actions option" ).each () ->
			if $( this ).text() is name
				$( this ).remove()
		fFillActionFunction arrName[ 0 ]

	fFetchActionParams = ( div, modName ) ->
		obj =
			command: 'get_action_invoker_params'
			payload: JSON.stringify
				id: modName
		$.post( '/usercommand', obj )
			.done ( data ) ->
				if data.message
					oParams = JSON.parse data.message
					table = $ '<table>'
					div.append table
					fAppendActionParam = ( name, shielded ) ->
						tr = $( '<tr>' )
						tr.append $( '<td>' ).css 'width', '20px'
						tr.append $( '<td>' ).attr( 'class', 'key').text name
						inp = $( '<input>' )
						if shielded
							inp.attr( 'type', 'password' )
						else
							inp.attr( 'type', 'text' )
						tr.append $( '<td>' ).text(' : ').append inp
						table.append tr
					fAppendActionParam name, sh for name, sh of oParams
			.fail fFailedRequest 'Error fetching action invoker params'

	fFetchActionFunctionArgs = ( tag, arrName ) ->
		obj =
			command: 'get_action_invoker_function_arguments'
			payload: JSON.stringify
				id: arrName[ 0 ]
		$.post( '/usercommand', obj )
			.done ( data ) ->
				if data.message
					oParams = JSON.parse data.message
					if oParams[ arrName[ 1 ] ]
						table = $( '<table>' ).appendTo tag
						for functionArgument in oParams[ arrName[ 1 ] ]
							tr = $( '<tr>' ).appendTo table
							td = $( '<td>' ).appendTo tr
							td.append $( '<div>' ).attr( 'class', 'funcarg' ).text functionArgument
							tr.append td
							td = $( '<td>' ).appendTo tr
							td.append $( '<input>' ).attr 'type', 'text'
							tr.append td
							td = $( '<td>' ).appendTo tr
							td.append $( '<input>' ).attr( 'type', 'checkbox' )
								.attr 'title', 'js-select expression to be resolved on event?'
			.fail fFailedRequest 'Error fetching action invoker function params'

	fFillActionFunction = ( name ) ->
		obj =
			command: 'get_action_invoker_user_params'
			payload: JSON.stringify
				id: name
		$.post( '/usercommand', obj )
			.done fAddActionUserParams name
		
		obj.command = 'get_action_invoker_user_arguments'
		obj.payload = JSON.stringify
			ruleId: $( '#input_id' ).val()
			moduleId: name
		$.post( '/usercommand', obj )
			.done fAddActionUserArgs name

	fAddActionUserParams = ( name ) ->
		( data ) ->
			oParams = JSON.parse data.message
			domMod = $( "#action_params div" ).filter () ->
				$( 'div.modName', this ).text() is name
			for param, oParam of oParams
				par = $( "tr", domMod ).filter () ->
					$( 'td.key', this ).text() is param
				$( 'input', par ).val oParam.value
				$( 'input', par ).attr 'unchanged', 'true'
				$( 'input', par ).change () ->
					$( this ).attr 'unchanged', 'false'

	fAddActionUserArgs = ( name ) ->
		( data ) ->
			for key, arrFuncs of data.message
				par = $( "#selected_actions tr" ).filter () ->
					$( 'td.title', this ).text() is "#{ name } -> #{ key }"
				for oFunc in JSON.parse arrFuncs
					tr = $( "tr", par ).filter () ->
						$( '.funcarg', this ).text() is "#{ oFunc.argument }"
					$( "input[type=text]", tr ).val oFunc.value
					$( "input[type=checkbox]", tr ).prop 'checked', oFunc.jsselector

	$( '#select_actions' ).on 'change', () ->
		opt = $ 'option:selected', this
		fAddSelectedAction opt.text()
		
	$( '#selected_actions' ).on 'click', 'img', () ->
		act = $( this ).closest( 'td' ).siblings( '.title' ).text()
		arrName = act.split ' -> '

		nMods = 0
		# Check whether we're the only function left that was selected from this module
		$( "#selected_actions td.title" ).each () ->
			arrNm = $( this ).text().split ' -> '
			nMods++ if arrNm[ 0 ] is arrName[ 0 ]
		if nMods is 1
			$('#action_params > div').each () ->
				if $( this ).children( 'div.modName' ).text() is arrName[ 0 ]
					$( this ).remove()

		opt = $( '<option>' ).text act
		$( '#select_actions' ).append opt
		$( this ).closest( 'tr' ).remove()


# SUBMIT

	$( '#but_submit' ).click () ->
		window.scrollTo 0, 0
		$( '#info' ).text ''

		try
			if $( '#input_id' ).val() is ''
				$( '#input_id' ).focus()
				throw new Error 'Please enter a rule name!'

			eventId = $( '#input_event' ).val()
			if eventId is ''
				$( '#input_event' ).focus()
				throw new Error 'Please assign an event!'

			ep = {}
			$( "#event_poller_params tr" ).each () ->
				key = $( this ).children( '.key' ).text()
				val = $( 'input', this ).val()
				if val is ''
					$( 'input', this ).focus()
					throw new Error "Please enter a value for '#{ key }' in the event module!"
				shielded = $( 'input', this ).attr( 'type' ) is 'password'
				ep[ key ] =
					shielded: shielded
				if not shielded or $( 'input', this ).attr( 'unchanged' ) isnt 'true'
					encryptedParam = cryptico.encrypt val, strPublicKey
					ep[ key ].value = encryptedParam.cipher 
				else
					ep[ key ].value = val

			evtFuncs = {}
			evtFuncs[ eventId ] = []
			$( '#event_poller_params tr.funcMappings' ).each () ->
				evtFuncs[ eventId ].push
					argument: $( 'div.funcarg', this ).text()
					value: $( 'input[type=text]', this ).val()

			if $( '#selected_actions tr' ).length is 0
				throw new Error 'Please select at least one action or create one!'

			# Store all selected action invokers
			ap = {}
			$( '> div', $( '#action_params' ) ).each () ->
				modName = $( '.modName', this ).text()
				params = {}
				$( 'tr', this ).each () ->
					key = $( '.key', this ).text()
					val = $( 'input', this ).val()
					shielded = $( 'input', this ).attr( 'type' ) is 'password'
					if val is ''
						$( 'input', this ).focus()
						throw new Error "'#{ key }' missing for '#{ modName }'"
					params[ key ] =
						shielded: shielded

					if not shielded or $( 'input', this ).attr( 'unchanged' ) isnt 'true'
						encryptedParam = cryptico.encrypt val, strPublicKey
						params[ key ].value = encryptedParam.cipher 
					else
						params[ key ].value = val
				ap[ modName ] = params
			acts = []
			actFuncs = {}
			$( '#selected_actions td.title' ).each () ->
				actionName = $( this ).text()
				actFuncs[ actionName ] = []
				acts.push actionName
				par = $( this ).parent()
				$( '.funcMappings tr', par ).each () ->
					# No need to encrypt this, right?
					# tmp =
					# 	argument: $( 'div.funcarg', this ).val()
					# 	value: $( 'input[type=text]', this ).val()
					# 	regexp: $( 'input[type=checkbox]', this ).is( ':checked' )
					# tmp = cryptico.encrypt JSON.stringify( tmp ), strPublicKey
					# actFuncs[ actionName ] = tmp.cipher
					actFuncs[ actionName ].push
						argument: $( 'div.funcarg', this ).text()
						value: $( 'input[type=text]', this ).val()
						jsselector: $( 'input[type=checkbox]', this ).is( ':checked' )
			
			try
				conds = JSON.parse editor.getValue()
			catch err
				throw new Error "Parsing of your conditions failed! Needs to be an Array of Strings!"
			
			if conds not instanceof Array
				throw new Error "Conditions Invalid! Needs to be an Array of Strings!"

			# Parse a time string
			fParseTime = ( str, hasDay ) ->
				arrTime = str.split ':'
				# If there's only one entry, this is the amount of minutes
				if hasDay
					def = 0
				else
					def = 10
				if arrTime.length is 1
					time = parseInt( str ) || def
					if hasDay
						time * 60
					else
						time
				else
					h = parseInt( arrTime[ 0 ] ) || 0
					if h > 0
						def = 0
					h * 60 + ( parseInt( arrTime[ 1 ] ) || def )


			txtInterval = $( '#event_interval' ).val()
			if not txtInterval
				mins = 10
			else
				arrInp = txtInterval.split ' '
				# There's only one string entered, either day or hour
				if arrInp.length is 1
					mins = fParseTime txtInterval
				else
					d = parseInt( arrInp[ 0 ] ) || 0
					mins = d * 24 * 60 + fParseTime arrInp[ 1 ], true

			# We have to limit this to 24 days because setTimeout only takes integer values
			# until we implement a scheduler that deals with larger intervals
			mins = Math.min mins, 35700
			mins = Math.max 1, mins
			fCheckOverwrite = ( obj ) ->
				( err ) ->
					if err.status is 409
						if confirm 'Are you sure you want to overwrite the existing rule?'
							payl = JSON.parse obj.payload
							payl.overwrite = true
							obj.payload = JSON.stringify payl
							$.post( '/usercommand', obj )
								.done ( data ) ->
									$( '#info' ).text data.message
									$( '#info' ).attr 'class', 'success'
								.fail fFailedRequest "#{ obj.id } not stored!"
					else
						fFailedRequest( "#{ obj.id } not stored!" ) err
			obj =
				command: 'forge_rule'
				payload: JSON.stringify
					id: $( '#input_id' ).val()
					event: eventId
					event_params: ep
					event_interval: mins
					event_functions: evtFuncs
					conditions: conds
					actions: acts
					action_params: ap
					action_functions: actFuncs
			$.post( '/usercommand', obj )
				.done ( data ) ->
					$( '#info' ).text data.message
					$( '#info' ).attr 'class', 'success'
				.fail fCheckOverwrite obj
		catch err
			console.log err
			$( '#info' ).text 'Error in upload: ' + err.message
			$( '#info' ).attr 'class', 'error'
			alert err.message
			throw err

	if oParams.id
		obj =
			command: 'get_rule'
			payload: JSON.stringify
				id: oParams.id
		$.post( '/usercommand', obj )
			.done ( data ) ->
				oRule = JSON.parse data.message
				if oRule
					$( '#input_id' ).val oRule.id
					
					# Event
					$( '#select_event' ).val oRule.event
					if $( '#select_event' ).val() isnt ''
						fFetchEventParams oRule.event
						fPlaceAndPaintInterval()

					$( '#input_event' ).val oRule.event
					$( '#event_interval' ).val oRule.event_interval

					# Conditions
					editor.setValue JSON.stringify oRule.conditions, undefined, 2

					# Actions
					for action in oRule.actions
						arrName = action.split ' -> '
						fAddSelectedAction action

			.fail ( err ) ->
				if err.responseText is ''
					msg = 'No Response from Server!'
				else
					try
						msg = JSON.parse( err.responseText ).message
				fFailedRequest( 'Error in upload: ' + msg ) err

window.addEventListener 'load', fOnLoad, true
