local S = minetest.get_translator("lzr_sky")

lzr_sky = {}

local DEFAULT_LIGHTING = {
	volumetric_light = {
		strength = 0,
	},
}
local DEFAULT_STARS = {
	visible = true,
	star_color = "#FFFFFF80",
	count = 2000,
}
local DEFAULT_SUN = {
	visible = true,
	sunrise_visible = true,
}
local DEFAULT_MOON = {
	visible = true,
}
local DEFAULT_FOG = {
	fog_distance = lzr_globals.MAX_VIEW_DISTANCE,
	fog_start = -1,
}

-- Start and end of the day in timeofday values.
local TIMEOFDAY_DAY_START = 0.251
local TIMEOFDAY_DAY_END = 0.749

-- Start and end of the night in timeofday values.
-- (wraps around)
local TIMEOFDAY_NIGHT_START = 0.85
local TIMEOFDAY_NIGHT_END = 0.15

-- Points of the time of day
local TIMEOFDAY_MIDNIGHT = 0.0
local TIMEOFDAY_DAWN = 0.26
local TIMEOFDAY_MIDDAY = 0.5
local TIMEOFDAY_DUSK = 0.74

-- Check the timeofday to reset every this many seconds
local TIMEOFDAY_RESET_TIME = 5.0

-- True if the special lightning sky is currently acitve
local lightning_active = false

-- Currently active sky
local current_sky

local registered_skies = {}

local function register_sky(name, def)
	registered_skies[name] = def
end

function lzr_sky.sky_exists(skyname)
	return registered_skies[skyname] ~= nil
end

function lzr_sky.get_skies()
	local list = {}
	for id, _ in pairs(registered_skies) do
		table.insert(list, id)
	end
	table.sort(list)
	return list
end

function lzr_sky.get_sky_description(id)
	return registered_skies[id].description
end

-- Check if the timeofday for given sky is within the
-- sky's permissible timeofday values. If not, reset it
-- to the start.
local function reset_timeofday(sky)
	local skydef = registered_skies[sky]
	if not skydef then
		return
	end
	local tod_start = skydef.timeofday_start
	local tod_end = skydef.timeofday_end
	local tod = minetest.get_timeofday()
	if tod_start < tod_end then
		if tod < tod_start or tod > tod_end then
			minetest.log("info", "[lzr_sky] Resetting timeofday for day sky "..sky)
			minetest.set_timeofday(tod_start)
		end
	else
		if tod < tod_start and tod > tod_end then
			minetest.log("info", "[lzr_sky] Resetting timeofday for night sky "..sky)
			minetest.set_timeofday(tod_start)
		end
	end
end

function lzr_sky.set_sky(skyname)
	local player = minetest.get_player_by_name("singleplayer")
	local skydef = registered_skies[skyname]
	-- Don't set sky if it's a sky already in use.
	-- Done to reduce unneccessary network packages.
	if current_sky == skyname then
		return
	end

	if skydef.timeofday_init then
		minetest.set_timeofday(skydef.timeofday_init)
	else
		minetest.set_timeofday(skydef.timeofday_start)
	end

	local skyskydef = table.copy(skydef.sky)
	if lightning_active and skyskydef.sky_color then
		-- Lightning takes precedence on the sky color.
		-- When the lightning is over, the real sky
		-- color is applied.
		skyskydef.sky_color.type = nil
		skyskydef.sky_color.day_sky = nil
		skyskydef.sky_color.day_horizon = nil
		skyskydef.sky_color.dawn_sky = nil
		skyskydef.sky_color.dawn_horizon = nil
		skyskydef.sky_color.night_sky = nil
		skyskydef.sky_color.night_horizon = nil
	end
	player:set_sky(skyskydef)
	player:set_clouds(skydef.clouds)
	player:set_sun(skydef.sun)
	player:set_moon(skydef.moon)
	player:set_stars(skydef.stars)
	player:set_lighting(skydef.lighting)
	-- Lightning takes precendence for day night ratio
	if not lightning_active then
		player:override_day_night_ratio(skydef.day_night_ratio)
	end

	-- Remember skyname for later
	current_sky = skyname

	minetest.log("action", "[lzr_sky] Sky set to "..skyname)
end

function lzr_sky.get_sky()
	return current_sky
end

function lzr_sky.get_lightning()
	return lightning_active
end

function lzr_sky.set_lightning(active)
	-- force-deactivate lightning if lightning was disabled by setting
	if minetest.settings:get_bool("lzr_lightning", true) ~= true then
		active = false
	end
	local player = minetest.get_player_by_name("singleplayer")
	if active then
		-- Activate flash
		lightning_active = true
		player:override_day_night_ratio(1)
		player:set_sky({
			type = "plain",
			base_color = "#ffffff",
		})
		minetest.log("info", "[lzr_sky] Lightning activated!")
	else
		-- Deactivate flash
		lightning_active = false
		local skydef = registered_skies[current_sky]
		-- (Re-)Apply the sky and day/night ratio of the current sky
		if skydef then
			player:set_sky(skydef.sky)
		end
		player:override_day_night_ratio(skydef.day_night_ratio)
		minetest.log("verbose", "[lzr_sky] Lightning deactivated!")
	end
end

-- Regularily check if the timeofday falls into the permissble range
-- of timeofday values for the current sky and if not, reset
-- it. This code is a workaround for the fact we cannot yet
-- cleanly halt the day-night cycle without screwing up
-- player settings.
-- What this does it providing an acceptable solution for two cases:
-- 1) time_speed is positive: The sun/moon will travel as usual,
--    and as soon it reaches timeofday_end, it wraps back to timeofday_start,
--    essentially "skipping" a few hours.
-- 2) time_speed is 0: There is no effect.
--
-- This solution works fine for day and night but is a bit weird
-- for dawn and dusk.
--
-- What we ACTUALLY want is that the sun is always static.
-- but this solution was determined to be the "next best" one
-- without too much disruption.
-- TODO: Halt the day-night cycle entirely without hacks as soon
-- Luanti allows us to do so.
local timer = TIMEOFDAY_RESET_TIME
minetest.register_globalstep(function(dtime)
	timer = timer + dtime
	if timer >= TIMEOFDAY_RESET_TIME then
		timer = 0
		reset_timeofday(current_sky)
	end
end)


register_sky("bright_blue", {
	--~ Sky name
	description = S("Bright Blue"),
	sky = {
		type = "regular",
		sky_color = {
			day_sky="#3d4caa",
			day_horizon="#55aaff",
		},
		fog = DEFAULT_FOG,
		clouds = true,
	},
	clouds = {
		height = 100,
		thickness = 20,
		density = 0.3,
		color = "#FFFFFF",
	},
	stars = DEFAULT_STARS,
	sun = DEFAULT_SUN,
	moon = DEFAULT_MOON,
	lighting = { volumetric_light = { strength = 0.2 }},
	timeofday_start = TIMEOFDAY_DAY_START,
	timeofday_init = TIMEOFDAY_MIDDAY,
	timeofday_end= TIMEOFDAY_DAY_END,
})
register_sky("island_blue", {
	--~ Sky name
	description = S("Island Blue"),
	sky = {
		type = "regular",
		sky_color = {
			day_sky="#01C8EB",
			day_horizon="#54A9FE",
			night_sky="#01C8EB",
			night_horizon="#54A9FE",
			dawn_sky="#01C8EB",
			dawn_horizon="#54A9FE",
		},
		fog = DEFAULT_FOG,
		clouds = true,
	},
	clouds = {
		height = 100,
		thickness = 20,
		density = 0.3,
		color = "#FFFFFF",
	},
	stars = DEFAULT_STARS,
	sun = DEFAULT_SUN,
	moon = DEFAULT_MOON,
	lighting = { volumetric_light = { strength = 0.2 }},
	timeofday_start = TIMEOFDAY_DAY_START,
	timeofday_init = TIMEOFDAY_MIDDAY,
	timeofday_end= TIMEOFDAY_DAY_END,
})

register_sky("jungle_green", {
	--~ Sky name
	description = S("Jungle Green"),
	sky = {
		type = "regular",
		sky_color = {
			day_sky="#009F6F",
			day_horizon="#249C48",
		},
		fog = {
			fog_distance = 200,
			fog_start = 0.45,
		},
		clouds = true,
	},
	clouds = {
		height = 100,
		thickness = 20,
		density = 0.4,
		color = "#B4FF938D",
	},
	day_night_ratio = 1.0,
	stars = DEFAULT_STARS,
	sun = DEFAULT_SUN,
	moon = DEFAULT_MOON,
	lighting = { volumetric_light = { strength = 0.1 }},
	timeofday_start = TIMEOFDAY_DAY_START,
	timeofday_init = TIMEOFDAY_MIDDAY,
	timeofday_end = TIMEOFDAY_DAY_END,
})
register_sky("tropical_dawn", {
	--~ Sky name
	description = S("Tropical Dawn"),
	sky = {
		type = "regular",
		sky_color = {
			day_sky="#d20000",
			day_horizon="#ff6c09",
		},
		fog = DEFAULT_FOG,
		clouds = true,
	},
	clouds = {
		height = 100,
		thickness = 20,
		density = 0.3,
		color = "#ff6600a5",
	},
	day_night_ratio = 0.95,
	stars = DEFAULT_STARS,
	sun = DEFAULT_SUN,
	moon = DEFAULT_MOON,
	lighting = { volumetric_light = { strength = 0.05 }},
	timeofday_start = TIMEOFDAY_DAY_START,
	timeofday_init = TIMEOFDAY_DAWN,
	timeofday_end = TIMEOFDAY_DAY_END,
})

register_sky("ocean_evening", {
	--~ Sky name
	description = S("Ocean Evening"),
	sky = {
		type = "regular",
		sky_color = {
			day_sky="#3d374d",
			day_horizon="#5a2a34",
			dawn_sky="#3d374d",
			dawn_horizon="#5a2a34",
			night_sky="#3d374d",
			night_horizon="#5a2a34",
		},
		fog = DEFAULT_FOG,
		clouds = true,
	},
	clouds = {
		height = 100,
		thickness = 20,
		density = 0.3,
		color = "#8e6164ff",
	},
	day_night_ratio = 0.8,
	stars = DEFAULT_STARS,
	lighting = DEFAULT_LIGHTING,
	sun = DEFAULT_SUN,
	moon = DEFAULT_MOON,
	timeofday_start = TIMEOFDAY_DAY_START,
	timeofday_init = TIMEOFDAY_DUSK,
	timeofday_end = TIMEOFDAY_DAY_END,
})
register_sky("ocean_morning", {
	--~ Sky name
	description = S("Ocean Morning"),
	sky = {
		type = "regular",
		sky_color = {
			day_sky="#5184a2",
			day_horizon="#ffb754",
		},
		fog = DEFAULT_FOG,
		clouds = true,
	},
	clouds = {
		height = 100,
		thickness = 20,
		density = 0.3,
		color = "#ffffff8a",
	},
	day_night_ratio = 0.9,
	stars = DEFAULT_STARS,
	sun = DEFAULT_SUN,
	moon = DEFAULT_MOON,
	lighting = { volumetric_light = { strength = 0.25 }},
	timeofday_start = TIMEOFDAY_DAY_START,
	timeofday_init = TIMEOFDAY_DAWN,
	timeofday_end = TIMEOFDAY_DAY_END,
})
register_sky("ominous_fog", {
	--~ Sky name
	description = S("Ominous Fog"),
	sky = {
		type = "regular",
		sky_color = {
			day_sky="#e7deff",
			day_horizon="#e1deff",
		},
		fog = {
			fog_distance = 40,
			fog_start = 0.2,
		},
		clouds = false,
	},
	stars = { visible = false },
	sun = { visible = false, sunrise_visible = false },
	moon = { visible = false },
	lighting = DEFAULT_LIGHTING,
	timeofday_start = TIMEOFDAY_DAY_START,
	timeofday_init = TIMEOFDAY_MIDDAY,
	timeofday_end = TIMEOFDAY_DAY_END,
})
register_sky("night_fog", {
	--~ Sky name
	description = S("Night Fog"),
	sky = {
		type = "regular",
		sky_color = {
			day_sky="#808080",
			day_horizon="#808080",
			dawn_sky="#808080",
			dawn_horizon="#808080",
			night_sky="#808080",
			night_horizon="#808080",
		},
		fog = {
			fog_distance = 40,
			fog_start = 0.0,
		},
		clouds = false,
	},
	stars = { visible = false },
	sun = { visible = false, sunrise_visible = false },
	moon = { visible = false },
	lighting = DEFAULT_LIGHTING,
	timeofday_start = TIMEOFDAY_NIGHT_START,
	timeofday_init = TIMEOFDAY_MIDNIGHT,
	timeofday_end = TIMEOFDAY_NIGHT_END,
	day_night_ratio = 0.2,
})
register_sky("storm_clouds", {
	--~ Sky name
	description = S("Stormy Clouds"),
	sky = {
		type = "regular",
		sky_color = {
			day_sky="#808080",
			day_horizon="#808080",
		},
		fog = {
			fog_distance = 60,
			fog_start = 0.2,
		},
		clouds = true,
	},
	clouds = {
		height = 100,
		thickness = 40,
		density = 0.7,
		color = "#808080aa",
	},
	stars = { visible = false },
	sun = { visible = false, sunrise_visible = false },
	moon = { visible = false },
	lighting = DEFAULT_LIGHTING,
	timeofday_start = TIMEOFDAY_DAY_START,
	timeofday_init = TIMEOFDAY_MIDDAY,
	timeofday_end = TIMEOFDAY_DAY_END,
	day_night_ratio = 0.7,
})
register_sky("starry_night", {
	--~ Sky name
	description = S("Starry Night"),
	sky = {
		type = "regular",
		sky_color = {
			day_sky="#000020",
			day_horizon="#000020",
			dawn_sky="#000020",
			dawn_horizon="#000020",
			night_sky="#000020",
			night_horizon="#000020",
		},
		fog = DEFAULT_FOG,
		clouds = true,
	},
	clouds = {
		height = 100,
		thickness = 10,
		density = 0.1,
		color = "#ffffff45",
	},
	day_night_ratio = 0.6,
	stars = DEFAULT_STARS,
	sun = { visible = false, sunrise_visible = false },
	moon = DEFAULT_MOON,
	lighting = { volumetric_light = { strength = 0.07 } },
	timeofday_start = TIMEOFDAY_NIGHT_START,
	timeofday_init = TIMEOFDAY_MIDNIGHT,
	timeofday_end = TIMEOFDAY_NIGHT_END,
})

-- Special sky for levels that are completely underground with
-- no sky exposure. Renders as little as possible for performance.
-- Most importantly, volumetric light is disabled, which is most
-- computationally heavy.
register_sky("underground", {
	--~ Sky name of a special sky for underground levels
	description = S("Underground"),
	sky = {
		type = "plain",
		base_color ="#000000",
		fog = DEFAULT_FOG,
		clouds = false,
	},
	day_night_ratio = nil,
	stars = { visible = false },
	sun = { visible = false, sunrise_visible = false },
	moon = { visible = false },
	lighting = { volumetric_light = { strength = 0 } },
	timeofday_start = TIMEOFDAY_DAY_START,
	timeofday_init = TIMEOFDAY_MIDDAY,
	timeofday_end = TIMEOFDAY_DAY_END,
})

-- Send warning message when trying to use /time command
-- to change time because lzr_sky overrides it.
minetest.register_on_chatcommand(function(name, command, params)
	if command == "time" and params ~= "" then
		local state = lzr_gamestate.get_state()
		if lzr_gamestate.get_state() == lzr_gamestate.EDITOR then
			--~ Message shown when trying to use "/time" command in editor
			minetest.chat_send_player(name, minetest.colorize("#ff8000", S("You can’t use this command to set the time in this game. Try a different sky in the level settings.")))
		else
			--~ Message shown when trying to use "/time" command
			minetest.chat_send_player(name, minetest.colorize("#ff8000", S("You can’t use this command to set the time in this game.")))
		end
		return true
	end
end)
