local S = core.get_translator("cow_game")

cow_game = {}

local countdown = nil
local countdown_job
local game_message_job

local camera_gui_active = false

local game_timer = 0
local game_timer_active = false
local game_timer_timer = 0

cow_game.STATE_MENU = 0
cow_game.STATE_PREPLAY = 1
cow_game.STATE_PLAY = 2
cow_game.STATE_TIMEOUT = 3
cow_game.STATE_GAME_OVER = 4
cow_game.STATE_RESULTS = 5

local START_LIVES = 3
local STATE_TIME_TIMEOUT = 3.0
local STATE_TIME_GAME_OVER = 3.0
local IS_PROTECTED_FOR = 3.0

local current_lives = 3
local is_protected = false
local is_protected_for = 0

local game_state = cow_game.STATE_MENU

local score = 0

function cow_game.set_state(state)
	game_state = state
	core.log("action", "[cow_game] Game state set to: "..state)
end
function cow_game.get_state()
	return game_state
end

function cow_game.add_score(player, add_score, particle_position)
	if add_score <= 0 then
		core.log("error", "[cow_game] add_score called with non-positive score!")
		return
	end
	if particle_position then
		cow_gui.show_score_in_world(particle_position, add_score)
	end

	cow_game.set_score(player, cow_game.get_score(player)+add_score, true)
end

function cow_game.set_score(player, new_score, show)
	score = math.floor(math.max(0, new_score))
	if show then
		cow_gui.show_score(player, score)
	end
end
function cow_game.get_score(player)
	return score
end

function cow_game.start_timer(player, seconds)
	game_timer_active = true
	game_timer = seconds
	game_timer_timer = 0
	cow_gui.show_time(player, seconds)
end

function cow_game.stop_timer(player)
	game_timer_active = false
	cow_gui.hide_time(player)
end

function cow_game.start_countdown(player, callback_start)
	countdown = 4
	local function update_countdown(player)
		if not player or not player:is_player() then
			return
		end
		if countdown then
			countdown = countdown - 1
			if countdown == 0 then
				core.sound_play({name="cow_game_countdown_go", gain=0.8}, {to_player=player:get_player_name()}, true)
				if callback_start then
					callback_start()
				end
			elseif countdown < 0 then
				cow_gui.hide_countdown(player)
				return
			end
			if countdown > 0 then
				core.sound_play({name="cow_game_countdown", gain=0.8}, {to_player=player:get_player_name()}, true)
			end
			cow_gui.show_countdown(player, countdown)
			countdown_job = core.after(1, update_countdown, player)
		end
	end
	countdown_job = core.after(1, update_countdown, player)
end

function cow_game.cancel_countdown(player)
	if countdown_job then
		countdown_job:cancel()
		countdown_job = nil
	end
	local holder = player:get_attach()
	if holder then
		player:set_detach()
		holder:remove()
	end
	cow_gui.hide_countdown(player)
end

function cow_game.stop_play(player)
	cow_game.set_state(cow_game.STATE_MENU)
	cow_game.stop_timer(player)
	cow_game.cancel_countdown(player)
	cow_gui.hide_score(player)
	cow_gui.hide_lives(player)
	cow_gui.hide_boost(player)
end

function cow_game.start_play(player, settings)
	if not core.global_exists("cow_objects") then
		core.log("error", "[cow_game] cow_game.start_play must not be called before mod init!")
		return
	end
	cow_player.set_texture_mode(player, "normal")
	player:set_camera({mode="third"})
	cow_game.stop_timer(player)
	cow_game.cancel_countdown(player)

	cow_game.set_state(cow_game.STATE_PREPLAY)
	cow_game.set_score(player, 0, true)
	cow_gui.show_time(player, settings.time)
	cow_game.set_lives(player, START_LIVES)
	cow_game.set_protected(player, false)

	cow_levels.init_level(settings.level)
	cow_levels.go_to_level(player, settings.level)
	local spawn_pos = cow_levels.get_level_spawn_pos(settings.level)

	local ent = core.add_entity(spawn_pos, "cow_objects:player_holder")
	if ent then
		local level_def = cow_levels.get_level_def(settings.level)
		-- FIXME: Cow doesn't rotate correctly with non-zero level yaw
		local yaw = level_def.yaw
		if not yaw then
			yaw = 0
		end
		local yawdeg = (yaw/math.pi*2) * 360
		player:set_attach(ent, nil, nil, {x=0,y=yawdeg,z=0})
	else
		core.log("error", "[cow_game] Failed to spawn cow holder for "..player:get_player_name())
	end

	local after_countdown = function()
		cow_game.set_state(cow_game.STATE_PLAY)
		if player and player:is_player() then
			cow_game.start_timer(player, settings.time)
			local holder = player:get_attach()
			if holder then
				player:set_detach()
				core.after(0, function()
					if player and player:is_player() then
						cow_levels.go_to_level(player, settings.level, false)
						cow_gui.show_boost(player, "recharging")
					end
				end)
				holder:remove()
			end
		end
	end
	cow_game.start_countdown(player, after_countdown)
end

function cow_game.set_lives(player, lives)
	current_lives = math.max(lives, 0)
	cow_gui.show_lives(player, current_lives)
end
function cow_game.get_lives(player)
	return lives
end
function cow_game.is_protected(player)
	return is_protected
end
function cow_game.set_protected(player, state)
	if state == true then
		is_protected = true
		is_protected_for = IS_PROTECTED_FOR
	else
		is_protected = false
		is_protected_for = 0
	end
end

function cow_game.die(player)
	current_lives = math.max(current_lives - 1, 0)
	cow_gui.show_lives(player, current_lives)
	core.sound_play({name="cow_game_lose_life", gain=0.9}, {to_player=player:get_player_name()}, true)
	if current_lives <= 0 then
		cow_game.game_over(player)
		cow_player.set_texture_mode(player, "dead")
	else
		cow_player.set_texture_mode(player, "damaged")
		cow_game.set_protected(player, true)
	end
end
function cow_game.die_if_unprotected(player)
	if not cow_game.is_protected(player) then
		cow_game.die(player)
		return
	end
end

function cow_game.game_over(player)
	cow_gui.hide_boost(player)
	cow_game.set_state(cow_game.STATE_GAME_OVER)
	game_timer = -STATE_TIME_TIMEOUT

	if game_message_job then
		game_message_job:cancel()
	end
	cow_gui.show_game_message(player, "game_over")
	core.sound_play({name="cow_game_game_over", gain=0.5}, {to_player=player:get_player_name()}, true)
	local game_message_job = core.after(STATE_TIME_GAME_OVER, function(player)
		cow_gui.hide_game_message(player)
		game_message_job = nil
	end, player)
end

local timer = 0
core.register_globalstep(function(dtime)
	timer = timer + dtime
	if game_timer_active then
		game_timer_timer = game_timer_timer + dtime
	end
	-- Reduce globalstep load
	if timer < 0.1 and game_timer_timer < 1 then
		return
	end

	local state = cow_game.get_state()
	local players = core.get_connected_players()
	for p=1, #players do
		if state == cow_game.STATE_PLAY and is_protected then
			is_protected_for = is_protected_for - (dtime + timer)
			if is_protected_for <= 0 then
				is_protected = false
				is_protected_for = 0
				cow_player.set_texture_mode(players[p], "normal")
			end
		end
	end

	timer = 0
	local update_game_timer = false
	if game_timer_active and game_timer_timer >= 1 then
		game_timer_timer = 0
		update_game_timer = true
	end

	if update_game_timer then
		game_timer = math.max(- (STATE_TIME_TIMEOUT + STATE_TIME_GAME_OVER), game_timer - 1)
		local entering_results = false
		if state == cow_game.STATE_PLAY and game_timer == 0 then
			cow_game.set_state(cow_game.STATE_TIMEOUT)
		elseif state == cow_game.STATE_TIMEOUT and game_timer == -STATE_TIME_TIMEOUT then
			cow_game.set_state(cow_game.STATE_GAME_OVER)
		elseif state == cow_game.STATE_GAME_OVER and game_timer == -(STATE_TIME_TIMEOUT+STATE_TIME_GAME_OVER) then
			cow_game.set_state(cow_game.STATE_RESULTS)
			entering_results = true
		end
		state = cow_game.get_state()

		for p=1, #players do
			local player = players[p]

			if state == cow_game.STATE_PLAY and game_timer >= 1 and game_timer <= 10 then
				core.sound_play({name="cow_game_countdown_final", gain=0.4}, {to_player=player:get_player_name()}, true)
			elseif state == cow_game.STATE_TIMEOUT and game_timer == 0 then
				if game_message_job then
					game_message_job:cancel()
				end
				cow_gui.show_game_message(player, "timeout")
				core.sound_play({name="cow_game_timeout", gain=0.5}, {to_player=player:get_player_name()}, true)
				local game_message_job = core.after(STATE_TIME_TIMEOUT, function(player)
					cow_gui.hide_game_message(player)
					game_message_job = nil
				end, player)
			elseif state == cow_game.STATE_GAME_OVER and game_timer == -STATE_TIME_TIMEOUT then
				if game_message_job then
					game_message_job:cancel()
				end
				cow_gui.show_game_message(player, "game_over")
				core.sound_play({name="cow_game_game_over", gain=0.15}, {to_player=player:get_player_name()}, true)
				local game_message_job = core.after(STATE_TIME_GAME_OVER, function(player)
					cow_gui.hide_game_message(player)
					game_message_job = nil
				end, player)
			elseif entering_results == true then
				cow_player.set_texture_mode(player, "normal")
				local menu_level = cow_levels.get_level_by_name("menu_lab")
				cow_levels.init_level(menu_level)
				cow_levels.go_to_level(player, menu_level)
				local is_new_highscore = cow_highscore.send_new_score(cow_game.get_score(player))
				local current_highscore = cow_highscore.get_highscore()
				cow_results.show_results(player, { score = cow_game.get_score(player), highscore = current_highscore, new_highscore = is_new_highscore })
				cow_game.stop_timer(player)
				cow_gui.hide_score(player)
				cow_gui.hide_lives(player)
			end
			if game_timer_active then
				cow_gui.show_time(player, game_timer)
			end
		end
	end
end)

core.register_chatcommand("cancel", {
	description = S("Cancel the active game and return to the menu"),
	privs = {},
	params = "",
	func = function(name, param)
		local state = cow_game.get_state()
		if state == cow_game.STATE_MENU or state == cow_game.STATE_RESULTS then
			return false, S("There’s no game to cancel!")
		end
		local player = core.get_player_by_name(name)
		if not player then
			return false, S("No player.")
		end
		cow_game.stop_play(player)
		core.after(0, function()
			if player and player:is_player() then
				cow_menu.open_menu(player, true)
			end
		end)
		return true
	end,
})
