-- license:BSD-3-Clause
-- copyright-holders:Vas Crabb
local exports = {
	name = 'commonui',
	version = '0.0.1',
	description = 'Common plugin UI helpers',
	license = 'BSD-3-Clause',
	author = { name = 'Vas Crabb' } }


local commonui = exports


function commonui.input_selection_menu(action, title, filter)
	menu = { }

	local choices
	local index_first_choice
	local index_cancel

	local function populate_choices()
		local ioport = manager.machine.ioport

		local function compare(a, b)
			if a.device.tag < b.device.tag then
				return true
			elseif a.device.tag > b.device.tag then
				return false
			end
			groupa = ioport:type_group(a.type, a.player)
			groupb = ioport:type_group(b.type, b.player)
			if groupa < groupb then
				return true
			elseif groupa > groupb then
				return false
			elseif a.type < b.type then
				return true
			elseif a.type > b.type then
				return false
			else
				return a.name < b.name
			end
		end

		choices = { }
		for tag, port in pairs(manager.machine.ioport.ports) do
			for name, field in pairs(port.fields) do
				if (not filter) or filter(field) then
					table.insert(choices, field)
				end
			end
		end
		table.sort(choices, compare)

		local index = 1
		local prev
		while index <= #choices do
			local current = choices[index]
			if (not prev) or (prev.device.tag ~= current.device.tag) then
				table.insert(choices, index, false)
				index = index + 2
			else
				index = index + 1
			end
			prev = current
		end
	end

	function menu:populate(initial_selection)
		if not choices then
			populate_choices()
		end

		local items = { }

		if title then
			table.insert(items, { title, '', 'off' })
			table.insert(items, { '---', '', '' })
		end

		index_first_choice = #items + 1
		local selection = index_first_choice
		for index, field in ipairs(choices) do
			if field then
				table.insert(items, { field.name, '', '' })
				if initial_selection and (field.port.tag == initial_selection.port.tag) and (field.mask == initial_selection.mask) and (field.type == initial_selection.type) then
					selection = #items
					initial_selection = nil
				end
			else
				local device = choices[index + 1].device
				if device.owner then
					table.insert(items, { string.format(_p('plugin-commonui', '%s [root%s]'), device.name, device.tag), '', 'heading' })
				else
					table.insert(items, { string.format(_p('plugin-commonui', '[root%s]'), device.tag), '', 'heading' })
				end
			end
		end

		table.insert(items, { '---', '', '' })
		table.insert(items, { _p('plugin-commonui', 'Cancel'), '', '' })
		index_cancel = #items

		return items, selection
	end

	function menu:handle(index, event)
		local selection
		if (event == 'back') or ((index == input_item_cancel) and (event == 'select')) then
			action(nil)
			return true
		elseif event == 'select' then
			local field = choices[index - index_first_choice + 1]
			if field then
				action(field)
				return true
			end
		elseif event == 'prevgroup' then
			local found_break = false
			while (index > index_first_choice) and (not selection) do
				index = index - 1
				if not choices[index - index_first_choice + 1] then
					if found_break then
						selection = index + 1
					else
						found_break = true
					end
				end
			end
		elseif event == 'nextgroup' then
			while ((index - index_first_choice + 2) < #choices) and (not selection) do
				index = index + 1
				if not choices[index - index_first_choice + 1] then
					selection = index + 1
				end
			end
		end
		return false, selection
	end

	return menu
end


function commonui.switch_polling_helper(starting_sequence)
	helper = { }

	local machine = manager.machine
	local cancel = machine.ioport:token_to_input_type('UI_CANCEL')
	local cancel_prompt = manager.ui:get_general_input_setting(cancel)
	local input = machine.input
	local uiinput = machine.uiinput
	local poller = input:switch_sequence_poller()
	local modified_ticks = 0

	if starting_sequence then
		poller:start(starting_sequence)
	else
		poller:start()
	end

	function helper:overlay(items, selection, flags)
		if flags then
			flags = flags .. " nokeys"
		else
			flags = "nokeys"
		end
		return items, selection, flags
	end

	function helper:poll()
		-- prevent race condition between uiinput:pressed() and poll()
		if (modified_ticks == 0) and poller.modified then
			modified_ticks = emu.osd_ticks()
		end

		if uiinput:pressed(cancel) then
			-- UI_CANCEL pressed, abort
			machine:popmessage()
			uiinput:reset()
			if (not poller.modified) or (modified_ticks == emu.osd_ticks()) then
				-- cancelled immediately
				self.sequence = nil -- TODO: communicate this better?
				return true
			else
				-- entered something before cancelling
				self.sequence = nil
				return true
			end
		elseif poller:poll() then
			uiinput:reset()
			if poller.valid then
				-- valid sequence entered
				machine:popmessage()
				self.sequence = poller.sequence
				return true
			else
				-- invalid sequence entered
				machine:popmessage(_p('plugin-commonui', 'Invalid combination entered'))
				self.sequence = nil
				return true
			end
		else
			machine:popmessage(string.format(
					_p('plugin-commonui', 'Enter combination or press %s to cancel\n%s'),
					cancel_prompt,
					input:seq_name(poller.sequence)))
			return false
		end
	end

	return helper
end


return exports
