local ms = minetest.get_mod_storage()

-- V
local bmsgs = {"Bye ", "Bye bye ", "See you ", "Cya ", "Until next time "}
local wmsgs = {"Hey ", "Hello ", "Hi ", "Heyo ", "Hii ", "Hai "}
local lmsgs = {"Oops! ", "Oh no! ", "Ouch! ", "Uh oh... ", "Oof... ", "Sigh... "}
local gmsgs = {"Yay!", "Well done!", "Good job!", "Nice one!", "Hooray!", "Congrats!", "Not bad!"}
local mines = {}
local hits = 0
local stime =  os.time(os.date('*t'))
local mtbl = {}

local function gsay(msg)
	minetest.chat_send_all(minetest.colorize("#FCF1AC", ">>> " .. msg))
end

local function gdm(name, msg)
	minetest.chat_send_player(name, minetest.colorize("#FCF1AC", ">>> " .. msg))
end

local function generate_board() -- {-5,1,-5}, {-5,11,5}
	minetest.place_schematic({x=-5,y=1,z=-5}, minetest.get_modpath("ls_game") .. "/schems/wall.mts", "0", {}, true)
	hits = 0
	local total = 0
	mines = {}
	while total < 15 do
  		local pos = {x = -5, y = math.random(1,11), z = math.random(-5,5)}
  		local hash = minetest.hash_node_position(pos)
		if not mines[hash] then
			mines[hash] = pos
			total = total + 1
		end
	end
end

local function check_win()
	local nmines = 0
	for _ in pairs(mines) do
		nmines = nmines + 1
	end
	local tohit = 121 - nmines
	if hits == tohit then
		gsay("You won!")
		gsay(os.date("Time: %M:%S", os.time(os.date('*t'))-stime))
		for k, v in pairs(mines) do
			minetest.set_node(v, {name="ls_game:flag"})
		end
		minetest.after(3, function()
			gsay("New game...")
			generate_board()
		end)
	elseif hits == 1 then
		stime =  os.time(os.date('*t'))
	end
end

local function check_node(pos, puncher)
	local pname = puncher:get_player_name()
	local mcount = 0
	for k, v in pairs(mines) do
		if minetest.pos_to_string(pos) == minetest.pos_to_string(v) then
			mcount = -1
			minetest.sound_play("wrong", {}, true)
			minetest.set_node(pos, {name="ls_game:mine"})
			gsay(lmsgs[math.random(1, #lmsgs)] .. "That was a mine...")
			local postbl, _ = minetest.find_nodes_in_area({x=-5,y=1,z=-5}, {x=-5,y=11,z=5}, {"ls_game:q"})
			for k, v in pairs(postbl) do
				minetest.set_node(v, {name="ls_game:late"})
			end
			for k, v in pairs(mines) do
				minetest.set_node(v, {name="ls_game:mine"})
			end
			minetest.after(3, function()
				gsay("New game...")
				generate_board()
			end)
			break
		else
			if minetest.pos_to_string(v) == minetest.pos_to_string({x=pos.x, y=pos.y+1, z=pos.z}) or
				minetest.pos_to_string(v) == minetest.pos_to_string({x=pos.x, y=pos.y-1, z=pos.z}) or
				minetest.pos_to_string(v) == minetest.pos_to_string({x=pos.x, y=pos.y+1, z=pos.z+1}) or
				minetest.pos_to_string(v) == minetest.pos_to_string({x=pos.x, y=pos.y+1, z=pos.z-1}) or
				minetest.pos_to_string(v) == minetest.pos_to_string({x=pos.x, y=pos.y-1, z=pos.z+1}) or
				minetest.pos_to_string(v) == minetest.pos_to_string({x=pos.x, y=pos.y-1, z=pos.z-1}) or
				minetest.pos_to_string(v) == minetest.pos_to_string({x=pos.x, y=pos.y, z=pos.z+1}) or
				minetest.pos_to_string(v) == minetest.pos_to_string({x=pos.x, y=pos.y, z=pos.z-1})
			then
				mcount = mcount + 1
			end
		end
	end
	if mcount > 0 then
		minetest.set_node(pos, {name="ls_game:" .. tostring(mcount)})
		minetest.sound_play("plop", {}, true)
		hits = hits + 1
	elseif mcount == 0 then
		minetest.set_node(pos, {name="ls_game:dead"})
		minetest.sound_play("plop", {}, true)
		hits = hits + 1
	end
	check_win()
end

local function edit_pts(name, pts)
	local pstats = minetest.deserialize(ms:get("pts")) or {}
	if pstats[name] then
		if pstats[name] + pts >= 0 then
			pstats[name] = pstats[name] + pts
		else
			pstats[name] = 0
		end
	else
		if pts >= 0 then
			pstats[name]= pts
		end
	end
	ms:set_string("pts", minetest.serialize(pstats))
end

local function get_pts(name)
	local pstats = minetest.deserialize(ms:get("pts")) or {}
	if pstats[name] then
		return true, pstats[name]
	else
		return false, -1
	end
end

-- ON JOIN/LEAVE
minetest.register_on_prejoinplayer(function(name, ip)
	if #minetest.get_connected_players() >= 1 then
		return "There are already " .. plimit .. " players in this game, please try again later."
	end
end)

minetest.register_on_joinplayer(function(player, last_login)
	local pname = player:get_player_name()
	minetest.place_schematic({x=-5,y=1,z=-5}, minetest.get_modpath("ls_game") .. "/schems/wall.mts", "0", {}, true)
	minetest.place_schematic({x=-1,y=-1,z=-6}, minetest.get_modpath("ls_game") .. "/schems/barrier.mts", "0", {}, true)
	generate_board()
	player:hud_set_flags({hotbar=false, healthbar=false, wielditem=false, breathbar=false, minimap=false, minimap_radar=false})
	player:set_pos({x=0,y=6,z=0})
	minetest.set_player_privs(pname, {fly=true, noclip=false})
	player:set_look_horizontal(math.pi/2)
	player:override_day_night_ratio(1)
	gdm(pname, "Welcome to the game! Need help? Try /luckysweeper")
	if ms:get_string("m_"..pname) ~= "n" then
		ms:set_string("m_"..pname, "y")
		mtbl[pname] = minetest.sound_play("sparks", {to_player=pname, gain=0.1, loop=true})
	else
		minetest.chat_send_player(pname, minetest.colorize("#FCF1AC", "[Music] OFF, /music to turn it on"))
	end
end)

-- JOIN/LEAVE/MSGS
function minetest.send_leave_message(pname, timed_out)
	gsay("*** " .. bmsgs[math.random(1, #bmsgs)] .. pname)
end

function minetest.send_join_message(pname)
	gsay("*** " .. wmsgs[math.random(1, #wmsgs)] .. pname)
end

-- NODES
minetest.register_node("ls_game:barrier", {
	description = "Barrier Node",
	tiles = {"ls_barrier.png"},
	use_texture_alpha = true,
	pointable = false
})

minetest.register_node("ls_game:q", {
	description = "Q Node",
	tiles = {"ls_q.png"},
	on_punch = function(pos, node, puncher, pointed_thing)
		check_node(pos, puncher)
	end,
	on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
		minetest.set_node(pos, {name="ls_game:flag"})
	end
})

minetest.register_node("ls_game:dead", {
	description = "Dead Node",
	tiles = {"ls_dead.png"},
})

minetest.register_node("ls_game:late", {
	description = "Late Node",
	tiles = {"ls_late.png"},
})

minetest.register_node("ls_game:mine", {
	description = "Mine Node",
	tiles = {"ls_mine.png"},
})

minetest.register_node("ls_game:flag", {
	description = "Flagged Node",
	tiles = {"ls_flag.png"},
	on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
		minetest.set_node(pos, {name="ls_game:q"})
	end
})

minetest.register_node("ls_game:1", {
	description = "Node 1",
	tiles = {"ls_1.png"}
})

minetest.register_node("ls_game:2", {
	description = "Node 2",
	tiles = {"ls_2.png"}
})

minetest.register_node("ls_game:3", {
	description = "Node 3",
	tiles = {"ls_3.png"}
})

minetest.register_node("ls_game:4", {
	description = "Node 4",
	tiles = {"ls_4.png"}
})

minetest.register_node("ls_game:5", {
	description = "Node 5",
	tiles = {"ls_5.png"}
})

minetest.register_node("ls_game:6", {
	description = "Node 6",
	tiles = {"ls_6.png"}
})

minetest.register_node("ls_game:7", {
	description = "Node 6",
	tiles = {"ls_7.png"}
})

minetest.register_node("ls_game:8", {
	description = "Node 8",
	tiles = {"ls_8.png"}
})

minetest.override_item("", {
	range = 20
})

-- FS
local ls_content = minetest.colorize("#FCF1AC", "LuckySweeper Help\n\n") ..
minetest.colorize("#FCF1AC", "How do I play this game?\n") .."Your goal is to guess which cube needs to be hit last. If the cube you hit turns grey it's all good, you can go on.\n" ..
"If the cube turns red, then you've hit this cube too early... If the cube turns green you succeeded! A new game\nwill start shortly.\n" ..
minetest.colorize("#FCF1AC", "\nCan I play this with friends?\n") ..
"Yes you can! The player limit can be tweaked in the game's settings. Keep in mind that it can get a bit crowded\nand thus confusing when too many players are in game at the same time.\n" ..
minetest.colorize("#FCF1AC", "\nAre there different levels?\n") ..
"No, unfortunately not yet. However this could be coming in a future update.\nStay tuned!\n" ..
minetest.colorize("#FCF1AC", "\nHow can I compete with others?\n") ..
"For every cube that turns grey you receive 2 points. For every cube that turns green you get 0 points. BUT!\nIf you hit a cube and it turns red you lose 3 points!\nCheck your own or someone else's score by tpying /pts <name>\n" ..
minetest.colorize("#FCF1AC", "\nWhat else do I need to know?\n") ..
"Make sure to be a lucky dude! Apart from that? Not much really, GLHF!"

local ls_help = "formspec_version[4]" ..
"size[18,12]" ..
"no_prepend[]" ..
"bgcolor[#000000AA;false]" ..
"style_type[button;bgcolor=#00FFCC]" ..
"position[0.5,0.5]" ..
"anchor[0.5,0.5]" ..
"label[0.3,0.5;" .. ls_content .."]" ..
"style_type[button_exit;bgcolor=#FF0000]" ..
"button_exit[17.3,0.2;0.5,0.5;exit;-]"

-- PRIVS
minetest.register_privilege("ls_admin", {
	description = "Administrate luckysweeper (points)",
	give_to_singleplayer = false
})

-- CMDS
minetest.register_chatcommand("st", {
	description = "TEST",
	func = function(name)
		local ctime =  os.time(os.date('*t'))
		gsay(ctime)
	end
})

minetest.register_chatcommand("luckysweeper", {
	description = "Get help on how to play the game",
	func = function(name)
		gdm(name, "Displaying help...")
		minetest.show_formspec(name, "ls_game:ls_help", ls_help)
	end
})

minetest.register_chatcommand("pts", {
	description = "Show score.",
	params = "[<name>]",
	func = function(name, param)
		if param == "" or nil then
			param = name
		end
		local _, pts = get_pts(param)
		if pts == -1 then
			gdm(name, "Player " .. param .. " has no records.")
		else
			gdm(name, "Points of " .. param .. ": " .. pts)
		end
	end
})

minetest.register_chatcommand("reset_pts", {
	description = "Reset score.",
	params = "[<name>]",
	func = function(name, param)
		if param == "" or param == name then
			param = name
		elseif not minetest.check_player_privs(name, {ld_admin=true}) then
			return
			gdm(name, "Insufficient privileges!")
		end
		local _, pts = get_pts(param)
		if pts == -1 then
			gdm(name, "Player " .. param .. " has no records.")
		else
			edit_pts(param, pts*-1)
			gdm(name, "Score of " .. param .. " has been reset!")
		end
	end
})

minetest.register_chatcommand("music", {
	description = "Toggle music.",
	func = function(name)
		if ms:get_string("m_"..name) == "y" then
			minetest.sound_stop(mtbl[name])
			mtbl[name] = nil
			ms:set_string("m_"..name, "n")
			minetest.chat_send_player(name, minetest.colorize("#FCF1AC", "[Music] OFF"))
		else
			ms:set_string("m_"..name, "y")
			mtbl[name] = minetest.sound_play("sparks", {to_player=name, gain=0.1, loop=true})
			minetest.chat_send_player(name, minetest.colorize("#FCF1AC", "[Music] ON"))
		end
	end
})