-- MOD CONFIG STARTS HERE

-- If set to true, the in-game date and time will be set to your system time.
-- DuskNightDawnOnly and SimulationSpeed options will then be ignored.
local UseSystemTime = false
-- Time zone (offset from UTC) to be used when UseSystemTime = true. Temporarily user configurable,
-- in the future it will be automatically inferred from system time.
local TimeZoneOffset = 1
-- If set to true, the game will loop between dusk -> night -> dawn -> night -> dusk.
-- If false, a regular 24h cycle will be used. Only works when UseSystemTime is not set.
local DuskNightDawnOnly = false
-- If set to true, the cloud coverage will be randomized every night.
local RandomizeClouds = true
-- If set to true, fog will be randomized every night. Has a 2/3 chance of being completely fogless, or having *some* fog.
local RandomizeFog = true
-- Time day/night cycle speed. Defaults to 1 IRL second = 30 in-game seconds.
-- Only works when DuskNightDawnOnly and UseSystemTime are not set.
local SimulationSpeed = 30.0

-- MOD CONFIG ENDS HERE

-- local UEHelpers = require("UEHelpers")

-- local SavedUDSState = nil -- Doesn't work yet
local SavedTimeOfDay = nil
local SavedCloudCoverage = nil
local SavedFog = nil

local SkyClass = CreateInvalidObject()
---@return UClass
function TryGetSkyClass()
	if not SkyClass:IsValid() then
		SkyClass = StaticFindObject('/Game/ITSB/ArtAssets/Models/Course/ACOMMON/Sky/BP_Sky.BP_Sky_C') ---@cast SkyClass UClass
	end
	return SkyClass
end

local CourseSkyClass = CreateInvalidObject()
---@return UClass
function TryGetCourseSkyClass()
	if not CourseSkyClass:IsValid() then
		CourseSkyClass = StaticFindObject('/Game/ITSB/ArtAssets/Models/Course/ACOMMON/Sky/BP_CourseSky.BP_CourseSky_C') ---@cast CourseSkyClass UClass
	end
	return CourseSkyClass
end

RegisterLoadMapPreHook(function()
	SkyClass = CreateInvalidObject()
	CourseSkyClass = CreateInvalidObject()
end)

local CachedCourseSkyActor = CreateInvalidObject()

math.randomseed(os.time())
RegisterBeginPlayPreHook(function(ActorParam)
	if CachedCourseSkyActor:IsValid() then
		return
	end

    local Actor = ActorParam:get()
    if not Actor:IsA(TryGetSkyClass()) then
		return
	end

	if not Actor:IsA(TryGetCourseSkyClass()) then
		-- If we get a non-course sky, then it means we're back in the garage.
		-- Reset the saved options.
		SavedTimeOfDay = nil
		SavedCloudCoverage = nil
		SavedFog = nil
		return
	end

	CachedCourseSkyActor = Actor

	Actor['Animate Time of Day'] = true
	Actor['Instant Exposure Adjustment On Begin Play'] = true
	if UseSystemTime then
		Actor['Use System Time'] = true
		Actor['Apply Daylight Savings Time'] = false -- Time Zone will contain a difference between local time and UTC already

		-- This is broken until UE4SS fixes passing structs with unreflected fields as arguments.
		-- For now, leave this as an user option.
		-- See https://github.com/UE4SS-RE/RE-UE4SS/issues/777
		-- local KismetMath = UEHelpers.GetKismetMathLibrary()
		-- if KismetMath:IsValid() then
			-- local TimeZoneOffset = KismetMath:GetHour(KismetMath:Now()) - KismetMath:GetHour(KismetMath:UtcNow())
			Actor['Time Zone'] = TimeZoneOffset
		-- end
	else
		if SavedTimeOfDay ~= nil then
			Actor['Time Of Day'] = SavedTimeOfDay
			--  Actor['Apply Saved UDS and UDW State'](SavedUDSState)
			SavedTimeOfDay = nil
		else
			local RandomTime = 1800.0 + (math.random() * 1000.0) -- 6pm to 4am
			if RandomTime >= 2400.0 then
				RandomTime = RandomTime - 2400.0
			end
			Actor['Time Of Day'] = RandomTime
		end
		Actor['Simulation Speed'] = SimulationSpeed

		if DuskNightDawnOnly then
			Actor['Simulate Real Sun'] = false
			Actor['Day Length'] = 200.0
			Actor['Night Length'] = 10.0
			Actor['Extend Dawn And Dusk'] = 10.0

			local ExitLoop = false
			local DawnTime = Actor['Dawn Time']
			local DuskTime = Actor['Dusk Time']
			local TimeInverted = false
			LoopAsync(1000, function()
				if ExitLoop then
					return true
				end
				ExecuteInGameThread(function()
					if not CachedCourseSkyActor:IsValid() then
						ExitLoop = true
						return
					end
					local CurrentTime = CachedCourseSkyActor['Time Of Day']
					if CurrentTime > DawnTime and CurrentTime < DuskTime then
						if not TimeInverted then
							CachedCourseSkyActor['Time Speed'] = CachedCourseSkyActor['Time Speed'] * -1.0
							TimeInverted = true
						end
					else
						TimeInverted = false
					end
				end)
				return false
			end)
		end
	end
end)

RegisterBeginPlayPostHook(function(ActorParam)
    local Actor = ActorParam:get()
    if Actor:GetAddress() ~= CachedCourseSkyActor:GetAddress() then
		return
	end

	local Weather = Actor['Ultra Dynamic Weather']
	if Weather:IsValid() then
		if RandomizeClouds then
			Weather['Cloud Coverage'] = math.random()^2 * 10.0
			Weather['Cloud Coverage - Manual Override'] = true
		end
		if RandomizeFog then
			Weather['Fog'] = math.max(0.0, (math.random() * 30.0) - 20.0)
			Weather['Fog - Manual Override'] = true
		end

		if SavedCloudCoverage ~= nil then
			Weather['Cloud Coverage'] = SavedCloudCoverage
			SavedCloudCoverage = nil
		end
		if SavedFog then
			Weather['Fog'] = SavedFog
			SavedFog = nil
		end
	end
end)

RegisterEndPlayPreHook(function(ActorParam)
    local Actor = ActorParam:get()
    if Actor:GetAddress() ~= CachedCourseSkyActor:GetAddress() then
		return
	end

	CachedCourseSkyActor = CreateInvalidObject()
	SavedTimeOfDay = Actor['Time Of Day']
	-- SavedUDSState = {}
	-- Actor['Create UDS and UDW State for Saving'](SavedUDSState)
	local Weather = Actor['Ultra Dynamic Weather']
	if Weather:IsValid() then
		SavedCloudCoverage = Weather['Cloud Coverage']
		SavedFog = Weather['Fog']
	end
end)

RegisterKeyBind(Key.F, {ModifierKey.ALT}, function()
	ExecuteInGameThread(function()
		if CachedCourseSkyActor:IsValid() then
			local AnimateTimeNewState = not CachedCourseSkyActor['Animate Time of Day']
			if AnimateTimeNewState then
				print('Time unfrozen\n')
			else
				print('Time frozen\n')
			end
			CachedCourseSkyActor['Animate Time of Day'] = AnimateTimeNewState
		end
	end)
end)
