-- LUALOCALS < ---------------------------------------------------------
local addgroups, get_mod_api, math, minetest, pairs, piredo_map,
      piredo_player, posrel, string
    = addgroups, get_mod_api, math, minetest, pairs, piredo_map,
      piredo_player, posrel, string
local math_random, string_sub
    = math.random, string.sub
-- LUALOCALS > ---------------------------------------------------------

local api = get_mod_api()

local modstore = minetest.get_mod_storage()
local codekey = "clock_room_code"
local code_solution = "27641"

local state_get, state_incr
do
	local states = {"1200", "600", "300", "330", "630", "900", "930"}
	local statekey = "clock_room_state"
	state_get = function()
		local state = modstore:get_int(statekey)
		if state < 1 then state = 1 end
		state = ((state - 1) % #states) + 1
		return state, states[state]
	end
	state_incr = function()
		local state = state_get()
		state = (state % #states) + 1
		modstore:set_int(statekey, state)
	end
end

local clock_sound
do
	local cache = {}
	local function playat(pos)
		cache[minetest.sound_play("piredo_puzzles_metal_clang",
			{pos = pos, gain = 1.5, pitch = 0.9 + math_random() * 0.2})] = true
	end
	clock_sound = function(rel)
		for k in pairs(cache) do minetest.sound_fade(k, 1, 0) end
		playat(rel(8, 0, 3))
		playat(rel(3, 0, 8))
		playat(rel(-2, 0, 3))
		playat(rel(3, 0, -2))
	end
end

local function placeclock(roompos, silent)
	local _, schem = state_get()
	local rel = posrel(roompos)

	local items = {}
	for dz = 3, 12 do
		for dy = 0, 1 do
			for dx = 3, 12 do
				local np = rel(dx, dy, dz)
				local node = minetest.get_node(np)
				if minetest.get_item_group(
					node.name, "collectable") > 0 then
					items[dz] = items[dz] or {}
					items[dz][dx] = node
				end
			end
		end
	end

	piredo_map.putschem(rel(3, 0, 3), "clock_" .. schem)
	if not silent then clock_sound(rel) end

	for dz = 3, 12 do
		local iz = items[dz]
		if iz then
			for dx = 3, 12 do
				local item = iz[dx]
				if item then
					local sp = rel(dx, 0, dz)
					local ap = rel(dx, 1, dz)
					local nn = minetest.get_node(sp)
					if nn.name ~= "air" then
						minetest.set_node(ap, item)
						piredo_map.savechange(sp, nn, "clock meta")
					else
						minetest.set_node(sp, item)
						minetest.remove_node(ap)
					end
				end
			end
		end
	end
end

do
	local oldset = piredo_map.set_current_room
	function piredo_map.set_current_room(pos, room, ...)
		if room == "large_clock_room" then
			placeclock(posrel(pos)(13, 1, 12), true)
		end
		return oldset(pos, room, ...)
	end
end

minetest.override_item("piredo_terrain:piranesi__clock_button_input", {
		on_punch = function(pos, node)
			local state = state_get()
			local code = state .. string_sub(modstore:get_string(codekey)
				or "", 1, 4)
			modstore:set_string(codekey, code)

			minetest.sound_play("default_dug_metal", {pos = pos}, true)
			if code == code_solution then
				piredo_player.discover("solve:clock_room")
				api.magic(pos, true)
				return minetest.set_node(pos, {
						name = "piredo_terrain:piranesi__time_totem",
						param2 = 1
					})
			else
				api.magic(pos)
			end

			node.name = node.name .. "_active"
			minetest.set_node(pos, node)
		end
	})

minetest.override_item("piredo_terrain:piranesi__clock_button_change", {
		on_punch = function(pos, node)
			state_incr()
			placeclock(pos)
			minetest.sound_play("default_dug_metal", {pos = pos}, true)
			node.name = node.name .. "_active"
			minetest.set_node(pos, node)
		end
	})

for k in pairs({input = 1, change = 1}) do
	local myname = "piredo_terrain:piranesi__clock_button_" .. k .. "_active"
	addgroups(myname, "button_active")
	minetest.override_item(myname, {
			on_timer = function(pos)
				minetest.set_node(pos, {
						name = "piredo_terrain:piranesi__clock_button_" .. k
					})
			end,
			on_construct = function(pos)
				minetest.get_node_timer(pos):start(1)
			end
		})
end
minetest.register_abm({
		nodenames = {"group:button_active"},
		interval = 5,
		chance = 1,
		action = function(pos)
			minetest.get_node_timer(pos):start(1)
		end
	})
