FontManager = {}


local FontManager_mt = Class(FontManager)
local modDirectory = g_currentModDirectory

local modXML = XMLFile.load("fontModDesc", modDirectory .. "modDesc.xml")
FontManager.VERSION = modXML:getString("modDesc.version")
modXML:delete()


local closestCharacters = {
	[163] = 36,
	[8364] = 36,
	[192] = 65,
	[193] = 65,
	[194] = 65,
	[195] = 65,
	[196] = 65,
	[199] = 67,
	[200] = 69,
	[201] = 69,
	[202] = 69,
	[203] = 69,
	[204] = 73,
	[206] = 73,
	[207] = 73,
	[210] = 79,
	[212] = 79,
	[214] = 79,
	[219] = 85,
	[220] = 85,
	[224] = 97,
	[226] = 97,
	[227] = 97,
	[228] = 97,
	[231] = 99,
	[232] = 101,
	[233] = 101,
	[234] = 101,
	[235] = 101,
	[237] = 105,
	[238] = 105,
	[239] = 105,
	[241] = 110,
	[243] = 111,
	[244] = 111,
	[245] = 111,
	[246] = 111,
	[250] = 117,
	[251] = 117,
	[252] = 117,
	[8216] = 39,
	[8217] = 39
}

local invalidCharacters = {
	[8592] = true,
	[8593] = true,
	[8594] = true,
	[8595] = true,
	[9003] = true,
	[9166] = true
}


function FontManager.new()

	local self = setmetatable({}, FontManager_mt)

	self:setDefaultScreenResolutionAdjustment()

	self.fonts = {}
	self.languages = {}
	self.language = "latin"
	self.defaultFont = "nunito_sans"
	self.missingFonts = {}
	self.cache2D = {}
	self.cache3D = {}
	self.cache3DLinked = {}
	self.cachedOverlays = {}
	self.cachedLineOverlays = {}
	self.sizeScale = 1
	self.forceDeletion = false

	self.render2D, self.render3D = true, true

	self.args = {
		["colour"] = { 1, 1, 1, 1 },
		["isButton"] = false,
		["bold"] = false,
		["italic"] = false,
		["underline"] = false,
		["strikethrough"] = false,
		["useEngineRenderer"] = false,
		["alignX"] = RenderText.ALIGN_LEFT,
		["alignY"] = RenderText.VERTICAL_ALIGN_MIDDLE,
		["font"] = self.defaultFont,
		["clip"] = { 0, 0, 1, 1 },
		["lines"] = {
			["indentation"] = 0,
			["width"] = 0,
			["startLine"] = 0,
			["numLines"] = 0,
			["heightScale"] = RenderText.DEFAULT_LINE_HEIGHT_SCALE
		},
		["lines3D"] = {
			["indentation"] = 0,
			["width"] = 0,
			["max"] = 0,
			["heightScale"] = RenderText.DEFAULT_LINE_HEIGHT_SCALE,
			["autoScale"] = false,
			["removeSpaces"] = false,
			["numWords"] = 0
		}
	}

	local _, yOffsetBaseline = getNormalizedScreenValues(0, 8)
	local _, yOffsetMiddle = getNormalizedScreenValues(0, 64)

	self.yOffset = {
		["baseline"] = yOffsetBaseline,
		["middle"] = yOffsetMiddle
	}

	self:replaceEngineFunctions()
	self:loadFonts()

	return self

end


function FontManager:replaceEngineFunctions()

	local engine = {
		["renderText3D"] = renderText3D,
		["renderText"] = renderText,
		["setTextBold"] = setTextBold,
		["setTextColor"] = setTextColor,
		["setTextAlignment"] = setTextAlignment,
		["setTextVerticalAlignment"] = setTextVerticalAlignment,
		["setTextClipArea"] = setTextClipArea,
		["setTextFirstLineIndentation"] = setTextFirstLineIndentation,
		["setTextWrapWidth"] = setTextWrapWidth,
		["setTextLineBounds"] = setTextLineBounds,
		["setTextLineHeightScale"] = setTextLineHeightScale,
		["getTextWidth"] = getTextWidth,
		["draw"] = draw
	}


	local isDrawing = false

	
	draw = function()

		for _, cache in pairs(self.cache3D) do cache.delete = true end
		for _, cache in pairs(self.cache2D) do cache.delete = not cache.defer end

		isDrawing = true

		engine.draw()

		isDrawing = false

		for i = #self.cache3D, 1, -1 do

			if self.cache3D[i].delete then
				delete(self.cache3D[i].node)
				table.remove(self.cache3D, i)
			end

		end

		local toRemove = {}

		for i, cache in pairs(self.cache2D) do

			if cache.delete or self.forceDeletion then

				for _, overlay in pairs(cache.overlays) do table.insert(self.cachedOverlays, overlay) end

				for _, line in pairs(cache.lines) do

					if line.underline ~= nil then table.insert(self.cachedLineOverlays, line.underline) end
					if line.strikethrough ~= nil then table.insert(self.cachedLineOverlays, line.strikethrough) end

				end

				table.insert(toRemove, i)
				continue

			end

			if cache.defer and not self.forceDeletion then

				for _, overlay in pairs(cache.overlays) do overlay:render() end

				for _, line in pairs(cache.lines) do

					if line.underline ~= nil then line.underline:render() end
					if line.strikethrough ~= nil then line.strikethrough:render() end

				end

				cache.defer = false

			end

		end

		for i = #toRemove, 1, -1 do
			table.remove(self.cache2D, toRemove[i])
		end

		self.forceDeletion = false

	end


	setTextColor = function(r, g, b, a)

		self.args.colour = { r, g, b, a }
		engine.setTextColor(r, g, b, a)

	end


	setTextBold = function(isBold)

		self.args.bold = isBold
		engine.setTextBold(isBold)

	end


	setTextAlignment = function(value)

		self.args.alignX = value
		engine.setTextAlignment(value)

	end

	setTextFont = function(fontName)

		self.args.font = fontName

	end


	setTextItalic = function(isItalic)

		self.args.italic = isItalic or false

	end


	setTextUnderlined = function(isUnderlined)

		self.args.underline = isUnderlined or false

	end


	setTextStrikethrough = function(isStrikethrough)

		self.args.strikethrough = isStrikethrough or false

	end


	setTextVerticalAlignment = function(value)

		self.args.alignY = value
		engine.setTextVerticalAlignment(value)

	end


	setTextClipArea = function(x1, y1, x2, y2)

		self.args.clip = { x1, y1, x2, y2 }
		engine.setTextClipArea(x1, y1, x2, y2)

	end


	setTextFirstLineIndentation = function(indentation)

		self.args.lines.indentation = indentation or 0
		engine.setTextFirstLineIndentation(indentation)

	end


	setTextWrapWidth = function(width)

		self.args.lines.width = width or 0
		engine.setTextWrapWidth(width)

	end


	set3DTextWrapWidth = function(width)

		self.args.lines3D.width = width or 0

	end


	set3DTextAutoScale = function(autoScale)

		self.args.lines3D.autoScale = autoScale or false

	end


	set3DTextRemoveSpaces = function(removeSpaces)

		self.args.lines3D.removeSpaces = removeSpaces or false

	end

	
	set3DTextWordsPerLine = function(numWords)

		self.args.lines3D.numWords = numWords or 0

	end


	setTextLineBounds = function(startLine, numLines)

		self.args.lines.startLine = startLine
		self.args.lines.numLines = numLines
		engine.setTextLineBounds(startLine, numLines)

	end


	setTextLineHeightScale = function(heightScale)

		self.args.lines.heightScale = heightScale or 1.1
		self.args.lines3D.heightScale = heightScale or 1.1
		engine.setTextLineHeightScale(heightScale)

	end


	setTextUseEngineRenderer = function(useEngineRenderer)

		self.args.useEngineRenderer = useEngineRenderer

	end


	getTextWidth = function(size, text, fontName, is3D, isBold, isItalic)

		if not is3D then return engine.getTextWidth(size, text) end

		fontName = fontName or self.args.font or self.defaultFont

		local variationName = "regular"

		if isBold and isItalic then
			variationName = "boldItalic"
		elseif isBold then
			variationName = "bold"
		elseif isItalic then
			variationName = "italic"
		end

		local font = self:getFont(fontName)
		local width = 0

		for i = 1, #text do

			local character = self:getCharacter(font, utf8Substr(text, i - 1, 1))

			if character == nil then
				width = width + 0.5
				continue
			end

			local variation = character:getVariation(variationName)
			width = (width - variation.left) + variation.right

		end

		return width * size

	end


	setTextIsButton = function(isButton)

		self.args.isButton = isButton or false

	end


	renderText3D = function(x, y, z, rx, ry, rz, size, text, fontName)

		if not self.render3D then

			engine.renderText3D(x, y, z, rx, ry, rz, size, text)
			return

		end

		local args = self.args

		fontName = fontName or args.font or self.defaultFont


		local variationName = "regular"

		if args.bold and args.italic then
			variationName = "boldItalic"
		elseif args.bold then
			variationName = "bold"
		elseif args.italic then
			variationName = "italic"
		end

		for _, cache in pairs(self.cache3D) do

			if cache.x == x and cache.y == y and cache.z == z and cache.rx == rx and cache.ry == ry and cache.rz == rz and cache.size == size and cache.text == text and cache.fontName == fontName then
				cache.delete = false
				return
			end

		end

		local node = clone(self.text, true, false, false)
		setVisibility(node, true)
		
		setTranslation(node, 0.25 * size, 0, 0)
		setWorldTranslation(node, x, y, z)
		setWorldRotation(node, rx, ry, rz)

		local font = self:getFont(fontName)
		local variationNode = font.nodes[variationName]
		local xOffset, yOffset = 0, 0

		local colour = args.colour
		setScale(node, size, size, 0)

		for i = 1, #text do

			local character = self:getCharacter(font, utf8Substr(text, i - 1, 1))

			if character == nil then
				xOffset = xOffset + 0.5
				continue
			end

			local charNode = clone(variationNode, false, false, false)
			link(node, charNode)

			setShaderParameter(charNode, "index", character.index, nil, nil, nil, false)
			setShaderParameter(charNode, "colorScale", colour[1], colour[2], colour[3], colour[4], false)

			setTranslation(charNode, xOffset, 0, 0)
			xOffset = xOffset + character:getVariation(variationName).width / 128

		end

		table.insert(self.cache3D, {
			["delete"] = false,
			["node"] = node,
			["x"] = x,
			["y"] = y,
			["z"] = z,
			["rx"] = rx,
			["ry"] = ry,
			["rz"] = rz,
			["size"] = size,
			["text"] = text,
			["fontName"] = fontName
		})

	end


	create3DLinkedText = function(parent, x, y, z, rx, ry, rz, size, text, fontName)
	
		local args = self.args
		fontName = fontName or args.font or self.defaultFont


		local variationName = "regular"

		if args.bold and args.italic then
			variationName = "boldItalic"
		elseif args.bold then
			variationName = "bold"
		elseif args.italic then
			variationName = "italic"
		end

		local node = clone(self.text, false, false, false)
		link(parent, node)
		setVisibility(node, true)

		setTranslation(node, x + 0.25 * size, y, z)
		setRotation(node, rx, ry + math.pi / 2, rz)

		local font = self:getFont(fontName)
		local variationNode = font.nodes[variationName]
		local xOffset, yOffset = 0, 0

		local colour = args.colour
		setScale(node, size, size, 0)

		local words = string.split(text, " ")
		local lines = { { ["text"] = "", ["width"] = 0, ["x"] = 0, ["y"] = 0, ["scale"] = 1 } }
		local lineConfig = args.lines3D
		local line = lines[1]

		local numWordsOnLine = 0


		for j, word in pairs(words) do

			local wordWidth = 0

			for i = 1, #word do

				local character = self:getCharacter(font, utf8Substr(word, i - 1, 1))

				if character == nil then
					wordWidth = wordWidth + 0.5
					continue
				end

				local variation = character:getVariation(variationName)
				wordWidth = (wordWidth - variation.left) + variation.right

			end

			if (lineConfig.numWords ~= 0 and lineConfig.numWords == numWordsOnLine) or (lineConfig.width ~= 0 and (line.width + wordWidth) * size > lineConfig.width) then
				
				if line.text == "" and lineConfig.autoScale then
					line.scale = lineConfig.width / (wordWidth * size)
				elseif line.text ~= "" then
					table.insert(lines, { ["text"] = "", ["width"] = 0, ["x"] = 0, ["y"] = 0, ["scale"] = 1 })
					line = lines[#lines]
					numWordsOnLine = 0
				end

			end

			line.text = line.text .. word
			line.width = line.width + wordWidth
			numWordsOnLine = numWordsOnLine + 1

			if not lineConfig.removeSpaces and j ~= #words then
				line.text = line.text .. " "
				line.width = line.width + 0.5
			end

		end


		local lowestX, lowestY, highestX, highestY


		for j, line in pairs(lines) do

			if args.alignX == RenderText.ALIGN_CENTER then
				line.x = line.x - line.width / 2
			elseif args.alignX == RenderText.ALIGN_RIGHT then
				line.x = line.x - line.width
			end

			if #lines > 1 then

				if args.alignY == RenderText.VERTICAL_ALIGN_BASELINE then
					line.y = 1.5 - j
				elseif args.alignY == RenderText.VERTICAL_ALIGN_TOP then
					line.y = 0.5 - j
				elseif args.alignY == RenderText.VERTICAL_ALIGN_MIDDLE then
					local centerLine = math.ceil(#lines / 2)
					if j ~= centerLine then line.y = centerLine - j end
				elseif args.alignY == RenderText.VERTICAL_ALIGN_BOTTOM then
					line.y = 0.5 + (#lines - j)
				end

				line.y = line.y * lineConfig.heightScale

			end

			local xOffset = 0

			for i = 1, #line.text do

				local character = self:getCharacter(font, utf8Substr(line.text, i - 1, 1))

				if character == nil then
					xOffset = xOffset + 0.5
					continue
				end

				local charNode = clone(variationNode, false, false, false)
				link(node, charNode)

				setShaderParameter(charNode, "index", character.index, nil, nil, nil, false)
				setShaderParameter(charNode, "colorScale", colour[1], colour[2], colour[3], colour[4], false)

				if line.scale ~= 1 then
					setScale(charNode, line.scale, line.scale, 1)
					xOffset = xOffset - (i - 1) * line.scale
				end

				local variation = character:getVariation(variationName)
				if i == #line.text and (highestX == nil or line.x + xOffset > highestX) then highestX = line.x + xOffset end
				xOffset = xOffset - variation.left
				setTranslation(charNode, line.x + xOffset, line.y, 0)
				if i == 1 and (lowestX == nil or line.x - variation.right < lowestX) then lowestX = line.x - variation.right end
				xOffset = xOffset + variation.right

			end

			if lowestY == nil or line.y - 0.25 < lowestY then lowestY = line.y - 0.25 end
			if highestY == nil or line.y + 0.5 > highestY then highestY = line.y + 0.5 end

		end


		if args.isButton then

			local start = createTransformGroup("start")
			local width = createTransformGroup("width")
			local height = createTransformGroup("height")

			link(node, start)
			link(node, width)
			link(node, height)

			setTranslation(start, lowestX, lowestY, 0)
			setTranslation(width, highestX, lowestY, 0)
			setTranslation(height, lowestX, highestY, 0)

		end


		self.cache3DLinked[node] = {
			["x"] = x,
			["y"] = y,
			["z"] = z,
			["rx"] = rx,
			["ry"] = ry,
			["rz"] = rz,
			["size"] = size,
			["text"] = text,
			["fontName"] = fontName
		}

		return node

	end


	function change3DLinkedTextColour(node, r, g, b, a)

		if node == nil or node == 0 then return end

		for i = 0, getNumOfChildren(node) - 1 do

			local child = getChildAt(node, i)
			setShaderParameter(child, "colorScale", r, g, b, a, false)

		end

	end


	function delete3DLinkedText(node)

		if entityExists(node) then delete(node) end

		self.cache3DLinked[node] = nil

	end


	local isLoading = g_gameStateManager:getGameState() == GameState.LOADING

	renderText = function(x, y, size, text, fontName)

		if not self.render2D then

			engine.renderText(x, y, size, text)
			return

		end
	
		local args = self.args

		if args.useEngineRenderer then
			engine.renderText(x, y, size, text)
			return
		end

		if isLoading then

			isLoading = false
			g_mpLoadingScreen.balanceText:setText(g_i18n:formatMoney(g_mpLoadingScreen.missionInfo.money or g_mpLoadingScreen.missionInfo.initialMoney))

		end
		
		fontName = fontName or self.args.font or self.defaultFont

		local variationName = "regular"

		if args.bold and args.italic then
			variationName = "boldItalic"
		elseif args.bold then
			variationName = "bold"
		elseif args.italic then
			variationName = "italic"
		end

		local lineConfig = args.lines
		local cachedRender
		local colour = args.colour
		local scale = size * 10 * self.sizeScale
		local engineY = y

		if args.alignY == RenderText.VERTICAL_ALIGN_BASELINE then
			y = y - self.yOffset.baseline * scale
		elseif args.alignY == RenderText.VERTICAL_ALIGN_TOP then
			y = y - self.yOffset.baseline * scale
		elseif args.alignY == RenderText.VERTICAL_ALIGN_MIDDLE then
			y = y - self.yOffset.middle * scale
		elseif args.alignY == RenderText.VERTICAL_ALIGN_BOTTOM then
			y = y - self.yOffset.middle * scale * 2
		end

		for _, cache in pairs(self.cache2D) do

			local cacheColour = cache.colour

			if cache.x == x and cache.y == y and cache.size == size and cache.text == text and cache.fontName == fontName and cacheColour[1] == colour[1] and cacheColour[2] == colour[2] and cacheColour[3] == colour[3] and cacheColour[4] == colour[4] then
				cache.delete = false
				cachedRender = cache
				break
			end

		end
		
		local cx1, cy1, cx2, cy2 = unpack(args.clip)

		if cachedRender == nil then

			local overlays = {}
			local font = self:getFont(fontName)
			scale = scale * font.scale
			local width, height = size, size
			local lines = { { ["text"] = "", ["width"] = 0, ["x"] = x } }


			local function writeCharacter(character, variation, posX, posY)

				local isRendered, overlayWidth, overlayHeight = true, variation.screenWidth * scale, variation.screenHeight * scale
				local uvs

				if (cx1 == 0 and cy1 == 0 and cx2 == 1 and cy2 == 1) or (posX >= cx1 and posX + overlayWidth <= cx2 and posY >= cy1 and posY + overlayHeight <= cy2) then
					uvs = variation.uvs
				else
					isRendered, overlayWidth, overlayHeight, uvs = character:getClippedUVs(variationName, self.screenResolutionAdjustment, posX, posX + overlayWidth, posY, posY + overlayHeight, cx1, cy1, cx2, cy2, text)
					if not isRendered then return end
					overlayWidth, overlayHeight = overlayWidth * scale, overlayHeight * scale
				end

				local overlay

				if #self.cachedOverlays > 0 then
					overlay = self.cachedOverlays[1]
					table.remove(self.cachedOverlays, 1)
				else
					overlay = Overlay.new()
				end

				overlay:setImage(font.variations[variationName][self.screenResolutionAdjustment])
				overlay:setDimension(overlayWidth, overlayHeight)
				overlay:setPosition(posX, posY)
				overlay:setUVs(uvs)
				overlay:setColor(colour[1], colour[2], colour[3], colour[4])

				table.insert(overlays, overlay)

			end

			local textWidth = 0
			local words = string.split(text, " ")
			local line = lines[1]

			for j, word in pairs(words) do

				local wordWidth = 0

				for i = 1, #word do

					local character, useEngineRenderer = self:getCharacter(font, utf8Substr(word, i - 1, 1))

					if useEngineRenderer then
						engine.renderText(x, engineY, size, text)
						return
					end

					if character == nil then
						wordWidth = wordWidth + size * 0.25
						continue
					end

					local variation = character:getVariation(variationName, self.screenResolutionAdjustment)
					wordWidth = wordWidth + variation.screenWidth * scale

				end

				if lineConfig.width ~= 0 and line.width + wordWidth > lineConfig.width then
					table.insert(lines, { ["text"] = "", ["width"] = 0, ["x"] = x })
					line = lines[#lines]
				end

				line.text = line.text .. word
				line.width = line.width + wordWidth / font.scale

				if j ~= #words then
					line.text = line.text .. " "
					line.width = line.width + size * 0.25
				end

			end

			for j, line in pairs(lines) do

				if args.alignX == RenderText.ALIGN_CENTER then
					line.x = line.x - line.width / 2
				elseif args.alignX == RenderText.ALIGN_RIGHT then
					line.x = line.x - line.width
				end

				local text, xOffset, yOffset = line.text, 0, (j - 1) * size + self.yOffset.baseline * (self.sizeScale - 0.75)

				for i = 1, #text do

					local character = self:getCharacter(font, utf8Substr(text, i - 1, 1))

					if character == nil then
						xOffset = xOffset + size * 0.25
						continue
					end

					local variation = character:getVariation(variationName, self.screenResolutionAdjustment)
					writeCharacter(character, variation, line.x + xOffset, (y - yOffset))
					xOffset = xOffset + variation.screenWidth * scale

				end

			end


			cachedRender = {
				["delete"] = false,
				["overlays"] = overlays,
				["x"] = x,
				["y"] = y,
				["size"] = size,
				["text"] = text,
				["colour"] = colour,
				["fontName"] = fontName,
				["width"] = textWidth,
				["lines"] = {},
				["defer"] = false
			}

			for i, line in pairs(lines) do

				table.insert(cachedRender.lines, {
					["width"] = line.width,
					["x"] = line.x,
					["y"] = y - (i - 1) * size - self.yOffset.baseline * (self.sizeScale - 0.75)
				})

			end

			table.insert(self.cache2D, cachedRender)

		end

		if cachedRender == nil then return end

		cachedRender.defer = not isDrawing

		local colour = args.colour

		if isDrawing then

			for _, overlay in pairs(cachedRender.overlays) do overlay:render() end

		end

		if args.underline or args.strikethrough then

			for _, line in pairs(cachedRender.lines) do

				if args.underline then

					local overlay = line.underline

					if overlay == nil then

						local isRendered, screenWidth, screenHeight = true, line.width, size * 0.075
						isRendered, overlay, screenWidth, screenHeight = self:getLineOverlay(line.x, screenWidth, line.y, screenHeight, cx1, cy1, cx2, cy2)

						if isRendered then

							overlay:setDimension(screenWidth, screenHeight)
							overlay:setPosition(line.x, line.y - size * 0.05)
							line.underline = overlay

						end

					end

					if overlay ~= nil then
						overlay:setColor(colour[1], colour[2], colour[3], colour[4])
						if isDrawing then overlay:render() end

					end

				end

				if args.strikethrough then

					local overlay = line.strikethrough

					if overlay == nil then

						local isRendered, screenWidth, screenHeight = true, line.width, size * 0.075
						isRendered, overlay, screenWidth, screenHeight = self:getLineOverlay(line.x, screenWidth, line.y, screenHeight, cx1, cy1, cx2, cy2)

						if isRendered then

							overlay:setDimension(screenWidth, screenHeight)
							overlay:setPosition(line.x, line.y + size * 0.5)
							line.strikethrough = overlay

						end

					end

					if overlay ~= nil then

						overlay:setColor(colour[1], colour[2], colour[3], colour[4])
						if isDrawing then overlay:render() end

					end

				end

			end

		end

	end

end


function FontManager:getLineOverlay(leftX, screenWidth, bottomY, screenHeight, minX, minY, maxX, maxY)

	local uvs
	local rightX, topY = leftX + screenWidth, bottomY + screenHeight

	if (minX == 0 and minY == 0 and maxX == 1 and maxY == 1) or (leftX >= minX and rightX <= maxX and bottomY >= minY and topY <= maxY) then
		uvs = GuiUtils.getUVs({ 0, 0, 3, 3 }, { 4, 4 })
	else

		if leftX > maxX or rightX < minX or bottomY > maxY or topY < minY then return false, nil, nil, nil end

		local x, y, width, height = 0, 0, 3, 3

		if leftX < minX then x = width * ((minX - math.abs(leftX)) / (rightX - leftX)) end
		if rightX > maxX then width = width - width * ((rightX - maxX) / (rightX - leftX)) end

		if bottomY < minY then y = height * ((minY - math.abs(bottomY)) / (topY - bottomY)) end
		if topY > maxY then height = height - height * ((topY - maxY) / (topY - bottomY)) - y end

		uvs = GuiUtils.getUVs({ x, y, width, height }, { 4, 4 })

		screenWidth, screenHeight = screenWidth * (width / 3), screenHeight * (height / 3) 

	end
	

	local overlay

	if #self.cachedLineOverlays > 0 then
		overlay = self.cachedLineOverlays[1]
		table.remove(self.cachedLineOverlays, 1)
	else
		overlay = Overlay.new()
		overlay:setImage("dataS/menu/base/graph_pixel.png")
	end

	overlay:setUVs(uvs)
	return true, overlay, screenWidth, screenHeight

end


function FontManager:loadFonts()

	self.fontHolder = g_i3DManager:loadI3DFile(modDirectory .. "fonts/fontHolder.i3d")
	self.template = I3DUtil.indexToObject(self.fontHolder, "0|0")

	local i3dNode = g_i3DManager:loadI3DFile(modDirectory .. "fonts/text.i3d")
	self.text = getChildAt(i3dNode, 0)
	self.textGroup = createTransformGroup("fontLibrary_texts")

	link(getRootNode(), self.textGroup)
	link(self.textGroup, self.text)

	setVisibility(self.text, false)
	setVisibility(self.fontHolder, false)

	self:loadFontsFromXMLFile(modDirectory .. "fonts/fonts.xml", modDirectory)

end


function FontManager:loadFontsFromXMLFile(xmlPath, directory)

	local xmlFile = XMLFile.loadIfExists("fontsXML", xmlPath)
	local fontIds = {}

	if xmlFile == nil then return fontIds end

	xmlFile:iterate("fonts.font", function(_, key)

		local path = directory .. xmlFile:getString(key .. "#path")
		local fontXML = XMLFile.loadIfExists("fontXML", path .. "font.xml")

		if fontXML == nil then
			fontXML = XMLFile.loadIfExists("fontXML", path .. "/font.xml")
			path = path .. "/"
		end

		if fontXML ~= nil then
		
			local id, name = self:loadFont(fontXML, "font", path)
			fontIds[name] = id
			fontXML:delete()

		end

	end)

	xmlFile:delete()

	if self.settingsManager ~= nil then self.settingsManager.reloadFonts() end
	if self.fontViewerDialog ~= nil then self.fontViewerDialog:reloadFonts() end

	return fontIds

end


function FontManager:loadFont(xmlFile, key, directory)

	local transformGroup = clone(self.template, true, false, false)

	local language = xmlFile:getString(key .. "#language", "latin")
	local name = xmlFile:getString(key .. "#name")
	local id, i = name, 0

	if self.fonts[language] == nil then
		self.fonts[language] = {}
		self.languages[language] = {}
	end

	while self.fonts[language][id] ~= nil do

		i = i + 1
		id = name .. "_" .. i

	end

	setName(transformGroup, id)

	if xmlFile:getBool(key .. "#default", false) then self.languages[language].default = id end
	
	local font = {
		["name"] = name,
		["id"] = id,
		["nodes"] = {},
		["imageWidth"] = xmlFile:getInt(key .. ".image#width", 8192),
		["imageHeight"] = xmlFile:getInt(key .. ".image#height", 256),
		["cellWidth"] = xmlFile:getInt(key .. ".cell#width", 128),
		["cellHeight"] = xmlFile:getInt(key .. ".cell#height", 128),
		["variations"] = {
			["regular"] = {
				["0"] = string.format("%s%s_0.dds", directory, name),
				["1.5"] = string.format("%s%s_1.5.dds", directory, name)
			},
			["bold"] = {
				["0"] = string.format("%s%sBold_0.dds", directory, name),
				["1.5"] = string.format("%s%sBold_1.5.dds", directory, name)
			},
			["italic"] = {
				["0"] = string.format("%s%sItalic_0.dds", directory, name),
				["1.5"] = string.format("%s%sItalic_1.5.dds", directory, name)
			},
			["boldItalic"] = {
				["0"] = string.format("%s%sBoldItalic_0.dds", directory, name),
				["1.5"] = string.format("%s%sBoldItalic_1.5.dds", directory, name)
			}
		},
		["characters"] = {},
		["useable"] = xmlFile:getBool(key .. "#useable", true)
	}

	font.scale = 128 / font.cellWidth

	local files = {
		["regular"] = string.format("%s%s_alpha.dds", directory, name),
		["bold"] = string.format("%s%sBold_alpha.dds", directory, name),
		["italic"] = string.format("%s%sItalic_alpha.dds", directory, name),
		["boldItalic"] = string.format("%s%sBoldItalic_alpha.dds", directory, name)
	}


	local templateNode = getChild(transformGroup, "template")


	for variation, file in pairs(files) do

		local node = clone(templateNode, true, false, false)

		setName(node, variation)
		local material = setMaterialCustomMapFromFile(getMaterial(node, 0), "alphaMap", file, false, true, false)
		setMaterial(node, material, 0)
		setShaderParameter(node, "widthAndHeight", font.imageWidth / font.cellWidth, (font.imageHeight * 2) / font.cellHeight, nil, nil, false)

		font.nodes[variation] = node

	end

	xmlFile:iterate(key .. ".character", function(_, charKey)
		
		local character = FontCharacter.new(font, spacing)

		character:loadFromXMLFile(xmlFile, charKey, font.imageWidth, font.imageHeight, font.cellWidth, font.cellHeight)

		font.characters[character.byte] = character
		
	end)

	self.fonts[language][id] = font

	if self.settingsManager ~= nil and language == self.language and font.useable then self.settingsManager.addFont(id, name) end
	if self.fontViewerDialog ~= nil and language == self.language and font.useable then self.fontViewerDialog:addFont(id, name) end

	print(string.format("FontLibrary - Loaded font \'%s\' (%s) as %s", name, id, language:upper()))

	if language ~= "latin" then self:configureLanguage() end

	return id, name

end


function FontManager:setSettingsManager(manager)

	self.settingsManager = manager

	for id, font in pairs(self.fonts[self.language]) do
		if font.useable then manager.addFont(id, font.name) end
	end

	manager.reloadFonts()

end


function FontManager:setFontViewerDialog(dialog)

	self.fontViewerDialog = dialog

	for id, font in pairs(self.fonts[self.language]) do
		if font.useable then dialog:addFont(id, font.name) end
	end

	dialog:reloadFonts()

end


function FontManager:setDefaultFont(id)

	self.defaultFont = id or self.defaultFont

	if self.fonts[self.language][self.defaultFont] == nil then self.defaultFont = self.languages[self.language].default end

end


function FontManager:getFont(id)

	if self.fonts[self.language][id] ~= nil then return self.fonts[self.language][id] end

	if self.fonts.latin[id] ~= nil then return self.fonts.latin[id] end

	if self.languages[self.language] ~= nil then return self.fonts[self.language][self.languages[self.language].default] end

	return self.fonts.latin[self.languages.latin.default]

end


function FontManager:getCharacter(font, character)

	if character == nil then return nil end

	local byte = utf8ToUnicode(character)

	if invalidCharacters[byte] then return nil, true end

	if byte == 32 or byte == 160 then return nil end

	if font.characters[byte] ~= nil then return font.characters[byte], false end
	if closestCharacters[byte] ~= nil then return font.characters[closestCharacters[byte]], false end

	byte = utf8ToUnicode(utf8ToUpper(character))

	if font.characters[byte] ~= nil then return font.characters[byte], false end
	if closestCharacters[byte] ~= nil then return font.characters[closestCharacters[byte]], false end

	return nil, false

end


function FontManager:configureLanguage()

	local alphabets = {
		["cyrillic"] = {
			"uk",
			"ru"
		},
		["chinese"] = {
			"cs"
		},
		["japanese"] = {
			"jp"
		},
		["korean"] = {
			"kr"
		}
	}

	for alphabet, languages in pairs(alphabets) do
		for _, language in pairs(languages) do
			if g_languageShort == language then
				self.language = alphabet
				return
			end
		end
	end

	self.language = "latin"

end


function FontManager:getValidFonts()

	return self.fonts[self.language] or self.fonts.latin

end


function FontManager:setDefaultScreenResolutionAdjustment()

	local adjustment = "0"

	if g_screenWidth <= 2000 then adjustment = "1.5" end

	self.screenResolutionAdjustment, self.defaultScreenResolutionAdjustment = adjustment, adjustment

	print("FontManager:", string.format("\\___ Screen Resolution: %sx%s", g_screenWidth, g_screenHeight), string.format("\\___ Default 2D Rendering Adjustment: %s", adjustment))

end


function FontManager:getDefaultScreenResolutionAdjustment()

	return self.defaultScreenResolutionAdjustment

end


function FontManager.onSettingChanged(setting, value)

	g_fontManager[setting] = value
	if setting == "screenResolutionAdjustment" then g_fontManager.forceDeletion = true end

end


g_fontManager = FontManager.new()