using System;
using System.Collections.Generic;
using System.Numerics;
using Robust.Shared.Utility;
using Robust.Client.ResourceManagement;
namespace Robust.Client.Graphics;
///
/// Stores a single style (Bold, Italic, Monospace), or any combination thereof.
///
public record FontVariant (FontStyle Style, FontResource[] Resource)
{
public virtual Font ToFont(byte size)
{
if (Resource.Length == 1)
return new VectorFont(Resource[0], size);
var fs = new Font[Resource.Length];
for (var i = 0; i < Resource.Length; i++)
fs[i] = new VectorFont(Resource[i], size);
return new StackedFont(fs);
}
};
internal record DummyVariant(FontStyle fs) : FontVariant(fs, new FontResource[0])
{
public override Font ToFont(byte size) => new DummyFont();
};
public record FontClass
(
string Id,
FontStyle Style,
FontSize Size
);
///
/// Manages font-based bookkeeping across a single stylesheet.
///
public interface IFontLibrary
{
FontClass Default { get; }
/// Associates a name to a set of font resources.
void AddFont(string name, params FontVariant[] variants);
/// Sets a standard size which can be reused across the Font Library.
void SetStandardSize(ushort number, byte size);
/// Sets a standard style which can be reused across the Font Library.
void SetStandardStyle(ushort number, string name, FontStyle style);
///
/// Returns a fancy handle in to the library.
/// The handle keeps track of relative changes to and .
///
IFontLibrarian StartFont(string id, FontStyle fst, FontSize fsz);
IFontLibrarian StartFont(FontClass? fclass = default) =>
StartFont(
(fclass ?? Default).Id,
(fclass ?? Default).Style,
(fclass ?? Default).Size
);
}
///
/// Acts as a handle in to an .
///
public interface IFontLibrarian
{
Font Current { get; }
Font Update(FontStyle fst, FontSize fsz);
}
public class FontLibrary : IFontLibrary
{
public FontClass Default { get; set; }
public FontLibrary(FontClass def)
{
Default = def;
}
private Dictionary _styles = new();
private Dictionary _standardSt = new();
private Dictionary _standardSz = new();
void IFontLibrary.AddFont(string name, params FontVariant[] variants) =>
_styles[name] = variants;
IFontLibrarian IFontLibrary.StartFont(string id, FontStyle fst, FontSize fsz) =>
new FontLibrarian(this, id, fst, fsz);
void IFontLibrary.SetStandardStyle(ushort number, string name, FontStyle style) =>
_standardSt[(FontStyle) number | FontStyle.Standard] = (name, style);
void IFontLibrary.SetStandardSize(ushort number, byte size) =>
_standardSz[(FontSize) number | FontSize.Standard] = size;
private FontVariant lookup(string id, FontStyle fst)
{
if (fst.HasFlag(FontStyle.Standard))
(id, fst) = _standardSt[fst];
FontVariant? winner = default;
foreach (var vr in _styles[id])
{
var winfst = winner?.Style ?? ((FontStyle) 0);
// Since the "style" flags are a bitfield, we can just see which one has more bits.
// More bits == closer to the desired font style. Free fallback!
// Variant's bit count
var vc = BitOperations.PopCount((ulong) (vr.Style & fst));
// Winner's bit count
var wc = BitOperations.PopCount((ulong) (winfst & fst));
if (winner is null || vc > wc)
winner = vr;
}
if (winner is null)
throw new Exception($"no matching font style ({id}, {fst})");
return winner;
}
private byte lookupSz(FontSize sz)
{
if (sz.HasFlag(FontSize.RelMinus) || sz.HasFlag(FontSize.RelPlus))
throw new Exception("can't look up a relative font through a library; get a Librarian first");
if (sz.HasFlag(FontSize.Standard))
return _standardSz[sz];
return (byte) sz;
}
class FontLibrarian : IFontLibrarian
{
public Font Current => _current;
private Font _current;
private FontLibrary _lib;
private string _id;
private FontStyle _fst;
private FontSize _fsz;
public FontLibrarian(FontLibrary lib, string id, FontStyle fst, FontSize fsz)
{
_id = id;
_fst = fst;
_fsz = fsz;
_lib = lib;
// Actual font entry
var f = lib.lookup(id, fst);
// Real size
var rsz = (byte) lib.lookupSz(fsz);
_current = f.ToFont(rsz);
}
Font IFontLibrarian.Update(FontStyle fst, FontSize fsz)
{
var f = _lib.lookup(_id, fst);
byte rsz = (byte) _fsz;
var msk = (byte) fsz & 0b0000_1111;
if (fsz.HasFlag(FontSize.Standard))
rsz = _lib.lookupSz(fsz);
else if (fsz.HasFlag(FontSize.RelPlus))
rsz = (byte) (((byte) _fsz) + msk);
else if (fsz.HasFlag(FontSize.RelMinus))
rsz = (byte) (((byte) _fsz) - msk);
_fsz = (FontSize) rsz;
_fst = fst;
return _current = f.ToFont((byte) rsz);
}
}
}