Kinkocalypse = Kinkocalypse or {}

Kinkocalypse.PunishmentList =
{
	"AlwaysNude",
	"Blinded",
	"Gaged",
	"HandsBack",
	"ChainedLegs",
	"NoMasturbate",
	"Vibtation",
	"NoRunning",
	"NoDriving",
	"NoOutside",
	"NoSleep",
	"NoSmoke",
	"NoEat",
	"NoRead",
	"EmptyHands",
	"ForceCrouch",
	"NippleClamps",
	"GenitalClamp",
	"Aphrodisiac"
}


Kinkocalypse.PunismentToString = function(punishment)
	if punishment == nil then return "???"
	else return getText("UI_Kincalypse_Rule_"..punishment) end
	return "???"
end

Kinkocalypse.OnControllCenterActivate = function(item)
	local player = getPlayer()
	local isFemale = player:isFemale()
	local wornItems = player:getWornItems():clear()
	local equippedRestrictions = {}

	-- Activate controll center
	player:setWornItem(item:getBodyLocation(), item)
	Kinkocalypse.WeldLock(item)
	Kinkocalypse.SetEquipedRestriction(player, item, true)

	-- Add items
	local colar = player:getInventory():AddItem("Kinkocalypse.SteelCollar")
	table.insert(equippedRestrictions, colar)

	local wirstCuffs = player:getInventory():AddItem("Kinkocalypse.DoubledSteelWristCuffs")
	table.insert(equippedRestrictions, wirstCuffs)

	local ankleCuffs = player:getInventory():AddItem("Kinkocalypse.SteelAnkleCuffs")
	table.insert(equippedRestrictions, ankleCuffs)

	local corset = player:getInventory():AddItem("Kinkocalypse.SteelCorset")
	table.insert(equippedRestrictions, corset)

	local chastityBra = nil
	if player:isFemale() == true then
		chastityBra = player:getInventory():AddItem("Kinkocalypse.SteelChastityBra")
		table.insert(equippedRestrictions, chastityBra) end

	local chastityBelt = player:getInventory():AddItem("Kinkocalypse.SteelChastityBeltChainedWithBothPlugs")
	table.insert(equippedRestrictions, chastityBelt)

	local catsuit = player:getInventory():AddItem("Kinkocalypse.Reinforced_Latex_Catsuit")
	table.insert(equippedRestrictions, catsuit)

	local gloves = player:getInventory():AddItem("Kinkocalypse.Reinforced_Latex_Gloves")
	table.insert(equippedRestrictions, gloves)

	local boots = player:getInventory():AddItem("Kinkocalypse.Reinforced_Knee_High_Boots")
	table.insert(equippedRestrictions, boots)

	local socks = player:getInventory():AddItem("Kinkocalypse.Reinforced_Latex_Socks")
	table.insert(equippedRestrictions, socks)

	if SandboxVars.Kinkocalypse.MaskType == 2 then
		local hood = player:getInventory():AddItem("Kinkocalypse.Reinforced_Latex_Hood")
		table.insert(equippedRestrictions, hood)
	end

	local gag = player:getInventory():AddItem("Kinkocalypse.SecuredGag")
	gag:getModData().GagClosed = false
	gag:getModData().PlugLocked = false
	table.insert(equippedRestrictions, gag)

	-- Equip all restrictions
	for i, itm in ipairs(equippedRestrictions) do
		player:setWornItem(itm:getBodyLocation(), itm)
		Kinkocalypse.WeldLock(itm)
		Kinkocalypse.SetEquipedRestriction(player, itm, true)
	end

	-- Set controller
	local controller =
	{
		IsActive = true,
		DaysLeft = 365,	
		LastDayCheck = getGameTime():getDay(),
		PunishDayCheck = getGameTime():getDay(),
		PunishmentScore = 0,
		Punishments = {},
	}

	player:Say(getText("UI_Kincalypse_Controller_Activated"))
	player:Say(getText("UI_Kincalypse_Controller_DaysInBondage")..controller.DaysLeft)
	player:getModData().RestrainController = controller

	local duration = ZombRand(120, 300)
	if player:getModData().Arousal == nil then
		player:getModData().Arousal = 0
	end

	player:getModData().Arousal = player:getModData().Arousal + ZombRand(15, 30)
	if ZombRand(100) <= 10 then
		Kinkocalypse.AddPunishment("HandsBack", duration)
	end
	if ZombRand(100) <= 15 then
		Kinkocalypse.AddPunishment("Blinded", duration)
	end	
	if ZombRand(100) <= 20 then
		Kinkocalypse.AddPunishment("ChainedLegs", duration)
	end
	if ZombRand(100) <= 25 then
		Kinkocalypse.AddPunishment("Gaged", duration)
	end
end


Kinkocalypse.OnControllCenterDeActivate = function()
	local player = getPlayer()
	local wornItems = player:getWornItems():clear()
	player:Say(getText("UI_Kincalypse_Controller_DeActivated"))
	player:getModData().RestrainController = nil
	player:getModData().RestrictedItems = nil
	Kinkocalypse.RegisteredItems = nil
end


Kinkocalypse.AddPunishmentScore = function(score, msg)
	local player = getPlayer()	
	local controller = player:getModData().RestrainController

	if controller ~= nil then
		controller.PunishmentScore = controller.PunishmentScore + score
		player:Say(getText("UI_Kincalypse_Controller_PunishedFor")..msg.."!")
		if ZombRand(100) > 50 and controller.PunishmentScore > 25 then
			local stats = player:getStats()
			local bodyDamage = player:getBodyDamage()
			local punishPower = score * ZombRand(0.1, 0.25)
			-- local punishPower = score * ZombRand(01, 025)
			local neckStiffness = bodyDamage:getBodyPart(BodyPartType.Neck):getStiffness()
	
			controller.PunishmentScore = controller.PunishmentScore - punishPower
			stats:setPain(stats:getPain() + punishPower + ZombRand(3.0, 5.0))
			if neckStiffness < 50 then 
				bodyDamage:getBodyPart(BodyPartType.Neck):setStiffness(neckStiffness + punishPower + ZombRand(1.0, 3.0))
				bodyDamage:getBodyPart(BodyPartType.Neck):setAdditionalPain(punishPower + ZombRand(1.0, 3.0))
			end
		end
	end
end


Kinkocalypse.GetPunishmentTime = function(punishment)
	local controller = getPlayer():getModData().RestrainController
	if controller ~= nil then
		if controller.Punishments[punishment] ~= nil then
			return controller.Punishments[punishment]
		end
	end
	return -1
end


Kinkocalypse.GetActivePunishmentsCount = function()
	local controller = getPlayer():getModData().RestrainController
	local count = 0
	if controller ~= nil then
		for i, v in ipairs(Kinkocalypse.PunishmentList) do
			if controller.Punishments[v] ~= nil then
				if controller.Punishments[v] > 0 then
					count = count + 1 
				end
			end
		end
	end
	return count
end


Kinkocalypse.AddPunishment = function(punishment, duration, silent)
	local player = getPlayer()
	local controller = getPlayer():getModData().RestrainController
	silent = silent or false --used to prevent player:Say spam when PunishBadSlave runs
	
	if controller ~= nil then
		if controller.Punishments == nil then
			controller.Punishments = {}
		end
		if controller.Punishments[punishment] == nil then
			controller.Punishments[punishment] = 0
			Kinkocalypse.StartPunishment(punishment)
		end
		
		controller.Punishments[punishment] = controller.Punishments[punishment] + duration
		local maxDuration = 3000
		if punishment == "Gaged" or punishment == "NoSleep" or punishment == "NoSmoke" or punishment == "NoEat" 
		then 
			maxDuration = 180
		elseif punishment == "HandsBack" then
			maxDuration = 600
		end
		
		if controller.Punishments[punishment] > maxDuration
			then controller.Punishments[punishment] = maxDuration
		end

		--write the text to console 
		print(getText("UI_Kincalypse_Controller_Punishment")..Kinkocalypse.PunismentToString(punishment)..getText("UI_Kincalypse_Controller_PunishmentActive")..Kinkocalypse.FormatMinutes(controller.Punishments[punishment]))		

		if not silent then
			player:Say(getText("UI_Kincalypse_Controller_Punishment")..Kinkocalypse.PunismentToString(punishment)..getText("UI_Kincalypse_Controller_PunishmentActive")..Kinkocalypse.FormatMinutes(controller.Punishments[punishment]))
		end
	end
end


Kinkocalypse.StartPunishment = function(punishment)
	local player = getPlayer()
	local wornItems = player:getWornItems()
	
	if punishment == "NoMasturbate" then
		if ZombRand(100) > 70 then Kinkocalypse.AddPunishment("Aphrodisiac", 180) end
	end

	if punishment == "AlwaysNude" and wornItems ~= nil then
		Kinkocalypse.UnEquipNonRestrictionClothing()
		Kinkocalypse.OnClothingUpdated()
	end
	if punishment == "Gaged" then
		local gag = wornItems:getItem("RestraintsMouth")
		if gag ~= nil then
			gag:getModData().GagClosed = true
			gag:getModData().PlugLocked = true
			Kinkocalypse.AddChainsToItemNoKey(gag)
			player:Say("[Click!]")
			player:Say(getText("UI_Kincalypse_GagPlugged"))
			Kinkocalypse.OnClothingUpdated()
		end
	end	
	if punishment == "HandsBack" then
		local handCuffs = wornItems:getItem("RestraintsWrist")
		if handCuffs ~= nil then
			handCuffs:getModData().ChainedHands = true
			handCuffs:getModData().ChainedHandsBack = true
			Kinkocalypse.AddChainsToItemNoKey(handCuffs)
			player:Say("[Click!]")
			Kinkocalypse.OnClothingUpdated()
		end
	end
	if punishment == "ChainedLegs" then
		local legCuffs = wornItems:getItem("RestraintsLegs")
		if legCuffs ~= nil then
			legCuffs:getModData().ChainedLegs = true
			Kinkocalypse.AddChainsToItemNoKey(legCuffs)
			player:Say("[Click!]")
			Kinkocalypse.OnClothingUpdated()
		end
	end
	if punishment == "GenitalClamp" then
		local gag = wornItems:getItem("RestraintsMouth")
		if gag ~= nil then
			player:Say("Mmff!!!")
		else player:Say("Auugh!!") end
	end
	if punishment == "Blinded" then
		-- TODO
		local headRestriction = wornItems:getItem("RestraintsHead")
		if not headRestriction then
			local blindfold = player:getInventory():AddItem("Kinkocalypse.SecuredBlindfold")
			player:setWornItem("RestraintsHead", blindfold)
			Kinkocalypse.SetEquipedRestriction(player, blindfold, true)
		end
		Kinkocalypse.OnClothingUpdated()
	end
end


Kinkocalypse.StopPunishment = function(punishment)
	local player = getPlayer()
	local wornItems = player:getWornItems()
	--local wornItems = player.getWornItems()
	
	if punishment == "Gaged" then
		local gag = wornItems:getItem("RestraintsMouth")
		if gag ~= nil then
			gag:getModData().GagClosed = false
			gag:getModData().PlugLocked = false
			Kinkocalypse.RemoveChainsFromItemNoKey(gag)
			player:Say(getText("UI_Kincalypse_GagUnPlugged"))
		end
	end	
	if punishment == "HandsBack" then
		local handCuffs = wornItems:getItem("RestraintsWrist")
		if handCuffs ~= nil then
			handCuffs:getModData().ChainedHands = false
			handCuffs:getModData().ChainedHandsBack = false
			Kinkocalypse.RemoveChainsFromItemNoKey(handCuffs)
			Kinkocalypse.OnClothingUpdated()
		end
	end	
	if punishment == "ChainedLegs" then
		local legCuffs = wornItems:getItem("RestraintsLegs")
		if legCuffs ~= nil then
			legCuffs:getModData().ChainedLegs = false
			Kinkocalypse.RemoveChainsFromItemNoKey(legCuffs)
			Kinkocalypse.OnClothingUpdated()
		end
	end
	if punishment == "Blinded" then
		local headRestriction = wornItems:getItem("RestraintsHead")
		if not headRestriction then return end
		if headRestriction:getFullType() == "Kinkocalypse.Reinforced_Latex_Hood" then return
		else
			Kinkocalypse.SetEquipedRestriction(player, headRestriction, false)
			player:removeWornItem(headRestriction)
			player:getInventory():Remove(headRestriction)
		end
		Kinkocalypse.OnClothingUpdated()
	end
end


Kinkocalypse.IsPunishmentActive = function(punishment)
	return Kinkocalypse.GetPunishmentTime(punishment) > 0
end


Kinkocalypse.GetActivePunishmentsText = function()
	local player = getPlayer()
	local modData = player:getModData()
	local text = "\n"
	
	local arousal = modData.Arousal
	if arousal == nil then arousal = 0 end
	text = text..getText("UI_Kincalypse_ArosalNow")..string.format("%.2f", arousal).."\n"
	
	if modData.RestrainController ~= nil then
		local daysLeft = modData.RestrainController.DaysLeft
		local punishmentScore = modData.RestrainController.PunishmentScore
		if daysLeft == nil then daysLeft = 0 end
		if punishmentScore == nil then punishmentScore = 0 end
		if SandboxVars.Kinkocalypse.AllowLockedForever == true and daysLeft >= SandboxVars.Kinkocalypse.LockedForeverThreshold then
			text = text.."Permanently Enslaved".."\n"
		else text = text..getText("UI_Kincalypse_DaysLeft")..math.floor(daysLeft + 0.5).."\n" end
		text = text..getText("UI_Kincalypse_PunishmentPoint")..string.format("%.2f", punishmentScore).."\n"
		
		local punishmentCount = Kinkocalypse.GetActivePunishmentsCount()
		text = text.."\n"..getText("UI_Kincalypse_ActivePunisments")..punishmentCount.."\n"
		local controller = modData.RestrainController
		if controller ~= nil then
			for i, v in ipairs(Kinkocalypse.PunishmentList) do
				if controller.Punishments[v] ~= nil then 
					text = text..Kinkocalypse.PunismentToString(v).." ("..Kinkocalypse.FormatMinutes(controller.Punishments[v])..") \n"
				end
			end
		end
	end
	return text
end


Kinkocalypse.ControllCenterIsOn = function()
	local controller = getPlayer():getModData().RestrainController
	if controller ~= nil then
		return controller.IsActive == true
	end
	return false
end


Kinkocalypse.ControllUnEquipRestriction = function()
	Kinkocalypse.AddPunishment("HandsBack", 120 + ZombRand(120))
	if ZombRand(100) <= 35 then Kinkocalypse.AddPunishment("Gaged", 120) end
	if ZombRand(100) <= 35 then Kinkocalypse.AddPunishment("Blinded", 120) end
	Kinkocalypse.AddPunishmentScore(100 + ZombRand(25), getText("UI_Kincalypse_ViolateUnEquipDevice"))
end


Kinkocalypse.PunishBadSlave = function()
	player:Say(getText("UI_Kincalypse_Controller_BadSlave"))
	local silent = true -- prevent notification spam from multiple punishments activated at once
	local duration = ZombRand(360, 600)
	Kinkocalypse.AddPunishment("Gaged", duration, silent)
	Kinkocalypse.AddPunishment("Blinded", duration, silent)
	Kinkocalypse.AddPunishment("HandsBack", duration, silent)
	Kinkocalypse.AddPunishment("ChainedLegs", duration, silent)
	Kinkocalypse.AddPunishment("ForceCrouch", duration, silent)
	if ZombRand(100) > 50 then Kinkocalypse.AddPunishment("NippleClamps", duration, silent) end
	if ZombRand(100) > 80 then Kinkocalypse.AddPunishment("GenitalClamp", duration, silent) end
end


Kinkocalypse.ApplyTimeDiff = function(player, modData, equipedTags, controller, prevTime)
	-- for Party System mod: catch up punishment time left & increase arousal based on how many ingame minutes since playing restrained character
	local timeCalendar = getGameTime():getCalender()
	local currentTime = timeCalendar:getTimeInMillis()

	local dt = math.abs(currentTime - prevTime)
	print("Calculated Time Difference: ",dt)
	local minMod = 1000*60
	local minutes = math.floor(dt/minMod)
	if minutes > 1 then
		for i = 1, minutes, 1 do
			Kinkocalypse.SetArousal(player, modData, equipedTags)
			for i, punishment in ipairs(Kinkocalypse.PunishmentList) do
				if controller.Punishments[punishment] ~= nil then
					controller.Punishments[punishment] = controller.Punishments[punishment] - 1
				end
			end
		end
	end
end


Kinkocalypse.MaintainControllCenter = function()
	local player = getPlayer()
	local modData = player:getModData()
	local equipedTags = Kinkocalypse.GetPlayerEquipedRestrictionsTags()

	if GameTime:isGamePaused() == true or Kinkocalypse.ControllCenterIsOn() ~= true then return end

	local controller = player:getModData().RestrainController

	for i, punishment in ipairs(Kinkocalypse.PunishmentList) do
		if controller.Punishments[punishment] ~= nil then
			controller.Punishments[punishment] = controller.Punishments[punishment] - 1
			if controller.Punishments[punishment] <= 0 then
				Kinkocalypse.StopPunishment(punishment)
				controller.Punishments[punishment] = nil
				if not player:isAsleep() then player:Say(getText("UI_Kincalypse_Controller_Punishment")..Kinkocalypse.PunismentToString(punishment)..getText("UI_Kincalypse_Controller_PunishmentFinish")) end
				print(getText("UI_Kincalypse_Controller_Punishment")..Kinkocalypse.PunismentToString(punishment)..getText("UI_Kincalypse_Controller_PunishmentFinish"))
			end
		end
	end
	
	local activePunishments = Kinkocalypse.GetActivePunishmentsCount()
	if ZombRand(1000) > 950 then
		if activePunishments <= 0 then
			local newPunishment = Kinkocalypse.PunishmentList[ZombRand(#Kinkocalypse.PunishmentList) + 1]
			if newPunishment ~= nil and not player:isAsleep() then
				local duration = ZombRand(120 + (controller.PunishmentScore * 10), 300 + (controller.PunishmentScore * 10))
				Kinkocalypse.AddPunishment(newPunishment, duration)
				activePunishments = activePunishments + 1
			end
		end
		if controller.PunishmentScore > 50 then
			local extraPunishments = 1 + (controller.PunishmentScore / 50)
			if activePunishments < extraPunishments and ZombRand(1000) > 500 then
				local newPunishment = Kinkocalypse.PunishmentList[ZombRand(#Kinkocalypse.PunishmentList) + 1]
				if newPunishment ~= nil  and not player:isAsleep() then
					local duration = ZombRand(120 + (controller.PunishmentScore * 2.5), 240 + (controller.PunishmentScore * 5))
					Kinkocalypse.AddPunishment(newPunishment, duration)
				end
			end
		end
	end
	if ZombRand(1000) > 950 and controller.PunishmentScore > 50 then controller.PunishmentScore = controller.PunishmentScore + ZombRand(0.05, 0.25) end
	
	-- Time check
	local gameTime = getGameTime()
	local hour = gameTime:getTimeOfDay()
	local day = gameTime:getDay()

	-- for Party System mod - allows punishment time & arousal to continue when playing as another character
	local timeCalendar = getGameTime():getCalender()
	local currentTime = timeCalendar:getTimeInMillis()
	local prevTime = player:getVariableFloat("TimeDiff", 0)
	if prevTime ~= 0 then Kinkocalypse.ApplyTimeDiff(player, modData, equipedTags, controller, prevTime) end
	player:setVariable("TimeDiff", currentTime)


	if controller.LastDayCheck == nil then controller.LastDayCheck = day end
	if controller.PunishDayCheck == nil then controller.PunishDayCheck = hour end
	if controller.PunishDayCheck ~= hour then controller.PunishDayCheck = hour end

	if controller.LastDayCheck ~= day and not player:isAsleep() then
		-- Punish Bad Slave if punishment score is too high
		if controller.PunishmentScore >= 100 and SandboxVars.Kinkocalypse.PunishBadSlave == true then Kinkocalypse.PunishBadSlave() end

		player:Say(getText("UI_Kincalypse_Controller_NewDayBegin")..controller.PunishmentScore)
		local daysRemove = -1 + (controller.PunishmentScore * 0.1)

		controller.LastDayCheck = day

		-- check if partial punishment score can be carried over to next day before removing punishment points
		if SandboxVars.Kinkocalypse.PunishmentCarryOver == true then
			if controller.PunishmentScore > SandboxVars.Kinkocalypse.PunishmentCarryThreshold then
				controller.PunishmentScore = controller.PunishmentScore - SandboxVars.Kinkocalypse.PunishmentCarryThreshold
			else controller.PunishmentScore = 0 end
		else controller.PunishmentScore = 0 end

		local maxDays = 0 -- max number of days before character is locked in bondage forever. ignore if 0.
		if SandboxVars.Kinkocalypse.AllowLockedForever == true then maxDays = SandboxVars.Kinkocalypse.LockedForeverThreshold end

		if controller.DaysLeft < maxDays and maxDays ~= 0 then controller.DaysLeft = controller.DaysLeft + daysRemove end

		if controller.DaysLeft <= 0 then OnControllCenterDeActivate() return;
		elseif controller.DaysLeft >= maxDays and maxDays ~= 0 then
			player:Say(getText("UI_Kincalypse_Controller_LockedForever"))
			if player:HasTrait("submissive") then
				player:getModData().Arousal = player:getModData().Arousal + ZombRand(15, 30)
				if equipedTags["SteelGag"] ~= nil then
					player:Say(Kinkocalypse.GetMoan()) end
			end
		else player:Say(getText("UI_Kincalypse_Controller_DaysInBondage")..math.floor(controller.DaysLeft + 0.5))
		end
	end
	player:getModData().RestrainController = controller
	
	-- prevent cheats and do overrides
	if SandboxVars.Kinkocalypse.AllowCheats == false then
		if CheatCoreCM ~= nil then CheatCoreCM.DisableAllCheat() end
		if ISUICheatMenu ~= nil then ISUICheatMenu.EventsRemove() end
	end

	local bodyDamage = player:getBodyDamage()
    local health = bodyDamage:getHealth()
    if health <= 15 then bodyDamage:AddGeneralHealth(50.0) end
end
Events.EveryOneMinute.Add(Kinkocalypse.MaintainControllCenter)
