using Cryville.Common.IO;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;

namespace Cryville.Common.Font {
	public abstract class FontFile : IEnumerable<Typeface> {
		public abstract int Count { get; }
		public abstract Typeface this[int index] { get; }
		protected FileInfo File { get; private set; }
		protected BinaryReader Reader { get; private set; }
		public FontFile(FileInfo file) {
			File = file;
			Reader = new BinaryReaderBE(new FileStream(file.FullName, FileMode.Open, FileAccess.Read));
		}
		public void Close() { Reader.Close(); }

		public static FontFile Create(FileInfo file) {
			switch (file.Extension) {
				case ".ttf": case ".otf": return new FontFileTTF(file);
				case ".ttc": case ".otc": return new FontFileTTC(file);
				default: return null;
			}
		}

		public Enumerator GetEnumerator() {
			return new Enumerator(this);
		}
		IEnumerator<Typeface> IEnumerable<Typeface>.GetEnumerator() {
			return GetEnumerator();
		}
		IEnumerator IEnumerable.GetEnumerator() {
			return GetEnumerator();
		}

		public struct Enumerator : IEnumerator<Typeface> {
			readonly FontFile _self;
			int _index;
			internal Enumerator(FontFile self) {
				_self = self;
				_index = -1;
			}

			public Typeface Current {
				get {
					if (_index < 0)
						throw new InvalidOperationException(_index == -1 ? "Enum not started" : "Enum ended");
					return _self[_index];
				}
			}

			object IEnumerator.Current { get { return Current; } }

			public void Dispose() {
				_index = -2;
			}

			public bool MoveNext() {
				if (_index == -2) return false;
				_index++;
				if (_index >= _self.Count) {
					_index = -2;
					return false;
				}
				return true;
			}

			public void Reset() {
				_index = -1;
			}
		}
	}
	public class FontFileTTF : FontFile {
		public override int Count { get { return 1; } }
		public override Typeface this[int index] {
			get {
				if (index != 0) throw new ArgumentOutOfRangeException("index");
				try {
					return new TypefaceTTF(Reader, File, index);
				}
				catch (Exception) {
					throw new InvalidDataException("Invalid font");
				}
			}
		}
		public FontFileTTF(FileInfo file) : base(file) { }
	}
	public class FontFileTTC : FontFile {
		readonly IReadOnlyList<uint> _offsets;
		public override int Count { get { return _offsets.Count; } }
		public override Typeface this[int index] {
			get {
				if (index < 0 || index >= Count) throw new ArgumentOutOfRangeException("index");
				Reader.BaseStream.Position = _offsets[index];
				try {
					return new TypefaceTTF(Reader, File, index);
				}
				catch (Exception) {
					throw new InvalidDataException("Invalid font");
				}
			}
		}
		public FontFileTTC(FileInfo file) : base(file) {
			try {
				_offsets = new TTCHeader(Reader, 0).GetItems();
			}
			catch (Exception) {
				throw new InvalidDataException("Invalid font");
			}
		}
	}
}
