-- @author DoomTero 2026
-- @copyright 2026 DoomTero - ZomboRut original work. ref:5e0a5726
-- @license Proprietary. Not licensed for reuse, redistribution or derivative works without written permission from DoomTero. Ask - permission is obtainable. See ZomboRut/LICENSE.txt

ZR_Moodles = ZR_Moodles or {}

local MF_ACTIVE = false
if getActivatedMods and getActivatedMods():contains("MoodleFramework") then
	MF_ACTIVE = true
	require "MF_ISMoodle"
elseif MF and MF.createMoodle and MF.getMoodle then
	MF_ACTIVE = true
end
ZR_Moodles.MF_ACTIVE = MF_ACTIVE

local function goodBadNeutral(value)
	if value >= 0.6 then return 1 end
	if value <= 0.4 then return 2 end
	return 0
end

local function levelFor(value)
	if value >= 0.9 or value <= 0.1 then return 4 end
	if value >= 0.8 or value <= 0.2 then return 3 end
	if value >= 0.7 or value <= 0.3 then return 2 end
	if value >= 0.6 or value <= 0.4 then return 1 end
	return 0
end

local LiteMoodle = ISUIElement:derive("ZRLiteMoodle")

local function moodleSize()
	local core = getCore()
	local opt = core:getOptionMoodleSize()
	local scale
	if opt < 5.5 then scale = 0.5 + 0.5 * opt
	elseif opt < 6.5 then scale = 4
	else scale = 0.5 + 0.5 * core:getOptionFontSizeReal() end
	return math.floor(32 * scale)
end

local BAD_RAMP = {
	{ 0.30, 0.55, 0.95 },
	{ 0.62, 0.40, 0.88 },
	{ 0.90, 0.42, 0.40 },
	{ 0.96, 0.15, 0.15 },
}
local GOOD_COLOR = { 0.30, 0.62, 0.95 }
local function moodleARGB(gbn, lvl)
	if lvl < 1 then lvl = 1 elseif lvl > 4 then lvl = 4 end
	if gbn == 2 then
		local c = BAD_RAMP[lvl]
		return 0.60 + 0.10 * lvl, c[1], c[2], c[3]
	end
	return 0.85, GOOD_COLOR[1], GOOD_COLOR[2], GOOD_COLOR[3]
end

function LiteMoodle:updateTextures(size)
	local s = tostring(math.floor(size))
	self.bgSolid   = getTexture("media/ui/ZRMoodles/" .. s .. "/ZR_MoodleBGsolid.png")
	self.bgOutline = getTexture("media/ui/ZRMoodles/" .. s .. "/ZR_MoodleBGoutline.png")
	self.pic = getTexture("media/ui/" .. s .. "/" .. self.name .. ".png")
	          or getTexture("media/ui/" .. self.name .. ".png")
end

local function sharedRegistry(character)
	if character == nil or character.getModData == nil then return nil end
	local md = character:getModData()
	if md == nil then return nil end
	md.Moodles = md.Moodles or {}
	return md.Moodles
end

local function publish(widget)
	local shared = sharedRegistry(widget.char)
	if shared == nil then return end
	local rec = shared[widget.name]
	if rec == nil then rec = {}; shared[widget.name] = rec end
	local gbn = goodBadNeutral(widget.value)
	rec.Value = widget.value
	rec.GoodBadNeutral = gbn
	rec.Level = (gbn ~= 0) and levelFor(widget.value) or 0
end

function LiteMoodle:getXYPosition()
	local size = moodleSize()
	if size ~= self:getWidth() then
		self:setWidth(size); self:setHeight(size)
		self:updateTextures(size)
	end
	local pn = (self.char.getPlayerNum and self.char:getPlayerNum()) or 0
	local x = getPlayerScreenLeft(pn) + getPlayerScreenWidth(pn) - 10 - size
	local y = getPlayerScreenTop(pn) + 120
	local distY = 10 + size

	local types = Registries.MOODLE_TYPE:values()
	local moodles = self.char:getMoodles()
	for i = 0, types:size() - 1 do
		local mt = types:get(i)
		local lvl = moodles:getMoodleLevel(mt)
		if lvl ~= 0 and mt ~= MoodleType.FOOD_EATEN or lvl >= 3 then
			y = y + distY
		end
	end

	local md = self.char:getModData()
	local aiteron = md and md.MoodleManager
	if aiteron and aiteron.moodles then
		for _, obj in pairs(aiteron.moodles) do
			if type(obj) == "table" and obj.getLevel and obj:getLevel() > 0 then
				y = y + distY
			end
		end
	end

	publish(self)
	local shared = sharedRegistry(self.char)
	if shared then
		for name, rec in pairs(shared) do
			if name == self.name then break end
			if type(rec) == "table" and rec.Level and rec.Level ~= 0 then
				y = y + distY
			end
		end
	end

	return x, y
end

function LiteMoodle:titleAndDesc(gbn, lvl)
	local side = (gbn == 2) and "Bad" or "Good"
	local title = getText("Moodles_" .. self.name .. "_" .. side .. "_lvl" .. lvl)
	local desc  = getText("Moodles_" .. self.name .. "_" .. side .. "_desc_lvl" .. lvl)
	return title or "", desc or ""
end

function LiteMoodle:renderTooltip(gbn, lvl, size)
	local mx, my = getMouseX(), getMouseY()
	local inside = mx >= self:getX() and mx <= self:getX() + size
	           and my >= self:getY() and my <= self:getY() + size
	if not (self:isMouseOver() or inside) then return end
	local title, desc = self:titleAndDesc(gbn, lvl)
	local tm = getTextManager()
	local wTitle = tm:MeasureStringX(UIFont.Small, title) + 7
	local wDesc  = tm:MeasureStringX(UIFont.Small, desc) + 7
	local textW  = math.max(wTitle, wDesc)
	local hTitle = tm:MeasureStringY(UIFont.Small, title)
	local hDesc  = tm:MeasureStringY(UIFont.Small, desc)
	local pad = 2
	local rectH = hTitle + hDesc + pad * 3
	local baseH = rectH < size and math.floor((size - rectH) / 2) or 0
	self:drawRect(-4 - (5 + textW), baseH, 5 + textW, rectH, 0.6, 0, 0, 0)
	self:drawTextRight(title, -10, baseH + pad, 1, 1, 1, 1)
	self:drawTextRight(desc, -10, baseH + hTitle + pad * 2, 1, 1, 1, 0.7)
end

function LiteMoodle:render()
	if self.char == nil or self.char:isDead() then return end
	local gbn = goodBadNeutral(self.value)
	if gbn == 0 then return end
	local lvl = levelFor(self.value)

	local x, y = self:getXYPosition()
	if x ~= self:getX() then self:setX(x) end
	if y ~= self:getY() then self:setY(y) end
	local size = self:getWidth()

	if self.bgSolid then
		local a, r, g, b = moodleARGB(gbn, lvl)
		self:drawTextureScaled(self.bgSolid, 0, 0, size, size, a, r, g, b)
	end
	if self.bgOutline then
		self:drawTextureScaled(self.bgOutline, 0, 0, size, size, 1)
	end
	self:renderTooltip(gbn, lvl, size)
	local pic = (self.pics and self.pics[gbn] and self.pics[gbn][lvl]) or self.pic
	if pic then
		self:drawTextureScaled(pic, 0, 0, size, size, 1)
	end
end

function LiteMoodle:setValueInternal(value)
	local before = goodBadNeutral(self.value)
	self.value = value
	local after = goodBadNeutral(value)
	publish(self)
	if before == 0 and after ~= 0 and not self.shown then
		self:addToUIManager(); self.shown = true
	elseif after == 0 and self.shown then
		self:removeFromUIManager(); self.shown = false
	end
end

local function newLiteMoodle(name, character)
	local size = moodleSize()
	local o = ISUIElement:new(0, 0, size, size)
	setmetatable(o, LiteMoodle)
	LiteMoodle.__index = LiteMoodle
	o.name = name
	o.char = character
	o.value = 0.5
	o.shown = false
	o.backgroundColor = { r = 0, g = 0, b = 0, a = 0 }
	o.borderColor = { r = 0, g = 0, b = 0, a = 0 }
	o:initialise()
	o.keepOnScreen = false
	o:updateTextures(size)
	o.pics = { {}, {} }
	for lvl = 1, 4 do
		o.pics[1][lvl] = getTexture("media/ui/" .. name .. "_Good_" .. lvl .. ".png")
		o.pics[2][lvl] = getTexture("media/ui/" .. name .. "_Bad_" .. lvl .. ".png")
	end
	publish(o)
	return o
end

ZR_Moodles._widgets = ZR_Moodles._widgets or {}
ZR_Moodles._order   = ZR_Moodles._order or {}

local function _ensureWidget(name, player)
	local w = ZR_Moodles._widgets[name]
	if w == nil or w.char ~= player then
		if w and w.shown then pcall(function() w:removeFromUIManager() end) end
		w = newLiteMoodle(name, player)
		ZR_Moodles._widgets[name] = w
	end
	return w
end

function ZR_Moodles.create(name)
	if MF_ACTIVE then
		MF.createMoodle(name)
		return
	end
	for i = 1, #ZR_Moodles._order do
		if ZR_Moodles._order[i] == name then return end
	end
	ZR_Moodles._order[#ZR_Moodles._order + 1] = name
end

function ZR_Moodles.set(name, player, value)
	if player == nil or type(value) ~= "number" then return end
	if MF_ACTIVE then
		if not (MF and MF.getMoodle) then return end
		local pn = (player.getPlayerNum and player:getPlayerNum()) or 0
		local m = MF.getMoodle(name, pn)
		if not m then return end
		if m.getValue and m:getValue() == value then return end
		m:setValue(value)
		return
	end
	local w = _ensureWidget(name, player)
	if w.value == value then return end
	w:setValueInternal(value)
end

function ZR_Moodles.setPicture(name, player, gbn, lvl, texture)
	if player == nil or type(gbn) ~= "number" or type(lvl) ~= "number" then return end
	if MF_ACTIVE then
		if not (MF and MF.getMoodle) then return end
		local pn = (player.getPlayerNum and player:getPlayerNum()) or 0
		local m = MF.getMoodle(name, pn)
		if m and m.setPicture then m:setPicture(gbn, lvl, texture) end
		return
	end
	local w = _ensureWidget(name, player)
	if w.pics and w.pics[gbn] then w.pics[gbn][lvl] = texture end
end

if not MF_ACTIVE then
	Events.OnPlayerDeath.Add(function(character)
		local shared = sharedRegistry(character)
		for name, w in pairs(ZR_Moodles._widgets) do
			if w.char == character then
				if w.shown then pcall(function() w:removeFromUIManager() end) end
				if shared then shared[name] = nil end
				ZR_Moodles._widgets[name] = nil
			end
		end
	end)
end

return ZR_Moodles
