using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Paraiba.Linq;
using Paraiba.Xml.Linq;

namespace CoverageFramework.Operator.Selector
{
	public abstract class AtomicBooleanTermSelector : IXElementRangeSelector
	{
		protected virtual bool IsInIfConditionExpression(XObject element, XElement parent)
		{
			return element.Parents()
				.TakeWhile(e_ => !ReferenceEquals(e_, parent))
				.All(IsConditionalExpressionComponent);
		}

		protected abstract bool IsBoolTermSepartor(XElement element);

		protected abstract bool IsConditionalExpression(XElement element);

		protected abstract bool IsConditionalExpressionComponent(XElement element);

		#region IXElementRangeSelector メンバ

		public IEnumerable<IEnumerable<XElement>> Select(XElement root)
		{
			// if文の条件式を構成する論理式を抽出
			var parent = root.Parent;
			var boolExprs = root
				.DescendantsAndSelf()
				.Where(IsConditionalExpression)
				.Where(e_ => IsInIfConditionExpression(e_, parent));

			// 論理式の項を抽出
			var boolTerms = boolExprs
				.SelectMany(e_ =>
					e_.Elements().SplitWithoutEmpty(IsBoolTermSepartor))
				.ToList();

			// 他の項の要素を含まない項の要素のみを抽出
			// a == b && (a == c || a == d) => a == b, a == c, a == d
			var atomicBoolTerms = boolTerms.Where(term_ =>
				!term_.Descendants().IsIntersect(boolTerms.PickUpFirst()))
				.ToList();

			// XML要素の位置でソーティング
			atomicBoolTerms.Sort((e1_, e2_) =>
				e1_[0].IsBefore(e2_[0]) ? -1 : 1);

			return atomicBoolTerms.Cast<IEnumerable<XElement>>();
		}

		#endregion
	}
}