NUiShipIntegrity = {}

function NUiShipIntegrity:SetupUI()
	gr.CurrentOpacityType = ALPHABLEND_NONE
	self.Active = {} --Keeps track of active elements
	require("nui-hud")

	self:ParseConfig("nui-hud.tbl")

	if self.Active.IntegrityFrame then
		self:SetupIntegrityFrame()
	end

	if self.Active.HullBar then
		self:SetupHullBar()
	end

	if self.Active.ShieldBar then
		self:SetupShieldBar()
	end

	if self.Active.BoostBar then
		self:SetupBoostBar()
	end

end

function NUiShipIntegrity:ParseConfig(file)
	NUi:ParseGeneralSettings(file, self)

	NUi:ParseConfigSection(file, "#Integrity Frame Settings", function() self:ConfigIntegrityFrame() end)

	NUi:ParseConfigSection(file, "#Hull Bar Settings", function() self:ConfigHullBar() end)

	NUi:ParseConfigSection(file, "#Shield Bar Settings", function() self:ConfigShieldBar() end)

	NUi:ParseConfigSection(file, "#Boost Bar Settings", function() self:ConfigBoostBar() end)

end

local function ParseEnergyBarConfig(self)
	local t = { Graphic = {}, Colors = {} }

	if parse.requiredString("$Frame Positioning:") then
		t.Graphic.Frame = NUi:ParseGraphicBlock(self.ScaleFactor)
		if parse.optionalString("+Label:") then
			t.Graphic.Frame.LabelImage = parse.getString()
		end
	end

	if parse.optionalString("$Bar Positioning:") then
		t.Graphic.Bar = NUi:ParseGraphicBlock(self.ScaleFactor)
		if parse.optionalString("+Label:") then
			t.Graphic.Bar.LabelImage = parse.getString()
		end
		if parse.optionalString("+Inverse:") then
			t.Graphic.Bar.Inverse = parse.getString() == "true"
		else
			t.Graphic.Bar.Inverse = false
		end
		if parse.optionalString("+Direction:") then
			t.Direction = NUi:GetDirection(parse.getString())
		else
			t.Direction = NUi.Direction.RIGHT
		end
	end

	if parse.optionalString("$Frame Colors:") then
		t.Colors.Frame = NUi:ParseColorBlock()
	else
		t.Colors.Frame = { Mode = "lerp", Colors = {{127, 127, 127, 255}}, Label = {0, 0, 0, 50} }
	end

	if parse.optionalString("$Bar Colors:") then
		t.Colors.Bar = NUi:ParseColorBlock()
	else
		t.Colors.Bar = { Mode = "lerp", Colors = {{127, 127, 127, 255}}, Label = {0, 0, 0, 50} }
	end

	if parse.optionalString("+Shimmer Speed:") then
		t.ShimmerSpeed = parse.getFloat()
	else
		t.ShimmerSpeed = 1.0
	end

	if parse.optionalString("+Shimmer White:") then
		local rgba = NUi:GetCSV(parse.getString())
		t.ShimmerWhite = {rgba[1], rgba[2], rgba[3], rgba[4] or 255}
	else
		t.ShimmerWhite = {255, 255, 255, 127}
	end

	parse.requiredString("#End")
	return t
end

function NUiShipIntegrity:ConfigIntegrityFrame()
	self.Active.IntegrityFrame = true

	local t = { Graphic = {}, Colors = {} }

	if parse.requiredString("$Frame Positioning:") then
		t.Graphic.Frame = NUi:ParseGraphicBlock(self.ScaleFactor)
	end

	if parse.optionalString("$Frame Colors:") then
		t.Colors.Frame = NUi:ParseColorBlock()
	end

	parse.requiredString("#End")

	self.IntegrityFrame = t
end

function NUiShipIntegrity:ConfigHullBar()
	self.Active.HullBar = true

	local t = { Graphic = {}, Colors = {} }

	if parse.optionalString("$Hull Bar Frame Positioning:") then
		t.Graphic.BarFrame = NUi:ParseGraphicBlock(self.ScaleFactor)
		if parse.optionalString("+Label:") then
			t.Graphic.BarFrame.LabelImage = parse.getString()
		end
	end

	if parse.optionalString("$Hull Bar Glow Positioning:") then
		t.Graphic.Glow = NUi:ParseGraphicBlock(self.ScaleFactor)
		if parse.optionalString("+Threshold:") then
			t.Graphic.Glow.Threshold = parse.getFloat()
		else
			t.Graphic.Glow.Threshold = 0.15
		end
		if parse.optionalString("+Flash Time:") then
			t.Graphic.Glow.FlashTime = parse.getFloat()
		else
			t.Graphic.Glow.FlashTime = 1.0
		end
		if parse.optionalString("+Delay Time:") then
			t.Graphic.Glow.DelayTime = parse.getFloat()
		else
			t.Graphic.Glow.DelayTime = 3.0
		end
	end

	if parse.optionalString("$Hull Bar Positioning:") then
		t.Graphic.Bar = NUi:ParseGraphicBlock(self.ScaleFactor)
		if parse.optionalString("+Label:") then
			t.Graphic.Bar.LabelImage = parse.getString()
		end
		if parse.optionalString("+Direction:") then
			t.HullDirection = NUi:GetDirection(parse.getString())
		else
			t.HullDirection = NUi.Direction.RIGHT
		end
	end

	if parse.optionalString("$Hull Bar Frame Colors:") then
		t.Colors.BarFrame = NUi:ParseColorBlock()
	end

	if parse.optionalString("$Hull Bar Colors:") then
		t.Colors.Bar = NUi:ParseColorBlock()
	end

	parse.requiredString("#End")

	self.HullBar = t
end

function NUiShipIntegrity:SetupIntegrityFrame()
	local t = self.IntegrityFrame
	t.Draw = NUiShipIntegrity.DrawIntegrityFrame
end

function NUiShipIntegrity:SetupHullBar()
	if not self.ShowSubsystemDamage then
		mn.runSEXP("(hud-set-retail-gauge-active (false) !Damage!)")
	end

	local t = self.HullBar

	t.CurrentHull = 1
	t.DisplayedHull = 1
	t.LastHull = 1
	t.LastClass = -1

	if self.LowHealthAlerts then
		t.LowHealth = {"LowHealth1", "LowHealth2"}
		t.LowHealthLevel = 0
		t.LowHealthTimer = -1
	end

	t.Draw = NUiShipIntegrity.DrawHullBar
end

function NUiShipIntegrity:DrawIntegrityFrame()
	local barInfo = self.Graphic

--- @type ElementInfo
	local frameElement = {
		Position = {
			x = barInfo.Frame.Position.x,
			y = barInfo.Frame.Position.y
		},
		Offset = {
			x = barInfo.Frame.Offset[1],
			y = barInfo.Frame.Offset[2]
		},
		Dimensions = {
			Width = barInfo.Frame.Width,
			Height = barInfo.Frame.Height
		},
		Image = barInfo.Frame.File,
		Color = NUi:ResolveColor(self.Colors.Frame, 1),
		Direction = NUi.Direction.LEFT,
		Percentage = 1
	}

	NUi:DrawHudElement(frameElement)
end

function NUiShipIntegrity:DrawHullBar()
	local object = hv.Player
	local barInfo = self.Graphic
	local fullPercentage = 1

	if NUiShipIntegrity.AutoGuardian then
		if self.LastClass ~= object.Class:getShipClassIndex() then
			self.LastClass = object.Class:getShipClassIndex()
			NUiShipIntegrity:GuardianAllSS(object)
		end
	end

	;----------FRAME----------;
--- @type ElementInfo
	local barFrameElement = {
		Position = {
			x = barInfo.BarFrame.Position.x,
			y = barInfo.BarFrame.Position.y
		},
		Offset = {
			x = barInfo.BarFrame.Offset[1],
			y = barInfo.BarFrame.Offset[2]
		},
		Dimensions = {
			Width = barInfo.BarFrame.Width,
			Height = barInfo.BarFrame.Height
		},
		Image = barInfo.BarFrame.File,
		Color = NUi:ResolveColor(self.Colors.BarFrame, 1),
		Direction = self.HullDirection,
		Percentage = fullPercentage
	}

--- @type ElementInfo
	local barFrameLabelElement = {
		Position = {
			x = barInfo.BarFrame.Position.x,
			y = barInfo.BarFrame.Position.y
		},
		Offset = {
			x = barInfo.BarFrame.Offset[1],
			y = barInfo.BarFrame.Offset[2]
		},
		Dimensions = {
			Width = barInfo.BarFrame.Width,
			Height = barInfo.BarFrame.Height
		},
		Image = barInfo.BarFrame.LabelImage,
		Color = self.Colors.BarFrame.Label,
		Direction = self.HullDirection,
		Percentage = fullPercentage
	}

	;----------HULL BAR----------;
	self.CurrentHull = object.HitpointsLeft / object.HitpointsMax
	self.DisplayedHull = AxEase:Process("hull", self.LastHull, self.CurrentHull, 0.5, "linear", 0.01) or self.DisplayedHull

	local barColor = self.Colors.Bar.Good
	if self.DisplayedHull <= 0.15 then
		barColor = self.Colors.Bar.Bad
	elseif self.DisplayedHull <= 0.33 then
		barColor = self.Colors.Bar.Warning
	end

--- @type ElementInfo
	local barElement = {
		Position = {
			x = barInfo.Bar.Position.x,
			y = barInfo.Bar.Position.y
		},
		Offset = {
			x = barInfo.Bar.Offset[1],
			y = barInfo.Bar.Offset[2]
		},
		Dimensions = {
			Width = barInfo.Bar.Width,
			Height = barInfo.Bar.Height
		},
		Image = barInfo.Bar.File,
		Color = barColor,
		Direction = self.HullDirection,
		Percentage = self.DisplayedHull
	}

--- @type ElementInfo
	local barLabelElement = {
		Position = {
			x = barInfo.Bar.Position.x,
			y = barInfo.Bar.Position.y
		},
		Offset = {
			x = barInfo.Bar.Offset[1],
			y = barInfo.Bar.Offset[2]
		},
		Dimensions = {
			Width = barInfo.Bar.Width,
			Height = barInfo.Bar.Height
		},
		Image = barInfo.Bar.LabelImage,
		Color = self.Colors.Bar.Label,
		Direction = self.HullDirection,
		Percentage = self.DisplayedHull
	}

	if barInfo.Glow and self.DisplayedHull <= barInfo.Glow.Threshold then
		local cycleTime = barInfo.Glow.FlashTime + barInfo.Glow.DelayTime
		local phase = mn.getMissionTime() % cycleTime
		if phase < barInfo.Glow.FlashTime then
			local pulse = math.sin(phase / barInfo.Glow.FlashTime * math.pi)
			local glowAlpha = math.floor(pulse * 255)
			if glowAlpha > 0 then
--- @type ElementInfo
				local glowElement = {
					Position = { x = barInfo.Glow.Position.x, y = barInfo.Glow.Position.y },
					Offset = { x = barInfo.Glow.Offset[1], y = barInfo.Glow.Offset[2] },
					Dimensions = { Width = barInfo.Glow.Width, Height = barInfo.Glow.Height },
					Image = barInfo.Glow.File,
					Color = { barColor[1], barColor[2], barColor[3], glowAlpha },
					Direction = self.HullDirection,
					Percentage = 1
				}
				NUi:DrawHudElement(glowElement)
			end
		end
	end

	NUi:DrawHudElement(barFrameElement)
	NUi:DrawHudElement(barFrameLabelElement)
	NUi:DrawHudElement(barElement)
	NUi:DrawHudElement(barLabelElement)

	if NUiShipIntegrity.LowHealthAlerts then
		local soundPlayed = false

		if self.CurrentHull <= 0.15 and self.LastHull > 0.15 then
			self.Sound = ad.playSound(ad.getSoundentry(self.LowHealth[2]))
			soundPlayed = true
		end

		if self.CurrentHull <= 0.33 and self.LastHull > 0.33 and not soundPlayed then
			if self.Sound and self.Sound:isPlaying() then self.Sound:stop() end
			self.Sound = ad.playSound(ad.getSoundentry(self.LowHealth[1]))
		end
	end

	self.LastHull = self.CurrentHull

end

function NUiShipIntegrity:ConfigShieldBar()
	self.ShowShields = true
	self.Active.ShieldBar = true
	self.ShieldBar = ParseEnergyBarConfig(self)
end

function NUiShipIntegrity:SetupShieldBar()
	local t = self.ShieldBar
	t.LastShield = 1
	t.DisplayedShield = 1
	t.LabelPixel = nil
	t.LastTargetPixel = nil
	t.LowQuadActive = false
	t.Draw = NUiShipIntegrity.DrawShieldBar
end


function NUiShipIntegrity:DrawShieldBar()
	local object = hv.Player
	local barInfo = self.Graphic
	local rawShield = object.Shields.CombinedLeft / object.Shields.CombinedMax
	local dt = ba.getRealFrametime()
	local diff = rawShield - self.DisplayedShield
	if diff < -0.001 then
		local lerpSpeed = diff < -0.02 and 20.0 or 8.0
		self.DisplayedShield = self.DisplayedShield + diff * math.min(1.0, dt * lerpSpeed)
	else
		self.DisplayedShield = math.min(rawShield, self.DisplayedShield + object.ShieldRegenRate * dt)
	end


	local anyLow, allHigh = false, true
	for i = 1, #object.Shields do
		local q = object.Shields[i] / object.Shields.CombinedMax
		if q < 0.05 then anyLow = true end
		if q < 0.08 then allHigh = false end
	end
	if anyLow then self.LowQuadActive = true
	elseif allHigh then self.LowQuadActive = false end
	local lowQuad = self.LowQuadActive

	local damaged = self.DisplayedShield < (self.LastShield or 1)
	local resolvedColor
	local barLabelColor = self.Colors.Bar.Label
	if lowQuad then
		local shimmerPhase = math.sin(ba.getSecondsOverall() * self.ShimmerSpeed * math.pi * 2)
		local shimmerAlpha = math.floor((0.6 + 0.2 * shimmerPhase) * 255)
		local base = self.Colors.Bar.Bad
		resolvedColor = { base[1], base[2], base[3], shimmerAlpha }
		barLabelColor = self.ShimmerWhite
	elseif damaged then
		resolvedColor = self.Colors.Bar.Warning
	else
		resolvedColor = self.Colors.Bar.Good
	end

--- @type ElementInfo
	local frameElement = {
		Position = { x = barInfo.Frame.Position.x, y = barInfo.Frame.Position.y },
		Offset = { x = barInfo.Frame.Offset[1], y = barInfo.Frame.Offset[2] },
		Dimensions = { Width = barInfo.Frame.Width, Height = barInfo.Frame.Height },
		Image = barInfo.Frame.File,
		Color = NUi:ResolveColor(self.Colors.Frame, 1),
		Direction = self.Direction,
		Percentage = 1
	}

--- @type ElementInfo
	local frameLabelElement = {
		Position = { x = barInfo.Frame.Position.x, y = barInfo.Frame.Position.y },
		Offset = { x = barInfo.Frame.Offset[1], y = barInfo.Frame.Offset[2] },
		Dimensions = { Width = barInfo.Frame.Width, Height = barInfo.Frame.Height },
		Image = barInfo.Frame.LabelImage,
		Color = self.Colors.Frame.Label,
		Direction = self.Direction,
		Percentage = 1
	}

--- @type ElementInfo
	local barElement = {
		Position = { x = barInfo.Bar.Position.x, y = barInfo.Bar.Position.y },
		Offset = { x = barInfo.Bar.Offset[1], y = barInfo.Bar.Offset[2] },
		Dimensions = { Width = barInfo.Bar.Width, Height = barInfo.Bar.Height },
		Image = barInfo.Bar.File,
		Color = resolvedColor,
		Direction = self.Direction,
		Percentage = self.DisplayedShield,
		Inverse = barInfo.Bar.Inverse
	}

	local targetPixel = math.floor(self.DisplayedShield * barInfo.Bar.Width)
	if self.LastTargetPixel ~= nil and targetPixel ~= self.LastTargetPixel then
		NDebugger:log("shield", string.format("raw=%.4f calc=%.4f px=%d", rawShield, self.DisplayedShield, targetPixel))
	end
	self.LastTargetPixel = targetPixel
	if self.LabelPixel == nil then self.LabelPixel = targetPixel end
	local pixelDiff = targetPixel - self.LabelPixel
	if barInfo.Bar.Inverse then
		if pixelDiff < 0 or pixelDiff > 1 then self.LabelPixel = targetPixel end
	else
		if pixelDiff > 0 or pixelDiff < -1 then self.LabelPixel = targetPixel end
	end

--- @type ElementInfo
	local barLabelElement = {
		Position = { x = barInfo.Bar.Position.x, y = barInfo.Bar.Position.y },
		Offset = { x = barInfo.Bar.Offset[1], y = barInfo.Bar.Offset[2] },
		Dimensions = { Width = barInfo.Bar.Width, Height = barInfo.Bar.Height },
		Image = barInfo.Bar.LabelImage,
		Color = barLabelColor,
		Direction = self.Direction,
		Percentage = self.LabelPixel / barInfo.Bar.Width,
		Inverse = barInfo.Bar.Inverse
	}

	NUi:DrawHudElement(frameElement)
	NUi:DrawHudElement(frameLabelElement)
	NUi:DrawHudElement(barElement)
	NUi:DrawHudElement(barLabelElement)
	self.LastShield = self.DisplayedShield
end

function NUiShipIntegrity:ConfigBoostBar()
	self.Active.BoostBar = true

	local t = { Graphic = {}, Colors = {} }

	if parse.optionalString("$Frame Positioning:") then
		t.Graphic.Frame = NUi:ParseGraphicBlock(self.ScaleFactor)
	end

	if parse.optionalString("$Label Positioning:") then
		t.Graphic.Label = NUi:ParseGraphicBlock(self.ScaleFactor)
	end

	if parse.optionalString("$Glow Positioning:") then
		t.Graphic.Glow = NUi:ParseGraphicBlock(self.ScaleFactor)
		if parse.optionalString("+Threshold:") then
			t.Graphic.Glow.Threshold = parse.getFloat()
		else
			t.Graphic.Glow.Threshold = 0.9
		end
	end

	if parse.optionalString("$Bar Positioning:") then
		t.Graphic.Bar = NUi:ParseGraphicBlock(self.ScaleFactor)
		if parse.optionalString("+Direction:") then
			t.BoostDirection = NUi:GetDirection(parse.getString())
		else
			t.BoostDirection = NUi.Direction.RIGHT
		end
	end

	if parse.optionalString("$Frame Colors:") then
		t.Colors.Frame = NUi:ParseColorBlock()
	end

	if parse.optionalString("$Label Colors:") then
		t.Colors.Label = NUi:ParseColorBlock()
	end

	if parse.optionalString("$Bar Colors:") then
		t.Colors.Bar = NUi:ParseColorBlock()
	end

	parse.requiredString("#End")
	self.BoostBar = t
end

function NUiShipIntegrity:SetupBoostBar()
	mn.runSEXP("(hud-set-retail-gauge-active (false) !Afterburner energy!)")
	local t = self.BoostBar
	t.Draw = NUiShipIntegrity.DrawBoostBar
end

function NUiShipIntegrity:DrawBoostBar()
	local barInfo = self.Graphic
	local fuelLevel = hv.Player.AfterburnerFuelLeft / hv.Player.AfterburnerFuelMax
	local displayLevel = 1 - fuelLevel
	local resolvedColor = NUi:ResolveThermalColor(self.Colors.Bar, displayLevel)

	if barInfo.Frame then
--- @type ElementInfo
		local frameElement = {
			Position = { x = barInfo.Frame.Position.x, y = barInfo.Frame.Position.y },
			Offset = { x = barInfo.Frame.Offset[1], y = barInfo.Frame.Offset[2] },
			Dimensions = { Width = barInfo.Frame.Width, Height = barInfo.Frame.Height },
			Image = barInfo.Frame.File,
			Color = NUi:ResolveColor(self.Colors.Frame, 1),
			Direction = self.BoostDirection,
			Percentage = 1
		}
		NUi:DrawHudElement(frameElement)
	end

	if barInfo.Glow then
		local glowThreshold = barInfo.Glow.Threshold
		local glowAlpha = math.floor(math.max(0, (displayLevel - glowThreshold) / (1 - glowThreshold)) * 255)
		if glowAlpha > 0 then
--- @type ElementInfo
			local glowElement = {
				Position = { x = barInfo.Glow.Position.x, y = barInfo.Glow.Position.y },
				Offset = { x = barInfo.Glow.Offset[1], y = barInfo.Glow.Offset[2] },
				Dimensions = { Width = barInfo.Glow.Width, Height = barInfo.Glow.Height },
				Image = barInfo.Glow.File,
				Color = { resolvedColor[1], resolvedColor[2], resolvedColor[3], glowAlpha },
				Direction = self.BoostDirection,
				Percentage = 1
			}
			NUi:DrawHudElement(glowElement)
		end
	end

--- @type ElementInfo
	local labelElement = {
		Position = { x = barInfo.Label.Position.x, y = barInfo.Label.Position.y },
		Offset = { x = barInfo.Label.Offset[1], y = barInfo.Label.Offset[2] },
		Dimensions = { Width = barInfo.Label.Width, Height = barInfo.Label.Height },
		Image = barInfo.Label.File,
		Color = resolvedColor,
		Direction = self.BoostDirection,
		Percentage = 1
	}

--- @type ElementInfo
	local barElement = {
		Position = { x = barInfo.Bar.Position.x, y = barInfo.Bar.Position.y },
		Offset = { x = barInfo.Bar.Offset[1], y = barInfo.Bar.Offset[2] },
		Dimensions = { Width = barInfo.Bar.Width, Height = barInfo.Bar.Height },
		Image = barInfo.Bar.File,
		Color = resolvedColor,
		Direction = self.BoostDirection,
		Percentage = fuelLevel,
		Inverse = barInfo.Bar.Inverse
	}

	NUi:DrawHudElement(labelElement)
	NUi:DrawHudElement(barElement)
end


function NUiShipIntegrity:Draw()
	if hv.Player.HitpointsLeft > 0 then
		if self.Active.IntegrityFrame then self.IntegrityFrame:Draw() end
		if self.Active.HullBar then self.HullBar:Draw() end
		if self.Active.ShieldBar then self.ShieldBar:Draw() end
		if self.Active.BoostBar then self.BoostBar:Draw() end
	end
end

function NUiShipIntegrity:GuardianAllSS(ship)
	for i=1, #ship do
		local ss = ship[i]
		local name = ship.Name
		local ssName = ss:getModelName()

		if not self:CheckSubsys(ssName) then
			mn.runSEXP("( ship-subsys-guardian-threshold 100 !" .. name .. "! !" .. ssName .. "! )")
		end
	end
end

function NUiShipIntegrity:CheckSubsys(subsys)
	if self.ExcludeSubsys then
		for i=1, #self.ExcludeSubsys do
			local entry = self.ExcludeSubsys[i]

			if entry == subsys then return true end

		end

		return false
	end
end
