
local S = core.get_translator(core.get_current_modname())

local has_pipeworks = core.get_modpath("pipeworks")

local function get_pipeworks_fs(x, y, meta)
	-- Use a container to reposition the pipeworks button.
	return "container["..x..","..(y - 4.3).."]"..
		pipeworks.fs_helpers.cycling_button(
			meta,
			pipeworks.button_base,
			"splitstacks",
			{
				pipeworks.button_off,
				pipeworks.button_on
			}
		)..pipeworks.button_label.."container_end[]"
end

local function get_color_fs(x, y, meta)
	local fs = ""
	for a = 0, 3 do
		for b = 0, 3 do
			fs = fs.."image_button["..(x + b * 0.73)..","..(y + 0.1 + a * 0.79)..";0.8,0.8;"..
				"technic_colorbutton"..(a * 4 + b)..".png;color_button"..(a * 4 + b + 1)..";]"
		end
	end
	local selected = meta:get_int("color")
	local color
	if technic.chests.colors[selected] then
		color = technic.chests.colors[selected][2]
	else
		color = S("None")
	end
	return fs.."label["..(x + 0.1)..","..(y + 3.4)..";"..S("Selected Color: @1", color).."]"
end

local function get_quickmove_fs(x, y)
	return "button["..x..","..y..";3,1;existing_to_chest;"..S("Move existing to Chest").."]"..
		"label["..(x + 0.1)..","..(y + 1.15)..";"..S("Move specific")..":\n("..S("Drop to move")..")]"..
		"list[context;quickmove;"..(x + 1.8)..","..(y + 1.15)..";1,1]"..
		"button["..x..","..(y + 2.3)..";3,1;all_to_chest;"..S("Move all to Chest").."]"..
		"button["..x..","..(y + 3.2)..";3,1;all_to_inv;"..S("Move all to Inventory").."]"
end

local function get_digilines_fs(x, y, meta)
	local channel = core.formspec_escape(meta:get_string("channel"))
	local put = meta:get_int("send_put") == 1 and "true" or "false"
	local take = meta:get_int("send_take") == 1 and "true" or "false"
	local inject = meta:get_int("send_inject") == 1 and "true" or "false"
	local pull = meta:get_int("send_pull") == 1 and "true" or "false"
	local overflow = meta:get_int("send_overflow") == 1 and "true" or "false"
	return "field["..(x + 0.3)..","..(y + 0.5)..";3,1;channel;Digiline Channel:;"..channel.."]"..
		"button["..(x + 0.5)..","..(y + 1.1)..";2,1;save_channel;Save Channel]"..
		"checkbox["..(x + 0.1)..","..(y + 1.8)..";send_put;"..S("Send player put messages")..";"..put.."]"..
		"checkbox["..(x + 0.1)..","..(y + 2.2)..";send_take;"..S("Send player take messages")..";"..take.."]"..
		"checkbox["..(x + 0.1)..","..(y + 2.6)..";send_inject;"..S("Send tube inject messages")..";"..inject.."]"..
		"checkbox["..(x + 0.1)..","..(y + 3.0)..";send_pull;"..S("Send tube pull messages")..";"..pull.."]"..
		"checkbox["..(x + 0.1)..","..(y + 3.4)..";send_overflow;"..S("Send overflow messages")..";"..overflow.."]"
end

local function get_infotext_fs(editing, meta)
	local infotext = core.formspec_escape(meta:get_string("infotext"))
	if editing then
		return "image_button[0,0.1;0.8,0.8;technic_checkmark_icon.png;save_infotext;]"..
			"field[1,0.3;4,1;infotext;;"..infotext.."]"
	else
		return "image_button[0,0.1;0.8,0.8;technic_pencil_icon.png;edit_infotext;]"..
			"label[1,0;"..infotext.."]"
	end
end

local function get_autosort_fs(x, meta)
	if meta:get_int("autosort") == 1 then
		return "button["..x..",0;2,1;autosort;"..S("Auto-sort is On").."]"
	else
		return "button["..x..",0;2,1;autosort;"..S("Auto-sort is Off").."]"
	end
end

local function get_sort_fs(x, meta)
	local mode = meta:get_int("sort_mode")
	local fs = "button["..(x + 2)..",0;1,1;sort;"..S("Sort").."]"
	if mode == 1 then
		return fs.."button["..x..",0;2,1;sort_mode;"..S("Sort by Quantity").."]"
	elseif mode == 2 then
		return fs.."button["..x..",0;2,1;sort_mode;"..S("Sort by Type").."]"
	elseif mode == 3 then
		return fs.."button["..x..",0;2,1;sort_mode;"..S("Sort by Wear").."]"
	elseif mode == 4 then
		return fs.."button["..x..",0;2,1;sort_mode;"..S("Natural sort").."]"
	else
		return fs.."button["..x..",0;2,1;sort_mode;"..S("Sort by Item").."]"
	end
end

function technic.chests.get_formspec(data)
	local formspec = {}
	local top_width = (data.infotext and 6 or 0) + (data.autosort and 2 or 0) + (data.sort and 3 or 0)
	local bottom_width = (data.quickmove and 3 or 0) + ((data.color or data.digilines) and 3 or 0) + 8
	local width = math.max(top_width, bottom_width, data.width)
	local padding = (width - bottom_width) / 2
	if data.quickmove and (data.color or data.digilines) then
		padding = (width - bottom_width) / 4
	elseif data.quickmove or data.color or data.digilines then
		padding = (width - bottom_width) / 3
	end
	local player_inv_left = padding
	if data.quickmove then
		player_inv_left = padding + 3 + padding
	end
	local player_inv_top = data.height + (has_pipeworks and 1.6 or 1.3)
	local height = data.height + (has_pipeworks and 5.4 or 5.1)
	local chest_inv_left = (width - data.width) / 2
	formspec.base =
		"size["..width..","..height.."]"..
		"list[context;main;"..chest_inv_left..",1;"..data.width..","..data.height..";]"..
		"list[current_player;main;"..player_inv_left..","..player_inv_top..";8,4;]"..
		"listring[context;main]"..
		"listring[current_player;main]"..
		default.get_hotbar_bg(player_inv_left, player_inv_top)
	if data.quickmove then
		formspec.base = formspec.base..get_quickmove_fs(padding, data.height + 1.2)
	end
	formspec.padding = padding
	formspec.width = width
	return formspec
end

function technic.chests.update_formspec(pos, data, edit_infotext)
	local formspec = data.formspec.base
	local meta = core.get_meta(pos)
	if data.infotext then
		formspec = formspec..get_infotext_fs(edit_infotext, meta)
	end
	if data.sort then
		formspec = formspec..get_sort_fs(data.formspec.width - 3, meta)
		if data.autosort then
			formspec = formspec..get_autosort_fs(data.formspec.width - 5, meta)
		end
	end
	if has_pipeworks then
		local offset = data.quickmove and (data.formspec.padding * 2 + 3) or data.formspec.padding
		formspec = formspec..get_pipeworks_fs(offset, data.height + 1, meta)
	end
	if data.color or data.digilines then
		local offset = data.quickmove and (data.formspec.padding * 3 + 11) or (data.formspec.padding * 2 + 8)
		if data.color then
			formspec = formspec..get_color_fs(offset, data.height + 1.2, meta)
		else
			formspec = formspec..get_digilines_fs(offset, data.height + 1.2, meta)
		end
	end
	meta:set_string("formspec", formspec)
end

function technic.chests.get_receive_fields(nodename, data)
	return function(pos, formname, fields, player)
		if not fields or not player then
			return
		end
		local meta = core.get_meta(pos)
		local chest_inv = meta:get_inventory()
		local player_inv = player:get_inventory()
		if fields.quit then
			if meta:get_int("autosort") == 1 then
				technic.chests.sort_inv(chest_inv, meta:get_int("sort_mode"))
			end
			technic.chests.update_formspec(pos, data)
			return
		end
		if not technic.chests.change_allowed(pos, player, data.locked, data.protected) then
			return
		end
		if data.sort and fields.sort then
			technic.chests.sort_inv(chest_inv, meta:get_int("sort_mode"))
			return
		end
		if data.quickmove then
			if fields.all_to_chest then
				local moved_items = technic.chests.move_inv(player_inv, chest_inv)
				if data.digilines and meta:get_int("send_put") == 1 then
					technic.chests.send_digiline_message(pos, "put", player, moved_items)
				end
				technic.chests.log_inv_change(pos, player:get_player_name(), "put", "stuff")
				return
			elseif fields.all_to_inv then
				local moved_items = technic.chests.move_inv(chest_inv, player_inv)
				if data.digilines and meta:get_int("send_take") == 1 then
					technic.chests.send_digiline_message(pos, "take", player, moved_items)
				end
				technic.chests.log_inv_change(pos, player:get_player_name(), "take", "stuff")
				return
			elseif fields.existing_to_chest then
				local items = technic.chests.get_inv_items(chest_inv)
				local moved_items = technic.chests.move_inv(player_inv, chest_inv, items)
				if data.digilines and meta:get_int("send_put") == 1 then
					technic.chests.send_digiline_message(pos, "put", player, moved_items)
				end
				technic.chests.log_inv_change(pos, player:get_player_name(), "put", "stuff")
				return
			end
		end
		if not technic.chests.change_allowed(pos, player, data.locked, true) then
			return  -- Protect settings from being changed, even for open chests
		end
		if has_pipeworks then
			pipeworks.fs_helpers.on_receive_fields(pos, fields)
		end
		if data.sort and fields.sort_mode then
			local value = meta:get_int("sort_mode")
			meta:set_int("sort_mode", (value + 1) % 5)
		end
		if data.autosort and fields.autosort then
			local value = meta:get_int("autosort") == 1 and 0 or 1
			meta:set_int("autosort", value)
		end
		if data.color then
			for i = 1, 16 do
				if fields["color_button"..i] then
					local node = core.get_node(pos)
					if technic.chests.colors[i] then
						node.name = nodename.."_"..technic.chests.colors[i][1]
					else
						node.name = nodename
					end
					core.swap_node(pos, node)
					meta:set_int("color", i)
					break
				end
			end
		end
		if data.digilines then
			if fields.save_channel and fields.channel then
				meta:set_string("channel", fields.channel)
			end
			for _,setting in pairs({"send_put", "send_take", "send_inject", "send_pull", "send_overflow"}) do
				if fields[setting] then
					local value = fields[setting] == "true" and 1 or 0
					meta:set_int(setting, value)
				end
			end
		end
		if data.infotext then
			if fields.edit_infotext then
				technic.chests.update_formspec(pos, data, true)
				return
			elseif fields.save_infotext and fields.infotext then
				meta:set_string("infotext", fields.infotext)
			end
		end
		technic.chests.update_formspec(pos, data)
	end
end
