
local minetest, ch_fireworks, ch_buildings, ch_ion_cannon = minetest, ch_fireworks, ch_buildings, ch_ion_cannon

ch_colours = {}

ch_colours.red = 1
ch_colours.yellow = 2
ch_colours.green = 3
ch_colours.blue = 4
ch_colours.purple = 5
ch_colours.black = 6

ch_colours.remote_trigger_at = function(pos, dir)
	local node = minetest.get_node_or_nil(pos)
	if node == nil then
		return
	end
	if node.name == "world:green" or node.name == "world:black" then
		return -- can't be remotely triggered
	end
	ch_colours.trigger(node, pos, dir)
end

ch_colours.cycle = function(pos)
	local old_node = minetest.get_node_or_nil(pos)
	if old_node == nil then
		return
	end
	local old_def = minetest.registered_nodes[old_node.name]
	if old_def.groups.building then
		return
	end
	if old_node.name == "world:red" then
		minetest.set_node(pos, {name = "world:yellow"})
	elseif old_node.name == "world:yellow" then
		minetest.set_node(pos, {name = "world:green"})
	elseif old_node.name == "world:green" then
		minetest.set_node(pos, {name = "world:blue"})
	elseif old_node.name == "world:blue" then
		minetest.set_node(pos, {name = "world:purple"})
	elseif old_node.name == "world:purple" then
		minetest.set_node(pos, {name = "world:black"})
	elseif old_node.name == "world:black" then
		minetest.set_node(pos, {name = "world:red"})
	end
	local new_node = minetest.get_node_or_nil(pos)
	if new_node == nil then
		return
	end
	local def = minetest.registered_nodes[new_node.name]
	if def.trigger_on_set then
		ch_colours.trigger(new_node, pos, 0)
	end
end

ch_colours.trigger = function(node, pos, dir)
	if node.name == "world:green" then
		local a = {x = pos.x +1, y = pos.y, z = pos.z}
		local b = {x = pos.x -1, y = pos.y, z = pos.z}
		local c = {x = pos.x, y = pos.y, z = pos.z+1}
		local d = {x = pos.x, y = pos.y, z = pos.z-1}
		ch_colours.remote_trigger_at(a, 1)
		ch_colours.remote_trigger_at(b, 2)
		ch_colours.remote_trigger_at(c, 3)
		ch_colours.remote_trigger_at(d, 4)
		-- check for building activations
		ch_buildings.check_on_green(pos)
	elseif node.name == "world:blue" then
		minetest.set_node(pos, {name = "world:blue_active"})
		local tm = minetest.get_node_timer(pos)
		tm:start(5)

		minetest.after(0.3, function(pos, dir)
			local a = {x = pos.x +1, y = pos.y, z = pos.z}
			local b = {x = pos.x -1, y = pos.y, z = pos.z}
			local c = {x = pos.x, y = pos.y, z = pos.z+1}
			local d = {x = pos.x, y = pos.y, z = pos.z-1}
			local an = minetest.get_node_or_nil(a)
			if dir == 1 and (an == nil or an.name == "air") then
				minetest.set_node(a, {name = "world:black"})
			else
				ch_colours.remote_trigger_at(a, 1)
			end

			local bn = minetest.get_node_or_nil(b)
			if dir == 2 and (bn == nil or bn.name == "air") then
				minetest.set_node(b, {name = "world:black"})
			else
				ch_colours.remote_trigger_at(b, 2)
			end

			local cn = minetest.get_node_or_nil(c)
			if dir == 3 and (cn == nil or cn.name == "air") then
				minetest.set_node(c, {name = "world:black"})
			else
				ch_colours.remote_trigger_at(c, 3)
			end

			local dn = minetest.get_node_or_nil(d)
			if dir == 4 and (dn == nil or dn.name == "air") then
				minetest.set_node(d, {name = "world:black"})
			else
				ch_colours.remote_trigger_at(d, 4)
			end
		end, pos, dir)
	elseif node.name == "world:black" then
		if dir == 0 then
			-- Cycle color of nearby blocks
			local a = {x = pos.x +1, y = pos.y, z = pos.z}
			local b = {x = pos.x -1, y = pos.y, z = pos.z}
			local c = {x = pos.x, y = pos.y, z = pos.z+1}
			local d = {x = pos.x, y = pos.y, z = pos.z-1}
			ch_colours.cycle(a)
			ch_colours.cycle(b)
			ch_colours.cycle(c)
			ch_colours.cycle(d)
		end
	elseif node.name == "world:purple" then
		local under_pos = {x = pos.x, y = pos.y - 1, z = pos.z}
		local under_node = minetest.get_node_or_nil(under_pos)
		if under_node == nil or under_node.name == "air" then
			-- this should be impossible, but whatever..
			return
		end
		local def = minetest.registered_nodes[under_node.name]
		if def.groups.building then
			-- can't copy building
			return
		end
		if dir == 0 then
			ch_colours.purple_write(pos, nil, under_node)
		elseif dir == 1 then
			local a = {x = pos.x +1, y = pos.y, z = pos.z}
			ch_colours.purple_write(pos, a, under_node)
		elseif dir == 2 then
			local a = {x = pos.x -1, y = pos.y, z = pos.z}
			ch_colours.purple_write(pos, a, under_node)
		elseif dir == 3 then
			local a = {x = pos.x, y = pos.y, z = pos.z+1}
			ch_colours.purple_write(pos, a, under_node)
		elseif dir == 4 then
			local a = {x = pos.x, y = pos.y, z = pos.z-1}
			ch_colours.purple_write(pos, a, under_node)
		end
	elseif node.name == "world:red" then
		local above_pos = {x = pos.x, y = pos.y + 1, z = pos.z}
		local above_node = minetest.get_node_or_nil(above_pos)
		if above_node == nil or above_node.name == "air" then
			minetest.set_node(above_pos, {name = "world:black"})
		else
			local def = minetest.registered_nodes[above_node.name]
			if def and def.groups.yellow and ch_buildings.sky_above(above_pos) then
				-- Shoot rocket!
				ch_fireworks.launch(above_pos, "yellow", false, false)
				minetest.set_node(above_pos, {name = "air"})
				minetest.check_for_falling({x=above_pos.x, y=above_pos.y+1, z=above_pos.z})
			elseif def and def.groups.red then
				-- High altitude rocket?
				local above2_pos = {x = pos.x, y = pos.y + 2, z = pos.z}
				local above2_node = minetest.get_node_or_nil(above2_pos)
				if above2_node ~= nil then
					local def2 = minetest.registered_nodes[above2_node.name]
					if def2 and def2.groups.yellow and ch_buildings.sky_above(above2_pos) then
						-- Shoot high altitude rocket!
						ch_fireworks.launch(above_pos, "red", true, false)
						minetest.set_node(above_pos, {name = "air"})
						minetest.set_node(above2_pos, {name = "air"})
						minetest.check_for_falling({x=above2_pos.x, y=above2_pos.y+1, z=above2_pos.z})
					end
				end
			elseif def and def.groups.blue then
				-- Shield breaking rocket?
				local above2_pos = {x = pos.x, y = pos.y + 2, z = pos.z}
				local above2_node = minetest.get_node_or_nil(above2_pos)
				if above2_node ~= nil then
					local def2 = minetest.registered_nodes[above2_node.name]
					if def2 and def2.groups.yellow and ch_buildings.sky_above(above2_pos) then
						-- Shoot shield breaking rocket!
						ch_fireworks.launch(above_pos, "blue", false, true)
						minetest.set_node(above_pos, {name = "air"})
						minetest.set_node(above2_pos, {name = "air"})
						minetest.check_for_falling({x=above2_pos.x, y=above2_pos.y+1, z=above2_pos.z})
					end
				end
			elseif def and def.groups.purple then
				-- Ion Cannon?!?
				if ch_buildings.check_for_ion_cannon(pos) then
					-- Wahoo!
					ch_ion_cannon.fire(pos)
					local top1 = {x=pos.x, y=pos.y+3, z=pos.z}
					local top2 = {x=pos.x, y=pos.y+2, z=pos.z}
					local top3 = {x=pos.x, y=pos.y+1, z=pos.z}
					minetest.set_node(top1, {name = "air"})
					minetest.set_node(top2, {name = "air"})
					minetest.set_node(top3, {name = "air"})
					minetest.check_for_falling(top1)
					minetest.check_for_falling(top2)
					minetest.check_for_falling(top3)
				end
			end
		end
	elseif node.name == "world:yellow" then
		minetest.set_node(pos, {name = "world:yellowa"})
		local tm = minetest.get_node_timer(pos)
		tm:start(20)
	elseif node.name == "world:yellowa" then
		minetest.set_node(pos, {name = "world:yellowb"})
		local tm = minetest.get_node_timer(pos)
		tm:start(20)
	elseif node.name == "world:yellowb" then
		local yellow_break_particles = {
			node = {name = "world:yellow"},
			size = 0,
			time = 0.05,
			amount = 30,
			minpos = {x = pos.x - 0.5, y = pos.y - 0.5, z = pos.z - 0.5},
			maxpos = {x = pos.x + 0.5, y = pos.y + 0.5, z = pos.z + 0.5},
			minvel = {x = -2, y = -2, z = -2},
			maxvel = {x = 2, y = 2, z = 2},
			minacc = {x = 0, y = -5, z = 0},
			maxacc = {x = 0, y = -5, z = 0},
		}
		minetest.add_particlespawner(yellow_break_particles)
		minetest.set_node(pos, {name = "air"})
		minetest.check_for_falling({x=pos.x, y=pos.y+1, z=pos.z})
	end
end

ch_colours.purple_write = function(purplepos, topos, source_node)
	minetest.set_node(purplepos, {name = "world:purple_active"})
	local tm = minetest.get_node_timer(purplepos)
	tm:start(5)
	local old_node = nil
	if topos ~= nil then
		old_node = minetest.get_node_or_nil(topos)
	end
	if old_node == nil or old_node.name == "air" then
		-- trigger above and below itself
		ch_colours.remote_trigger_at({x=purplepos.x, y=purplepos.y-1, z=purplepos.z}, 0)
		ch_colours.remote_trigger_at({x=purplepos.x, y=purplepos.y+1, z=purplepos.z}, 0)
		return
	end
	local old_def = minetest.registered_nodes[old_node.name]
	if old_def.groups.building then
		return -- can't overwrite building
	end
	minetest.set_node(topos, {name = source_node.name})
	minetest.check_for_falling(topos)
	local new_node = minetest.get_node_or_nil(topos)
	if new_node and minetest.registered_nodes[new_node.name] then
		local new_def = minetest.registered_nodes[new_node.name]
		if new_def.sounds and new_def.sounds.place then
			minetest.sound_play(new_def.sounds.place.name, {
				pos = topos,
				gain = new_def.sounds.place.gain,
				loop = false
			})
		end
		if new_def.trigger_on_set then
			ch_colours.trigger(new_node, topos, 0)
		end
	end
end

local baseheight = 8

ch_colours.set_and_trigger = function(player, node, pos)
	local meta = player:get_meta()
	local col = meta:get_int("colour")
	local colourname = ch_colours.colour_name(col)
	local new_name = "world:" .. colourname
	if node.name ~= new_name then
		local n = minetest.registered_nodes[node.name]
		if new_name == "world:yellow" and pos.y <= baseheight-10 then
			-- can't dig that deep
			return
		end
		if new_name == "world:red" and pos.y >= baseheight+10 then
			-- can't build that high
			return
		end
		if n.groups.building then
			-- can't modify buildings
			if new_name == "world:yellow" then
				-- Break the building?
				ch_buildings.incremental_break(node, pos, 0)
			end
			return
		end
		if new_name ~= "world:yellow" or not n.groups.yellow then
			minetest.set_node(pos, {name = new_name} )
		end
	end
	minetest.check_for_falling(pos)
	local new_node = minetest.get_node_or_nil(pos)
	if new_node and minetest.registered_nodes[new_node.name] then
		local def = minetest.registered_nodes[new_node.name]
		if def.sounds and def.sounds.place then
			minetest.sound_play(def.sounds.place.name, {
				object = player,
				gain = def.sounds.place.gain,
				loop = false
			})
		end
		-- Should we trigger it?
		if def.trigger_on_set and new_node.name ~= node.name then
			ch_colours.trigger(new_node, pos, 0)
		elseif def.trigger_on_jump and new_node.name == node.name then
			ch_colours.trigger(new_node, pos, 0)
		end
	end
end

ch_colours.colour_name = function(color_number)
	local name = "green"
	if color_number == ch_colours.red then
		name = "red"
	elseif color_number == ch_colours.yellow then
		name = "yellow"
	elseif color_number == ch_colours.green then
		name = "green"
	elseif color_number == ch_colours.blue then
		name = "blue"
	elseif color_number == ch_colours.purple then
		name = "purple"
	elseif color_number == ch_colours.black then
		name = "black"
	end
	return name
end


ch_colours.next_color = function(itemstack, player, pointed_thing)
	local meta = player:get_meta()
	local col = meta:get_int("colour")
	local maxcol = meta:get_int("max_col")
	if maxcol == 0 then
		maxcol = ch_colours.green
	end
	col = col + 1
	if col > maxcol then
		col = 1
	end
	meta:set_int("colour", col)
	ch_player_api.set_colour(player, ch_colours.colour_name(col))
end

ch_colours.teleclimb = function(player, pos)
	local meta = player:get_meta()
	local col = meta:get_int("colour")
	if col ~= ch_colours.green then
		-- Only green can tele-climb for now.
		return
	end
	local meta = player:get_meta()
	local teleclimbing_already = meta:get_int("teleclimbing")
	if teleclimbing_already == 1 then
		-- stop spamming! :<
		return
	end
	meta:set_int("teleclimbing", 1)
	minetest.after(0.3, function(player, pos)
		local meta = player:get_meta()
		local col = meta:get_int("colour")
		meta:set_int("teleclimbing", 0)
		if col ~= ch_colours.green then
			return -- You are a sneek!
		end
		local found_height = baseheight+11
		for i=baseheight+11,baseheight-11,-1 do
			local n = minetest.get_node_or_nil({x=pos.x, y=i, z=pos.z})
			if n == nil or n.name == "air" then
				found_height = i
			else
				break
			end
		end
		player:set_pos({x=pos.x, y=found_height, z=pos.z})
	end, player, pos)
end

-- Modify "hand"
minetest.override_item("", {
	on_use = ch_colours.next_color,
	on_place = function(itemstack, placer, pointed_thing)
		ch_colours.teleclimb(placer, pointed_thing.under)
	end,
	on_secondary_use = function(itemstack, user, pointed_thing)
		ch_colours.teleclimb(user, user:get_pos())
	end,
	wield_image = "colours_blank.png"
})
