//--------------------------------------
//               PowerUI
//
//        For documentation or 
//    if you have any issues, visit
//        powerUI.kulestar.com
//
//    Copyright  2013 Kulestar Ltd
//          www.kulestar.com
//--------------------------------------

using System;
using System.Collections;
using System.Collections.Generic;
using InfiniText;


namespace PowerUI{

	/// <summary>
	/// Used for e.g. Emoji. Holds all CharacterProvider instances. When a character is not found in the font,
	/// PowerUI checks with these character providers to see if any of them can provide it instead.
	/// Add custom providers if you have your own set of images to add.
	/// </summary>

	public static class CharacterProviders{
		
		/// <summary>True when this has been Setup.</summary>
		private static bool Started;
		/// <summary>True if emoji should render at a fixed (original) height. Otherwise it will render at a best-guess fontsize height.</summary>
		public static bool FixHeight;
		/// <summary>The path where emoji chars are found. Any ordinary URL, but must be set before you call UI.Start.</summary>
		public static string EmojiPath="resources://Characters";
		/// <summary>The set of all available character providers.</summary>
		public static List<CharacterProvider> Providers;
		/// <summary>Cached loaded characters.</summary>
		public static Dictionary<int,Glyph> CachedCharacters;
		
		
		/// <summary>Adds the default character providers. Called by UI.Start.</summary>
		public static void Setup(){
			// Emoji ranges:
			
			if(Started){
				return;
			}
			
			Started=true;
			
			// The numbers represent the min and max Unicode charcode in decimal to map to an image.
			Add(new CharacterProvider(EmojiPath,8252,12953));
			Add(new CharacterProvider(EmojiPath,126980,128704));
		}
		
		
		
		/// <summary>Adds the given character provider to the set of Providers.</summary>
		/// <param name="provider">The provider to add.</param>
		public static void Add(CharacterProvider provider){
			if(Providers==null){
				Providers=new List<CharacterProvider>(1);
				Providers.Add(provider);
			}else{
				// Sorted set, so where should it go?
				
				// Insert at this index:
				int index=InsertIndex(provider);
				
				// Insert at the given index:
				Providers.Insert(index,provider);
			}
		}
		
		/// <summary>Finds the index where the given provider should be inserted at.</summary>
		/// <param name="provider">The provider to add.</param>
		/// <returns>The index to add it at.</returns>
		public static int InsertIndex(CharacterProvider provider){
			// Ignore that it's a range and just essentially sort by minimum ID.
			// Go up the set of all providers (it's only tiny, so this will be faster than bin search)
			// When you hit a minimum that is bigger than the new minimum, then that's the spot it should go at.
			
			int newMinimum=provider.MinimumID;
			int size=Providers.Count;
			
			for(int i=0;i<size;i++){
				
				// Is the new providers minimum bigger than the new minimum?
				if(Providers[i].MinimumID>=newMinimum){
					// Yes it is - it can go here.
					return i;
				}
				
			}
			
			// Must go at the end:
			return size;
		}
		
		/// <summary>Searches for the most suitable index for the given character code.</summary>
		/// <param name="code">The charcode to look for.</param>
		/// <returns>The index in provider that charcode can be found in.</returns>
		public static int SearchFor(int code){
			if(Providers==null){
				// No providers anyway.
				return -1;
			}
			
			int size=Providers.Count;
			
			// A straight search is faster than binary searching - it's only a small set.
			
			// For each provider..
			for(int i=0;i<size;i++){
				CharacterProvider provider=Providers[i];
				
				// Is the code within it's range?
				if(code>=provider.MinimumID && code<=provider.MaximumID){
					// Yes - right here.
					return i;
				}
				
			}
			
			// Not found.
			return -1;
		}
		
		/// <summary>Attempts to find the character provider for a character of the given charcode.</summary>
		/// <param name="code">The charcode of the character to look for.</param>
		/// <returns>A provider for the character, if found.</returns>
		public static CharacterProvider FindFor(int code){
			if(Providers==null){
				return null;
			}
			
			int index=SearchFor(code);
			
			if(index==-1){
				return null;
			}
			
			CharacterProvider provider=Providers[index];
			if(provider.MaximumID>=code){
				return provider;
			}
			
			return null;
		}
		
		/// <summary>Attempts to find and apply an image to the given character.</summary>
		/// <param name="character">The on screen character to attempt to find an image for.</param>
		public static Glyph Find(int charcode){
			
			Glyph glyph;
			if(CachedCharacters!=null && CachedCharacters.TryGetValue(charcode,out glyph)){
				
				return glyph;
				
			}
			
			CharacterProvider provider=FindFor(charcode);
			
			if(provider==null){
				return null;
			}
			
			// The provider is likely able to supply us with the character.
			// If it can't, we can just assume nobody else can.
			glyph=new Glyph();
			
			// Update the charcode:
			glyph.RawCharcode=charcode;
			
			// Load into the glyph now:
			provider.Load(glyph,charcode);
			
			// Cache it next:
			if(CachedCharacters==null){
				CachedCharacters=new Dictionary<int,Glyph>();
			}
			
			CachedCharacters[charcode]=glyph;
			
			return glyph;
			
		}
		
		/// <summary>Attempts to find and apply an image to the given character.</summary>
		/// <param name="character">The on screen character to attempt to find an image for.</param>
		public static void FindInto(Glyph loadInto,int charcode){
			
			CharacterProvider provider=FindFor(charcode);
			
			if(provider==null){
				return;
			}
			
			// The provider is likely able to supply us with the character.
			// If it does then we simply return it.
			
			// Load into the glyph now:
			provider.Load(loadInto,charcode);
			
		}
		
	}
	
}