context: // 
  // Font.cs
  //  
  // Author:
  //       Lluis Sanchez <lluis@xamarin.com>
  // 
  // Copyright (c) 2011 Xamarin Inc
  // 
  // Permission is hereby granted, free of charge, to any person obtaining a copy
  // of this software and associated documentation files (the "Software"), to deal
  // in the Software without restriction, including without limitation the rights
  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  // copies of the Software, and to permit persons to whom the Software is
  // furnished to do so, subject to the following conditions:
  // 
  // The above copyright notice and this permission notice shall be included in
  // all copies or substantial portions of the Software.
  // 
  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  // THE SOFTWARE.
  
  using System;
  using Xwt.Backends;
  using System.Windows.Markup;
  using System.ComponentModel;
  using System.Text;
  using System.Globalization;
  using System.Collections.Generic;
  using System.Collections.ObjectModel;
  using System.Linq;
  
  
  namespace Xwt.Drawing
  {
  	[TypeConverter (typeof(FontValueConverter))]
  	[ValueSerializer (typeof(FontValueSerializer))]
  	public sealed class Font: XwtObject
  	{
  		FontBackendHandler handler;
  
  		internal Font (object backend): this (backend, null)
  		{
  		}
  		
  		internal Font (object backend, Toolkit toolkit)
  		{
  			if (toolkit != null)
  				ToolkitEngine = toolkit;
  			handler = ToolkitEngine.FontBackendHandler;
  			if (backend == null)
  				throw new ArgumentNullException ("backend");
  			Backend = backend;
  		}
  
  		internal void InitForToolkit (Toolkit tk)
  		{
  			if (ToolkitEngine != tk) {
  				// Gather existing font property before switching handler
  				var fname = Family;
  				var size = Size;
  				var style = Style;
  				var weight = Weight;
  				var stretch = Stretch;
  				var oldHandler = ToolkitEngine.FontBackendHandler;
  				ToolkitEngine = tk;
  				handler = tk.FontBackendHandler;
  
  				if (fname == oldHandler.SystemFont.Family)
  					Backend = handler.WithSettings (handler.SystemFont.Backend, size, style, weight, stretch);
  				else if (fname == oldHandler.SystemMonospaceFont.Family)
  					Backend = handler.WithSettings (handler.SystemMonospaceFont.Backend, size, style, weight, stretch);
  				else if (fname == oldHandler.SystemSansSerifFont.Family)
  					Backend = handler.WithSettings (handler.SystemSansSerifFont.Backend, size, style, weight, stretch);
  				else if (fname == oldHandler.SystemSerifFont.Family)
  					Backend = handler.WithSettings (handler.SystemSerifFont.Backend, size, style, weight, stretch);
  				else {
  					var fb = handler.Create (fname, size, style, weight, stretch);
  					Backend = fb ?? handler.WithSettings(handler.GetSystemDefaultFont (), size, style, weight, stretch);
  				}
  			}
  		}
  
  		/// <summary>
  		/// Register a font file with the system font manager that is then accessible through FromName.
  		/// The font is only available during the lifetime of the process.
  		/// </summary>
  		/// <returns><c>true</c>, if font from file was registered, <c>false</c> otherwise.</returns>
  		/// <param name="fontPath">Font path.</param>
  		public static bool RegisterFontFromFile (string fontPath)
  		{
  			return RegisterFontFromFile (fontPath, Toolkit.CurrentEngine);
  		}
  
  		static bool RegisterFontFromFile (string fontPath, Toolkit toolkit)
  		{
  			var handler = toolkit.FontBackendHandler;
  			var result = handler.RegisterFontFromFile (fontPath);
  			if (result) {
  				installedFonts = null;
  				installedFontsArray = null;
  			}
  			return result;
  		}
  
  		/// <summary>
  		/// Creates a new font description from a string representation in the form "[FAMILY-LIST] [STYLE-OPTIONS] [SIZE]"
  		/// </summary>
  		/// <returns>
  		/// The new font
  		/// </returns>
  		/// <param name='name'>
  		/// Font description
  		/// </param>
  		/// <remarks>
  		/// Creates a new font description from a string representation in the form "[FAMILY-LIST] [STYLE-OPTIONS] [SIZE]", 
  		/// where FAMILY-LIST is a comma separated list of families optionally terminated by a comma, STYLE_OPTIONS is a
  		/// whitespace separated list of words where each WORD describes one of style, weight or stretch, and SIZE is a
  		/// decimal number (size in points). Any one of the options may be absent. If FAMILY-LIST is absent, the default
  		/// font family will be used. If STYLE-OPTIONS is missing, then all style options will be set to the default values.
  		/// If SIZE is missing, the size in the resulting font description will be set to the default font size.
  		/// If the font doesn't exist, it returns the system font.
  		/// </remarks>
  		public static Font FromName (string name)
  		{
  			var toolkit = Toolkit.CurrentEngine;
  			return FromName (name, toolkit);
  		}
  
  		internal static Font FromName (string name, Toolkit toolkit)
  		{
  			if (name == null)
  				throw new ArgumentNullException (nameof (name), "Font name cannot be null");
  			if (name.Length == 0)
  				return toolkit.FontBackendHandler.SystemFont;
  			
  			var handler = toolkit.FontBackendHandler;
  
  			double size = -1;
  			FontStyle style = FontStyle.Normal;
  			FontWeight weight = FontWeight.Normal;
  			FontStretch stretch = FontStretch.Normal;
  
  			int i = name.LastIndexOf (' ');
  			int lasti = name.Length;
  			do {
  				if (lasti > 0 && IsFontSupported (name.Substring (0, lasti)))
  					break;
  				
  				string token = name.Substring (i + 1, lasti - i - 1);
  				FontStyle st;
  				FontWeight fw;
  				FontStretch fs;
  				double siz;
  				if (double.TryParse (token, NumberStyles.Any, CultureInfo.InvariantCulture, out siz)) { // Try parsing the number first, since Enum.TryParse can also parse numbers
  					if (size == -1) // take only first number
  						size = siz;
  				}
  				else if (Enum.TryParse<FontStyle> (token, true, out st) && st != FontStyle.Normal)
  					style = st;
  				else if (Enum.TryParse<FontWeight> (token, true, out fw) && fw != FontWeight.Normal)
  					weight = fw;
  				else if (Enum.TryParse<FontStretch> (token, true, out fs) && fs != FontStretch.Normal)
  					stretch = fs;
  				else if (token.Length > 0)
  					break;
  
  				lasti = i;
  				if (i <= 0)
  					break;
  
  				i = name.LastIndexOf (' ', i - 1);
  			} while (true);
  
  			string fname = lasti > 0 ? name.Substring (0, lasti) : string.Empty;
  			fname = fname.Length > 0 ? GetSupportedFont (fname) : Font.SystemFont.Family;
  
  			if (size == -1)
  				size = SystemFont.Size;
  
  			var fb = handler.Create (fname, size, style, weight, stretch);
  			if (fb != null)
  				return new Font (fb, toolkit);
  			else
  				return Font.SystemFont;
  		}
  
  		static bool IsFontSupported (string fontName)
  		{
  			if (fontName == null)
  				throw new ArgumentNullException(nameof (fontName), "Font family name not provided");
  			if (fontName == string.Empty)
  				return false;
  			
  			LoadInstalledFonts ();
  			
  			return installedFonts.ContainsKey (fontName.Trim ());
  		}
  
  		static string GetSupportedFont (string fontNames)
  		{
  			LoadInstalledFonts ();
  
  			int i = fontNames.IndexOf (',');
  			if (i == -1) {
  				var f = fontNames.Trim ();
  				string nf;
  				if (installedFonts.TryGetValue (f, out nf))
  					return nf;
  				else
  					return GetDefaultFont (f);
  			}
  
  			string[] names = fontNames.Split (new char[] {','}, StringSplitOptions.RemoveEmptyEntries);
  			if (names.Length == 0)
  				throw new ArgumentException ("Font family name not provided");
  
  			foreach (var name in names) {
  				var n = name.Trim ();
  				if (installedFonts.ContainsKey (n))
  					return n;
  			}
  			return GetDefaultFont (fontNames.Trim (' ',','));
  		}
  
  		static string GetDefaultFont (string unknownFont)
  		{
  			if (unknownFont != Font.SystemFont.Family) // ignore rare case when the default system font is not registered
  				Console.WriteLine ("Font '" + unknownFont + "' not available in the system. Using '" + Font.SystemFont.Family + "' instead");
  			return Font.SystemFont.Family;
  		}
  
  		static Dictionary<string,string> installedFonts;
  		static ReadOnlyCollection<string> installedFontsArray;
  
  
  		static void LoadInstalledFonts ()
  		{
  			if (installedFonts == null) {
  				installedFonts = new Dictionary<string,string> (StringComparer.OrdinalIgnoreCase);
  				foreach (var f in Toolkit.CurrentEngine.FontBackendHandler.GetInstalledFonts ())
  					installedFonts [f] = f;
  				installedFontsArray = new ReadOnlyCollection<string> (installedFonts.Values.ToArray ());
  				// add dummy font names for unit tests, the names are not exposed to users
  				// see the FontNameWith* tests (Testing/Tests/FontTests.cs) for details
  				installedFonts.Add("____FakeTestFont 72", "Arial");
  				installedFonts.Add ("____FakeTestFont Rounded MT Bold", "Arial");
  				// HACK: add font mapping for patched pango SF Font support
  				installedFonts ["-apple-system-font"] = ".AppleSystemUIFont";
  			}
  		}
  
  		public static ReadOnlyCollection<string> AvailableFontFamilies {
  			get {
  				LoadInstalledFonts ();
  				return installedFontsArray;
  			}
  		}
  
  		public static Font SystemFont {
  			get { return Toolkit.CurrentEngine.FontBackendHandler.SystemFont; }
  		}
  
  		public static Font SystemMonospaceFont {
  			get {
  				return Toolkit.CurrentEngine.FontBackendHandler.SystemMonospaceFont;
  			}
  		}
  
  		public static Font SystemSerifFont {
  			get {
  				return Toolkit.CurrentEngine.FontBackendHandler.SystemSerifFont;
  			}
  		}
  
  		public static Font SystemSansSerifFont {
  			get {
  				return Toolkit.CurrentEngine.FontBackendHandler.SystemSansSerifFont;
  			}
  		}
  
  		/// <summary>
  		/// Gets the available family/font variants with varying weight, style and stretch.
  		/// </summary>
  		/// <returns>All available font variants for a specific family/font.</returns>
  		/// <param name="fontFamily">A comma separated list of families</param>
  		/// <remarks>
  		/// Not all weights, styles or strech variants and combinations are available
  		/// for every font or family. In case of an invalid combination, most toolkits
  		/// fallback to a system default variant. GetAvailableFontFaces helps to retrieve
  		/// only valid combinations for a specific font family.
  		/// </remarks>
  		public static ReadOnlyCollection<FontFace> GetAvailableFontFaces (string fontFamily)
  		{
  			fontFamily = GetSupportedFont (fontFamily);
  			return new ReadOnlyCollection<FontFace>(Toolkit.CurrentEngine.FontBackendHandler.GetAvailableFamilyFaces(fontFamily).Select (f => new FontFace(f.Key, f.Value)).ToList ());
  		}
  
  		/// <summary>
  		/// Gets the available variants of the font with varying weight, style and stretch.
  		/// </summary>
  		/// <returns>All available font variants for a specific family/font.</returns>
  		/// <remarks>
  		/// Not all weights, styles or strech variants and combinations are available
  		/// for every font or family. In case of an invalid combination, most toolkits
  		/// fallback to a system default variant. GetAvailableFontFaces helps to retrieve
  		/// only valid combinations for a specific font family.
  		/// </remarks>
  		public ReadOnlyCollection<FontFace> GetAvailableFontFaces ()
  		{
  			return GetAvailableFontFaces (Family);
  		}
  
  		/// <summary>
  		/// Returns a copy of the font using the provided font family
  		/// </summary>
  		/// <returns>The new font</returns>
  		/// <param name="fontFamily">A comma separated list of families</param>
  		public Font WithFamily (string fontFamily)
  		{
  			fontFamily = GetSupportedFont (fontFamily);
  			return new Font (ToolkitEngine.FontBackendHandler.SetFamily (Backend, fontFamily), ToolkitEngine);
  		}
  		
  		public string Family {
  			get {
  
gt: 				return handler.GetFamily (Backend);