using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Text.RegularExpressions;

namespace KeyConfig.Config
{
	internal class ConfigReader
	{
		//----------// //----------// //----------// //----------//
		//
		//	Constructor
		//
		//----------// //----------// //----------// //----------//

		internal ConfigReader(Stream input)
		{
			if (input == null) throw new ArgumentNullException("input");

			sr = new StreamReader(input, Encoding.GetEncoding("shift-jis"));

			stateReader.Add(SectionState.Key, ReadKey);
			stateReader.Add(SectionState.Pad, ReadPad);
			stateReader.Add(SectionState.Screen, ReadScreen);
			stateReader.Add(SectionState.Ignored, ReadIgnored);
		}

		//----------// //----------// //----------// //----------//
		//
		//	Fields
		//
		//----------// //----------// //----------// //----------//

		StreamReader sr;

		ConfigData config;
		SectionState state = SectionState.Ignored;
		Dictionary<SectionState, Action<string, string>> stateReader = new Dictionary<SectionState, Action<string, string>>();

		Regex rKeyValue = new Regex("(?<key>[^=].*)[=](?<code>.*)");

		//----------// //----------// //----------// //----------//
		//
		//	Methods
		//
		//----------// //----------// //----------// //----------//

		internal ConfigData Read()
		{
			config = new ConfigData();

			while (sr.Peek() != -1)
			{
				ReadLine(sr.ReadLine());
			}

			return config;
		}

		//----------// //----------// //----------// //----------//
		//
		//	Methods (private)
		//
		//----------// //----------// //----------// //----------//

		private void ReadLine(string line)
		{
			line = TrimComment(line);

			// コメント行の除去
			if (String.IsNullOrEmpty(line))
			{
				return;
			}

			// セクション名の読み取り
			var section = SectionFinder.Detect(line);

			if (section != null)
			{
				state = section ?? SectionState.Ignored;
				return;
			}

			//
			ReadSectionLine(line);
		}

		private void ReadSectionLine(string line)
		{
			var match = rKeyValue.Match(line);

			if (match != Match.Empty)
			{
				string key = match.Groups["key"].Value;
				string value = match.Groups["code"].Value;

				stateReader[state](key, value);
			}
		}

		private string TrimComment(string line)
		{
			int index = line.IndexOf('#');

			if (index == 0)
			{
				return null;
			}
			else if (index > 0)
			{
				return line.Substring(0, index);
			}
			else
			{
				return line;
			}
		}

		private void ReadKey(string key, string value)
		{
			config.KeyList.Add(new KeyValuePair<string, int>(key, Convert.ToInt32(value)));
		}

		private void ReadPad(string key, string value)
		{
			config.PadList.Add(new KeyValuePair<string, int>(key, Convert.ToInt32(value)));
		}

		private void ReadScreen(string key, string value)
		{
			if (key == "FULL")
			{
				config.FullScreen = value == "0" ? false : true;
			}
			else if (key == "TIMER")
			{
				config.SyncTimer = value == "0" ? false : true;
			}
			else if (key == "SKIP")
			{
				config.SkipFrame = Convert.ToInt32(value);
			}
		}

		private void ReadIgnored(string key, string value)
		{

		}

		//----------// //----------// //----------// //----------//
		//
		//	static
		//
		//----------// //----------// //----------// //----------//

		internal static ConfigData ReadFrom(string path)
		{
			if (!File.Exists(path))
			{
				throw new FileNotFoundException();
			}
			using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
			{
				return new ConfigReader(fs).Read();
			}
		}

		private enum SectionState
		{
			Key,
			Pad,
			Screen,
			Ignored
		}

		private class SectionFinder
		{
			static Regex rKey = new Regex("\\[KEYCONFIG\\]");
			static Regex rPad = new Regex("\\[PADCONFIG\\]");
			static Regex rScreen = new Regex("\\[SCREEN\\]");
			static Regex rIgnored = new Regex("\\[.*\\]");

			internal static SectionState? Detect(string line)
			{
				if (rKey.IsMatch(line))
				{
					return SectionState.Key;
				}
				else if (rPad.IsMatch(line))
				{
					return SectionState.Pad;
				}
				else if (rScreen.IsMatch(line))
				{
					return SectionState.Screen;
				}
				else if (rIgnored.IsMatch(line))
				{
					return SectionState.Ignored;
				}
				else
				{
					return null;
				}
			}
		}
	}
}