using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Windows.Forms;
using System.Xml.Linq;
using CoverageFramework.AstGenerator;
using CoverageFramework.Operator.Selector;
using CoverageFramework.Model;
using CoverageFramework.Utility;
using Paraiba.Utility;
using Paraiba.Windows.Forms;
using Paraiba.Xml.Linq;

namespace ConditionSelectorGenerator
{
	public partial class Main : Form
	{
		public Main()
		{
			InitializeComponent();
		}

		private void Main_Load(object sender, EventArgs e)
		{
			foreach (var astGen in InstanceFactory.AstGenerators)
			{
				cbxAstGenerator.Items.Add(astGen.GetType().ToString());
			}

			foreach (var selector in InstanceFactory.XElementSelectors)
			{
				cbxSelector.Items.Add(selector.GetType().ToString());
				clbSelectors1.Items.Add(selector.GetType().ToString());
				clbSelectors2.Items.Add(selector.GetType().ToString());
			}

			foreach (var generator in InstanceFactory.XElementGenerators)
			{
				cbxStatementGenerator.Items.Add(generator.GetType().ToString());
			}
		}

		private void SelectText(RichTextBox richTextBox, CodePosition pos)
		{
			var text = richTextBox.Text;

			var startLineHeadPos = 0;
			var iLines = 1;
			for (; iLines < pos.StartLine; iLines++)
			{
				startLineHeadPos = text.IndexOf('\n', startLineHeadPos) + 1;
			}

			var endLineHeadPos = startLineHeadPos;
			for (; iLines < pos.EndLine; iLines++)
			{
				endLineHeadPos = text.IndexOf('\n', endLineHeadPos) + 1;
			}

			var start = startLineHeadPos + pos.StartPos;
			var end = endLineHeadPos + pos.EndPos;

			richTextBox.Select(start, end - start);
		}

		private void button1_Click(object sender, EventArgs e)
		{
			listBox1.Items.Clear();
			textBox2.Clear();

			var astGen = InstanceFactory.AstGenerators.ElementAt(cbxAstGenerator.SelectedIndex);
			var selector = InstanceFactory.XElementSelectors.ElementAt(cbxSelector.SelectedIndex);
			var ast = astGen.GenerateXml(new StringReader(richTextBox1.Text));
			var selectedElements = selector.Select(ast.Root);
			var nameSet = new HashSet<string>();
			var logicalTermName = txtLogicalTermName.Text;

			foreach (var root in selectedElements)
			{
				var targets = root.Descendants()
					.Where(elem => elem.Name.LocalName == logicalTermName)
					.Where(elem => elem.Descendants().All(elem2 => elem2.Name.LocalName != logicalTermName));

				// 他の項の要素を含まない項の要素のみを抽出
				// a == b && (a == c || a == d) => a == b, a == c, a == d
				var names = targets.Independents()
					// ルートノードまでに出現したノード名を列挙
					.SelectMany(elem => elem.ParentsWhile(root))
					.Select(elem => elem.Name.LocalName);

				foreach (var name in names)
				{
					nameSet.Add(name);
				}
			}

			foreach (var name in nameSet)
			{
				textBox2.AppendText("\"" + name + "\",\n");
			}

			var selectedElementAndPotisions = selectedElements.Select(elem =>
				Make.XTuple(elem, CodePositionUtil.CreateCodePosition(elem)));
			foreach (var elemAndPos in selectedElementAndPotisions)
			{
				listBox1.Items.Add("\"" + elemAndPos.Value1.Value + "\", " + elemAndPos.Value2);
				SelectText(richTextBox1, elemAndPos.Value2);
				richTextBox1.SelectionColor = Color.Red;
			}
		}

		private void richTextBox1_SelectionChanged(object sender, EventArgs e)
		{
			textBox1.Text = richTextBox1.SelectionStart + "," + richTextBox1.SelectionLength;
		}

		private void textBox2_KeyDown(object sender, KeyEventArgs e)
		{
			var textBox = (TextBox)sender;
			if (e.Control && e.KeyCode == Keys.A)
				textBox.SelectAll();
		}

		private void Main_DragEnter(object sender, DragEventArgs e)
		{
			e.Effect = DragDropEffects.All;
		}

		private void Main_DragDrop(object sender, DragEventArgs e)
		{
			if (e.Data.GetDataPresent(DataFormats.FileDrop))
			{
				var fileNames = (string[])e.Data.GetData(DataFormats.FileDrop);
				using (var fs = new FileStream(fileNames[0], FileMode.Open))
				using (var reader = new StreamReader(fs))
				{
					tbOriginal.Text = reader.ReadToEnd();
				}
			}
		}

		private void btProcess_Click(object sender, EventArgs e)
		{
			if (cbxAstGenerator.SelectedIndex < 0)
				return;

			var astGen = InstanceFactory.AstGenerators.ElementAt(cbxAstGenerator.SelectedIndex);
			var ast = astGen.GenerateXml(new StringReader(tbOriginal.Text));
			rtbAst.Text = ast.ToString();

			SetSelectorTabContent(ast);
			SetConditionSelectorTabContent(ast);
		}

		private IXElementSelector GetSelector()
		{
			IXElementSelector selector = null;

			if (clbSelectors1.CheckedIndices.Count > 0)
			{
				var selectors1 = clbSelectors1.CheckedIndices.AsEnumerableInt()
					.Select(index => InstanceFactory.XElementSelectors.ElementAt(index));
				selector = XElementSelectorUtil.Merge(selectors1);
			}

			if (clbSelectors2.CheckedIndices.Count > 0)
			{
				var selectors2 = clbSelectors2.CheckedIndices.AsEnumerableInt()
					.Select(index => InstanceFactory.XElementSelectors.ElementAt(index));
				selector = selector != null ?
					XElementSelectorUtil.Chain(selector, XElementSelectorUtil.Merge(selectors2)) :
					XElementSelectorUtil.Merge(selectors2);
			}

			return selector;
		}

		private void SetSelectorTabContent(XDocument ast)
		{
			lbSelectedElements.Items.Clear();
			rtbSelector.Text = tbOriginal.Text;

			var selector = GetSelector();
			if (selector == null)
				return;

			var selectedElements = selector.Select(ast.Root);
			var selectedElementAndPotisions = selectedElements.Select(elem =>
				Make.XTuple(elem, CodePositionUtil.CreateCodePosition(elem)));

			foreach (var elemAndPos in selectedElementAndPotisions)
			{
				lbSelectedElements.Items.Add("\"" + elemAndPos.Value1.Value + "\", " + elemAndPos.Value2);
				SelectText(rtbSelector, elemAndPos.Value2);
				rtbSelector.SelectionColor = Color.Red;
			}
		}

		private void SetConditionSelectorTabContent(XDocument ast)
		{
			if (cbxSelector.SelectedIndex < 0)
				return;

			listBox1.Items.Clear();
			textBox2.Clear();
			richTextBox1.Text = tbOriginal.Text;

			var selector = InstanceFactory.XElementSelectors.ElementAt(cbxSelector.SelectedIndex);
			var selectedElements = selector.Select(ast.Root);
			var nameSet = new HashSet<string>();
			var logicalTermName = txtLogicalTermName.Text;

			foreach (var root in selectedElements)
			{
				var targets = root.Descendants()
					.Where(elem => elem.Name.LocalName.EndsWith(logicalTermName))
					.Where(elem => elem.Descendants().All(elem2 => !elem2.Name.LocalName.EndsWith(logicalTermName)));

				// 他の項の要素を含まない項の要素のみを抽出
				// a == b && (a == c || a == d) => a == b, a == c, a == d
				var names = targets.Independents()
					// ルートノードまでに出現したノード名を列挙
					.SelectMany(elem => elem.ParentsWhile(root))
					.Select(elem => elem.Name.LocalName);

				foreach (var name in names)
				{
					nameSet.Add(name);
				}
			}

			foreach (var name in nameSet)
			{
				textBox2.AppendText("\"" + name + "\",\n");
			}

			var selectedElementAndPotisions = selectedElements.Select(elem =>
				Make.XTuple(elem, CodePositionUtil.CreateCodePosition(elem)));
			foreach (var elemAndPos in selectedElementAndPotisions)
			{
				listBox1.Items.Add("\"" + elemAndPos.Value1.Value + "\", " + elemAndPos.Value2);
				SelectText(richTextBox1, elemAndPos.Value2);
				richTextBox1.SelectionColor = Color.Red;
			}
		}

		private void SetStatemetTabContent(XDocument ast)
		{
			if (cbxStatementGenerator.SelectedIndex < 0)
				return;

			var selector = GetSelector();
			if (selector == null)
				return;
			

		}
	}
}
