-- custom font class (this is what the player interacts with)

local customFont = {};

function customFont.new(fontName, class, isButton)
	local self = event.new {};
	
	local exists = not (type(class) == "string");
	local child = exists and class or instance(class);
	local fontModule = fonts:FindFirstChild(fontName);
	--local folder = instance("Folder", child);
	
	local settings = settings.new(require(fontModule), self, child);
	settings:preload();
	
	local events = {};
	local properties = {};
	local propertyobjects = {};
	local drawncharacters = {};
	
	self.FontName = fontName;
	self.Style = settings.style;
	self.TextTransparency = child.TextTransparency;
	self.TextStrokeTransparency = child.TextStrokeTransparency;
	self.BackgroundTransparency = child.BackgroundTransparency;
	self.TextFits = false;
	
	-- create the physical representation of the custom properties
	for name, _ in next, customProperties do
		local property = self[name];
		local t = type(property);
		local className = upper(sub(t, 1, 1)) .. sub(t, 2) .. "Value";
		local physicalProperty = Instance.new(className, child);
		
		physicalProperty.Name = name;
		physicalProperty.Value = property;
		
		physicalProperty.Changed:connect(function(newValue)
			self[name] = newValue;
		end);
		
		propertyobjects[physicalProperty.Name] = physicalProperty;
		properties[physicalProperty] = true;
	end;
	
	local background = newBackground(child, isButton and "TextButton");
	defaultHide(child);	
	
	-- common function
	
	local function drawText()
		background:ClearAllChildren();
		drawncharacters = drawLines(child.Text, settings, background);
	end;
	
	-- custom events
	
	self:connect("FontName", function(value) drawText(); end);
	self:connect("TextStrokeTransparency", function(value) drawText(); end);
	
	self:connect("BackgroundTransparency", function(value)
		background.BackgroundTransparency = value;
	end);
	
	self:connect("Style", function(value)
		settings.style = value;
		propertyobjects["Style"].Value = value;
		drawText();
	end);
	
	self:connect("TextTransparency", function(value)
		for i = 1, #drawncharacters do
			drawncharacters[i].ImageTransparency = value;
		end;
	end);
	
	self:connect("FontName", function(value)
		local fontModule = fonts:FindFirstChild(value);
		settings = settings.new(require(fontModule), self, child);
		settings:preload();
		propertyobjects["FontName"].Value = value;
		if (not child.TextScaled) then
			settings.size = child.TextSize;
		end;
		settings.style = self.Style;
		drawText();
	end);
	
	-- real events
	
	insert(events, child.Changed:connect(function(property)
		if (overwrites[property]) then	
			if (child[property] ~= 2) then
				self[property] = child[property]	
			end;
			child[property] = 2;
		elseif (property == "TextSize") then
			settings.size = child[property];
			drawText();
		elseif (property == "TextColor3") then
			for _, sprite in next, drawncharacters do
				sprite.ImageColor3 = child[property];
			end;
		elseif (property == "ZIndex") then
			background.ZIndex = child[property];
			for _, sprite in next, drawncharacters do
				sprite.ZIndex = child[property];
			end;
		elseif (property == "Text") then
			drawText();
		elseif (redraws[property]) then
			if (property == "TextScaled" and not child[property]) then
				settings.size = child.TextSize;
			end;
			drawText();
		elseif (not match(property, "Text") and not noReplicate[property]) then
			pcall(function() background[property] = child[property]; end);
		end;
	end));
	
	if (child:IsA("TextBox")) then
		insert(events, child.Focused:connect(function()
			if (child.ClearTextOnFocus) then
				child.Text = "";
			end;
		end));
	end;
	
	-- methods
	
	function self:Revert()
		for _, property in next, propertyobjects do property:Destroy(); end;
		for _, event in next, events do event:disconnect(); end;
		background:Destroy();
		child.TextTransparency = self.TextTransparency;
		child.BackgroundTransparency = self.BackgroundTransparency;
		self, properties, propertyobjects, events = nil, nil, nil, nil;
		return child;
	end;
	
	function self:GetChildren()
		local children = {};
		for _, kid in next, child:GetChildren() do
			if (kid ~= background and not properties[kid]) then
				insert(children, kid);
			end;
		end;
		return children;
	end;
	
	function self:ClearAllChildren()
		for _, kid in next, child:GetChildren() do
			if (kid ~= background and not properties[kid]) then
				kid:Destroy();
			end;
		end;
	end;
	
	function self:Destroy()
		self:Revert():Destroy();
	end;
	
	-- return
	drawText();
	return wrapper(child, self);
end;