<commit_before>﻿﻿//
// Copyright (c) 2017 <NAME>
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Xamarin.Forms;
// ReSharper disable CompareOfFloatsByEqualityOperator

namespace EasyLayout.Forms
{
    public static class EasyLayoutForms
    {
        private class Assertion
        {
            private double _relativeSizeDelta = 0;

            public Assertion(View view)
            {
                View = view;
            }

            public View View { get; }
            private LeftExpression LeftExpression { get; set; }
            public RightExpression RightExpression { get; private set; }
            public string LeftName => LeftExpression.Name;

            public void Initialize(LeftExpression leftExpression, RightExpression rightExpression)
            {
                LeftExpression = leftExpression;
                RightExpression = rightExpression;
            }

            public Constraint GetSizeConstraint(Position position, List<View> dependentViews = null) {
				if (RightExpression.IsConstant && RightExpression.Constant.HasValue)
				{
					return Constraint.Constant(RightExpression.Constant.Value);
				}
                
                if (dependentViews != null)
                {   
                    if (position == Position.Width)
                    {
                        return Constraint.RelativeToParent(rl =>
                            rl.Width - dependentViews.Aggregate(0.0, (d, view) => d + GetWidth(rl, view.Id, false, 0)));
                    }
                    if (position == Position.Height)
                        return Constraint.RelativeToParent(rl =>
                            rl.Height - dependentViews.Aggregate(0.0, (d, view) => d + GetHeight(rl, view.Id, 0)));
                }
                else
                {
                    var sibling = RightExpression.View;
                    var margin = GetMargin();

                    var isParent = RightExpression.IsParent;
                    if (isParent && position == Position.Width)
                        return Constraint.RelativeToParent(rl => rl.Width + margin);
                    if (isParent && position == Position.Height)
                        return Constraint.RelativeToParent(rl => rl.Height + margin);
                    if (position == Position.Width)
                        return Constraint.RelativeToView(sibling, (rv, v) => v.Width);
                    if (position == Position.Height)
                        return Constraint.RelativeToView(sibling, (rv, v) => v.Height);                    
                }
                throw new ArgumentException("Unsupported position in size constraint: " + position);
			}

            public Constraint GetPositionConstraint(Assertion widthHeightAssertion = null)
            {
                if (RightExpression.IsParent)
                {
                    return GetLayoutRuleForParent(widthHeightAssertion);
                }
                return GetLayoutRuleForSibling(widthHeightAssertion);
            }

            private static double GetWidth(RelativeLayout relativeLayout, Guid id, bool isSizeRelativeToParent, double? widthAssertion)
            {
                if (isSizeRelativeToParent) return relativeLayout.Width + (widthAssertion ?? 0);
                if (widthAssertion.HasValue && widthAssertion > 0) return widthAssertion.Value;
                var child = GetChildById(relativeLayout, id);
                if (child == null) return 0;
                // -1 = width hasn't been computed yet so we have to calculate it
                if (child.Width != -1) return child.Width;
                return GetSize(relativeLayout, child).Width;
            }

            private static double GetHeight(RelativeLayout relativeLayout, Guid id, double? heightAssertion)
            {
                if (heightAssertion.HasValue && heightAssertion > 0) return heightAssertion.Value;
                var child = GetChildById(relativeLayout, id);
                if (child == null) return 0;
                // -1 = height hasn't been computed yet so we have to calculate it
                if (child.Height != -1) return child.Height;
                return GetSize(relativeLayout, child).Height;
            }

            /// <summary>
            /// Thought getting the height/width of an element in a constraint would be easy?  Hah.
            /// http://stackoverflow.com/questions/40942691/xamarin-forms-how-to-center-views-using-relative-layout-width-and-height-r
            /// </summary>
            private static Size GetSize(RelativeLayout relativeLayout, View visualElement)
            {
                if (visualElement == null) return Size.Zero;
                if (relativeLayout == null) return Size.Zero;
                return visualElement.Measure(relativeLayout.Width, relativeLayout.Height).Request;
            }

            private static View GetChildById(RelativeLayout relativeLayout, Guid id) => relativeLayout.Children.FirstOrDefault(i => i.Id == id);

            private Constraint GetLayoutRuleForParent(Assertion sizeAssertion)
            {
                Position childPosition = LeftExpression.Position;
                Position parentPosition = RightExpression.Position;
                string childName = LeftExpression.Name;
                Guid childId = LeftExpression.View.Id;
                var margin = GetMargin();

                var sizeConstant = sizeAssertion?.RightExpression.Constant;
                var isSizeRelativeToParent = sizeAssertion?.RightExpression.IsParent ?? false;

                // X Constraints

                // aka LayoutRules.AlignParentLeft
                if (childPosition == Position.Left && parentPosition == Position.Left)
                    return Constraint.RelativeToParent(rl => rl.X + margin);

                // aka LayoutRules.AlignParentRight
                if (childPosition == Position.Right && parentPosition == Position.Right)
                    return Constraint.RelativeToParent(rl => rl.Width - GetWidth(rl, childId, isSizeRelativeToParent, sizeConstant) + margin);

                // aka LayoutRules.CenterHorizontal
                if (childPosition == Position.CenterX && parentPosition == Position.CenterX)
                    return Constraint.RelativeToParent(rl => (rl.Width * .5f) - (GetWidth(rl, childId, isSizeRelativeToParent, sizeConstant) * .5f) + margin);

                // Y Constraints

                // aka LayoutRules.AlignParentTop
                if (childPosition == Position.Top && parentPosition == Position.Top)
                    return Constraint.RelativeToParent(rl => rl.Y + margin);

                // aka LayoutRules.AlignParentBottom
                if (childPosition == Position.Bottom && parentPosition == Position.Bottom)
                    return Constraint.RelativeToParent(rl => rl.Height - GetHeight(rl, childId, sizeConstant) + margin);

                // aka LayoutRules.CenterVertical
                if (childPosition == Position.CenterY && parentPosition == Position.CenterY)
                    return Constraint.RelativeToParent(rl => (rl.Height * .5f) - (GetHeight(rl, childId, sizeConstant) * .5f) + margin);

                // todo: support more parent layout constraints
                // aka LayoutRules.CenterInParent
                //if (childPosition == Position.Center && parentPosition == Position.Center)
                //    return LayoutRules.CenterInParent;
                //if (childPosition == Position.Width && parentPosition == Position.Width)
                //    return null;
                //if (childPosition == Position.Height && parentPosition == Position.Height)
                //    return null;
                throw new ArgumentException($"Unsupported parent positioning combination: {childName}.{childPosition} with parent.{parentPosition}");
            }

            private Constraint GetLayoutRuleForSibling(Assertion widthHeightAssertion)
            {
                Position leftPosition = LeftExpression.Position;
                Position rightPosition = RightExpression.Position;
                string leftExpressionName = LeftExpression.Name;
                string rightExpressionName = RightExpression.Name;
                View sibling = RightExpression.View;
                var margin = GetMargin();
                var childId = LeftExpression.View.Id;

                var heightWidthConstant = widthHeightAssertion?.RightExpression.Constant;

                // X Constraints

                // aka LayoutRules.AlignLeft
                if (leftPosition == Position.Left && rightPosition == Position.Left)
                    return Constraint.RelativeToView(sibling, (rl, v) => v.X + margin);

                // aka LayoutRules.AlignRight
                if (leftPosition == Position.Right && rightPosition == Position.Right)
                    return Constraint.RelativeToView(sibling, (rl, v) => v.Bounds.Right - GetWidth(rl, childId, false, heightWidthConstant) + margin);

                // aka LayoutRules.RightOf
                if (leftPosition == Position.Left && rightPosition == Position.Right)
                    return Constraint.RelativeToView(sibling, (rl, v) => v.Bounds.Right + margin);

                // aka LayoutRules.LeftOf
                if (leftPosition == Position.Right && rightPosition == Position.Left)
                    return Constraint.RelativeToView(sibling, (rl, v) => v.Bounds.Left - GetWidth(rl, childId, false, heightWidthConstant) + margin);

                // Y Constraints

                // aka LayoutRules.AlignTop
                if (leftPosition == Position.Top && rightPosition == Position.Top)
                    return Constraint.RelativeToView(sibling, (rl, v) => v.Y + margin);

                // aka LayoutRules.AlignBottom
                if (leftPosition == Position.Bottom && rightPosition == Position.Bottom)
                    return Constraint.RelativeToView(sibling, (rl, v) => v.Bounds.Bottom - GetHeight(rl, childId, heightWidthConstant) + margin);
                
                // aka LayoutRules.Below
                if (leftPosition == Position.Top && rightPosition == Position.Bottom)
                    return Constraint.RelativeToView(sibling, (rl, v) => v.Bounds.Bottom + margin);

                // aka LayoutRules.Above
                if (leftPosition == Position.Bottom && rightPosition == Position.Top)
                    return Constraint.RelativeToView(sibling, (rl, v) => v.Bounds.Top - GetHeight(rl, childId, heightWidthConstant) + margin);

                if (leftPosition == Position.CenterX && rightPosition == Position.CenterX)
                    return Constraint.RelativeToView(sibling, (rl, v) => v.X  + (v.Bounds.Width * .5f) - (GetWidth(rl, childId, false, heightWidthConstant) * .5f) + margin);

                // todo: constrain width's & height's
                //if (leftPosition == Position.Width && rightPosition == Position.Width)
                //    throw new ArgumentException("Unfortunatly Android's relative layout isn't sophisticated enough to allow constraining widths.  You might be able to achieve the same result by constraining Left's and Right's.");
                //if (leftPosition == Position.Height && rightPosition == Position.Height)
                //    throw new ArgumentException("Unfortunatly Android's relative layout isn't sophisticated enough to allow constraining heights.  You might be able to achieve the same result by constraining Top's and Bottom's.");
                throw new ArgumentException($"Unsupported relative positioning combination: {leftExpressionName}.{leftPosition} with {rightExpressionName}.{rightPosition}");
            }

            private double GetMargin()
            {
                if (RightExpression.Constant == null) return 0 - _relativeSizeDelta;
                var value = RightExpression.Constant.Value;

                switch (LeftExpression.Position)
                {
                    case Position.Top:
                    case Position.Left:
                    case Position.Right:
                    case Position.Bottom:
                    case Position.Width:
                    case Position.Height:
                    case Position.CenterX:
                        return value - _relativeSizeDelta;
                    default:
                        throw new ArgumentException($"Constant expressions with {RightExpression.Position} are currently unsupported.");
                }
            }

            public bool IsExplicitWidthAssertion()
            {
                return LeftExpression.Position == Position.Width;
            }

            public bool IsExplicitHeightAssertion()
            {
                return LeftExpression.Position == Position.Height;
            }

            public bool IsXConstraint()
            {
                return LeftExpression.Position == Position.Left ||
                       LeftExpression.Position == Position.Right ||
                       LeftExpression.Position == Position.CenterX;
            }

            public bool IsYConstraint()
            {
                return LeftExpression.Position == Position.Top ||
                       LeftExpression.Position == Position.Bottom ||
                       LeftExpression.Position == Position.CenterY;
            }

            public bool IsTopOrLeft()
            {
                if (LeftExpression.Position == Position.Top) return true;
                if (LeftExpression.Position == Position.Left) return true;
                return false;
            }

            public Position Position => LeftExpression.Position;

            public void UpdateMarginFromRelativeWidthConstraint(Assertion leftOrTopAssertion)
            {
                _relativeSizeDelta = leftOrTopAssertion?.RightExpression.Constant ?? 0;
            }
        }

        private enum Position
        {
            Top,
            Right,
            Left,
            Bottom,
            CenterX,
            CenterY,
            Center,
            Constant,
            Width,
            Height
        }

        private struct LeftExpression
        {
            public View View { get; set; }
            public Position Position { get; set; }
            public string Name { get; set; }
        }

        private struct RightExpression
        {
            public bool IsParent { get; set; }
            public View View { get; set; }
            public string Name { get; set; }
            public Position Position { get; set; }
            public double? Constant { get; set; }
            public bool IsConstant => !IsParent && View == null && Constant != null;
        }

        public static double GetCenterX(this Rectangle rectangle)
        {
            return rectangle.Left + (rectangle.Width / 2);
        }

        public static double GetCenterY(this Rectangle rectangle)
        {
            return rectangle.Top + (rectangle.Height / 2);
        }

        public static int ToConst(this int i)
        {
            return i;
        }

        private static int ToConst(this float i)
        {
            return (int)i;
        }

        public static void ConstrainLayout(this RelativeLayout relativeLayout, Expression<Func<bool>> constraints)
        {
            var constraintExpressions = FindConstraints(constraints.Body);
            var viewAndRule = ConvertConstraintsToRules(relativeLayout, constraintExpressions);
            UpdateLayoutParamsWithRules(relativeLayout, viewAndRule);
        }

        private static void UpdateLayoutParamsWithRules(RelativeLayout relativeLayout, IEnumerable<Assertion> viewAndRule)
        {
            var assertions = viewAndRule.ToList();
            var assertionsGroupedByView = assertions.GroupBy(i => i.View);

            foreach (var viewAndRules in assertionsGroupedByView)
            {
                var view = viewAndRules.Key;

                var widthAssertion = GetExplicitWidthConstraint(viewAndRules);
                var heightAssertion = GetExplicitHeightConstraint(viewAndRules);
				var xConstraint = GetXConstraint(viewAndRules, widthAssertion);
				var yConstraint = GetYConstraint(viewAndRules, heightAssertion);
                widthAssertion = widthAssertion ?? GetRelativeWidthConstraint(viewAndRules);
                heightAssertion = heightAssertion ?? GetRelativeHeightConstraint(viewAndRules);
				var widthConstraint =
				    widthAssertion?.GetSizeConstraint(Position.Width, GetDependentViews(view, assertions, Position.Left));
                var heightConstraint =
                    heightAssertion?.GetSizeConstraint(Position.Height, GetDependentViews(view, assertions, Position.Top));

                relativeLayout.Children.Add(view, xConstraint, yConstraint, widthConstraint, heightConstraint);
            }
        }

        private static List<View> GetDependentViews(View view, List<Assertion> allAssertions, Position position)
        {
            /*
            First we must check if the view is fully constrained.
            If a view has both a top and bottom constraint it is fully constrained.
            Or if a view has both a left and right constraint it is also fully constrained.
            */
            var oppositePosition = position == Position.Top ? Position.Bottom : Position.Right;
            var viewAssertions = allAssertions.Where(assertion => assertion.View.Equals(view)).ToList();
            var fullyConstrained =
                viewAssertions.Any(assertion => assertion.Position == position)
                && viewAssertions.Any(assertion => assertion.Position == oppositePosition);
            if (!fullyConstrained) return null;

            // Get all dependent views in either direction/position (top and bottom) or (left and right)
            var dependentViews = new List<View>();
            GetDependentViewsRecursively(view, allAssertions, position, dependentViews);
            GetDependentViewsRecursively(view, allAssertions, oppositePosition, dependentViews);

            return !dependentViews.Any() ? null : dependentViews;
        }

        private static void GetDependentViewsRecursively(View view, List<Assertion> allAssertions, Position position, List<View> result)
        {
            var dependentViews = from assertion in allAssertions
                where assertion.View.Equals(view)
                      && assertion.Position == position
                      && !assertion.RightExpression.IsParent
                select assertion.RightExpression.View;

            foreach (var dependentView in dependentViews)
            {
                result.Add(dependentView);
                GetDependentViewsRecursively(dependentView, allAssertions, position, result);
            }
        }

        private static Constraint GetXConstraint(IGrouping<View, Assertion> assertions, Assertion widthAssertion)
        {
            return GetXorYConstraint(assertions, widthAssertion, "X", i => i.IsXConstraint());
        }


        private static Constraint GetXorYConstraint(IGrouping<View, Assertion> assertions, 
                                                    Assertion sizeAssertion, string xorY,
                                                    Func<Assertion, bool> positionConstraintFunc)
        {
            var xAssertions = assertions.Where(positionConstraintFunc).ToList();
            if (xAssertions.Count == 0) return null;
            if (xAssertions.Count == 1)
            {
                return xAssertions[0].GetPositionConstraint(sizeAssertion);
            }<commit_msg>Bug- and performancefixes<commit_after>﻿﻿//
// Copyright (c) 2017 <NAME>
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Xamarin.Forms;
// ReSharper disable CompareOfFloatsByEqualityOperator

namespace EasyLayout.Forms
{
    public static class EasyLayoutForms
    {
        private class Assertion
        {
            private double _relativeSizeDelta = 0;

            public Assertion(View view)
            {
                View = view;
            }

            public View View { get; }
            private LeftExpression LeftExpression { get; set; }
            public RightExpression RightExpression { get; private set; }
            public string LeftName => LeftExpression.Name;

            public void Initialize(LeftExpression leftExpression, RightExpression rightExpression)
            {
                LeftExpression = leftExpression;
                RightExpression = rightExpression;
            }

            public Constraint GetSizeConstraint(Position position, List<View> dependentViews = null) {
				if (RightExpression.IsConstant && RightExpression.Constant.HasValue)
				{
					return Constraint.Constant(RightExpression.Constant.Value);
				}
                
                if (dependentViews != null && dependentViews.Count == 2)
                {
                    var v1 = dependentViews.ElementAt(0);
                    var v2 = dependentViews.ElementAt(1);
                    if (position == Position.Width)
                        return Constraint.RelativeToParent(rl => v2.Bounds.X - v2.Width - v1.Bounds.X);
                    if (position == Position.Height)
                        return Constraint.RelativeToParent(rl => v2.Bounds.Y - v2.Height - v1.Bounds.Y);
                }
                else
                {
                    var sibling = RightExpression.View;
                    var margin = GetMargin();

                    var isParent = RightExpression.IsParent;
                    if (isParent && position == Position.Width)
                        return Constraint.RelativeToParent(rl => rl.Width + margin);
                    if (isParent && position == Position.Height)
                        return Constraint.RelativeToParent(rl => rl.Height + margin);
                    if (position == Position.Width)
                        return Constraint.RelativeToView(sibling, (rv, v) => v.Width);
                    if (position == Position.Height)
                        return Constraint.RelativeToView(sibling, (rv, v) => v.Height);                    
                }
                throw new ArgumentException("Unsupported position in size constraint: " + position);
			}

            public Constraint GetPositionConstraint(Assertion widthHeightAssertion = null)
            {
                if (RightExpression.IsParent)
                {
                    return GetLayoutRuleForParent(widthHeightAssertion);
                }
                return GetLayoutRuleForSibling(widthHeightAssertion);
            }

            private static double GetWidth(RelativeLayout relativeLayout, Guid id, bool isSizeRelativeToParent, double? widthAssertion)
            {
                if (isSizeRelativeToParent) return relativeLayout.Width + (widthAssertion ?? 0);
                if (widthAssertion.HasValue && widthAssertion > 0) return widthAssertion.Value;
                var child = GetChildById(relativeLayout, id);
                if (child == null) return 0;
                // -1 = width hasn't been computed yet so we have to calculate it
                if (child.Width != -1) return child.Width;
                return GetSize(relativeLayout, child).Width;
            }

            private static double GetHeight(RelativeLayout relativeLayout, Guid id, double? heightAssertion)
            {
                if (heightAssertion.HasValue && heightAssertion > 0) return heightAssertion.Value;
                var child = GetChildById(relativeLayout, id);
                if (child == null) return 0;
                // -1 = height hasn't been computed yet so we have to calculate it
                if (child.Height != -1) return child.Height;
                return GetSize(relativeLayout, child).Height;
            }

            /// <summary>
            /// Thought getting the height/width of an element in a constraint would be easy?  Hah.
            /// http://stackoverflow.com/questions/40942691/xamarin-forms-how-to-center-views-using-relative-layout-width-and-height-r
            /// </summary>
            private static Size GetSize(RelativeLayout relativeLayout, View visualElement)
            {
                if (visualElement == null) return Size.Zero;
                if (relativeLayout == null) return Size.Zero;
                return visualElement.Measure(relativeLayout.Width, relativeLayout.Height).Request;
            }

            private static View GetChildById(RelativeLayout relativeLayout, Guid id) => relativeLayout.Children.FirstOrDefault(i => i.Id == id);

            private Constraint GetLayoutRuleForParent(Assertion sizeAssertion)
            {
                Position childPosition = LeftExpression.Position;
                Position parentPosition = RightExpression.Position;
                string childName = LeftExpression.Name;
                Guid childId = LeftExpression.View.Id;
                var margin = GetMargin();

                var sizeConstant = sizeAssertion?.RightExpression.Constant;
                var isSizeRelativeToParent = sizeAssertion?.RightExpression.IsParent ?? false;

                // X Constraints

                // aka LayoutRules.AlignParentLeft
                if (childPosition == Position.Left && parentPosition == Position.Left)
                    return Constraint.RelativeToParent(rl => rl.X + margin);

                // aka LayoutRules.AlignParentRight
                if (childPosition == Position.Right && parentPosition == Position.Right)
                    return Constraint.RelativeToParent(rl => rl.Width - GetWidth(rl, childId, isSizeRelativeToParent, sizeConstant) + margin);

                // aka LayoutRules.CenterHorizontal
                if (childPosition == Position.CenterX && parentPosition == Position.CenterX)
                    return Constraint.RelativeToParent(rl => (rl.Width * .5f) - (GetWidth(rl, childId, isSizeRelativeToParent, sizeConstant) * .5f) + margin);

                // Y Constraints

                // aka LayoutRules.AlignParentTop
                if (childPosition == Position.Top && parentPosition == Position.Top)
                    return Constraint.RelativeToParent(rl => rl.Y + margin);

                // aka LayoutRules.AlignParentBottom
                if (childPosition == Position.Bottom && parentPosition == Position.Bottom)
                    return Constraint.RelativeToParent(rl => rl.Height - GetHeight(rl, childId, sizeConstant) + margin);

                // aka LayoutRules.CenterVertical
                if (childPosition == Position.CenterY && parentPosition == Position.CenterY)
                    return Constraint.RelativeToParent(rl => (rl.Height * .5f) - (GetHeight(rl, childId, sizeConstant) * .5f) + margin);

                // todo: support more parent layout constraints
                // aka LayoutRules.CenterInParent
                //if (childPosition == Position.Center && parentPosition == Position.Center)
                //    return LayoutRules.CenterInParent;
                //if (childPosition == Position.Width && parentPosition == Position.Width)
                //    return null;
                //if (childPosition == Position.Height && parentPosition == Position.Height)
                //    return null;
                throw new ArgumentException($"Unsupported parent positioning combination: {childName}.{childPosition} with parent.{parentPosition}");
            }

            private Constraint GetLayoutRuleForSibling(Assertion widthHeightAssertion)
            {
                Position leftPosition = LeftExpression.Position;
                Position rightPosition = RightExpression.Position;
                string leftExpressionName = LeftExpression.Name;
                string rightExpressionName = RightExpression.Name;
                View sibling = RightExpression.View;
                var margin = GetMargin();
                var childId = LeftExpression.View.Id;

                var heightWidthConstant = widthHeightAssertion?.RightExpression.Constant;

                // X Constraints

                // aka LayoutRules.AlignLeft
                if (leftPosition == Position.Left && rightPosition == Position.Left)
                    return Constraint.RelativeToView(sibling, (rl, v) => v.X + margin);

                // aka LayoutRules.AlignRight
                if (leftPosition == Position.Right && rightPosition == Position.Right)
                    return Constraint.RelativeToView(sibling, (rl, v) => v.Bounds.Right - GetWidth(rl, childId, false, heightWidthConstant) + margin);

                // aka LayoutRules.RightOf
                if (leftPosition == Position.Left && rightPosition == Position.Right)
                    return Constraint.RelativeToView(sibling, (rl, v) => v.Bounds.Right + margin);

                // aka LayoutRules.LeftOf
                if (leftPosition == Position.Right && rightPosition == Position.Left)
                    return Constraint.RelativeToView(sibling, (rl, v) => v.Bounds.Left - GetWidth(rl, childId, false, heightWidthConstant) + margin);

                // Y Constraints

                // aka LayoutRules.AlignTop
                if (leftPosition == Position.Top && rightPosition == Position.Top)
                    return Constraint.RelativeToView(sibling, (rl, v) => v.Y + margin);

                // aka LayoutRules.AlignBottom
                if (leftPosition == Position.Bottom && rightPosition == Position.Bottom)
                    return Constraint.RelativeToView(sibling, (rl, v) => v.Bounds.Bottom - GetHeight(rl, childId, heightWidthConstant) + margin);
                
                // aka LayoutRules.Below
                if (leftPosition == Position.Top && rightPosition == Position.Bottom)
                    return Constraint.RelativeToView(sibling, (rl, v) => v.Bounds.Bottom + margin);

                // aka LayoutRules.Above
                if (leftPosition == Position.Bottom && rightPosition == Position.Top)
                    return Constraint.RelativeToView(sibling, (rl, v) => v.Bounds.Top - GetHeight(rl, childId, heightWidthConstant) + margin);

                if (leftPosition == Position.CenterX && rightPosition == Position.CenterX)
                    return Constraint.RelativeToView(sibling, (rl, v) => v.X  + (v.Bounds.Width * .5f) - (GetWidth(rl, childId, false, heightWidthConstant) * .5f) + margin);

                // todo: constrain width's & height's
                //if (leftPosition == Position.Width && rightPosition == Position.Width)
                //    throw new ArgumentException("Unfortunatly Android's relative layout isn't sophisticated enough to allow constraining widths.  You might be able to achieve the same result by constraining Left's and Right's.");
                //if (leftPosition == Position.Height && rightPosition == Position.Height)
                //    throw new ArgumentException("Unfortunatly Android's relative layout isn't sophisticated enough to allow constraining heights.  You might be able to achieve the same result by constraining Top's and Bottom's.");
                throw new ArgumentException($"Unsupported relative positioning combination: {leftExpressionName}.{leftPosition} with {rightExpressionName}.{rightPosition}");
            }

            private double GetMargin()
            {
                if (RightExpression.Constant == null) return 0 - _relativeSizeDelta;
                var value = RightExpression.Constant.Value;

                switch (LeftExpression.Position)
                {
                    case Position.Top:
                    case Position.Left:
                    case Position.Right:
                    case Position.Bottom:
                    case Position.Width:
                    case Position.Height:
                    case Position.CenterX:
                        return value - _relativeSizeDelta;
                    default:
                        throw new ArgumentException($"Constant expressions with {RightExpression.Position} are currently unsupported.");
                }
            }

            public bool IsExplicitWidthAssertion()
            {
                return LeftExpression.Position == Position.Width;
            }

            public bool IsExplicitHeightAssertion()
            {
                return LeftExpression.Position == Position.Height;
            }

            public bool IsXConstraint()
            {
                return LeftExpression.Position == Position.Left ||
                       LeftExpression.Position == Position.Right ||
                       LeftExpression.Position == Position.CenterX;
            }

            public bool IsYConstraint()
            {
                return LeftExpression.Position == Position.Top ||
                       LeftExpression.Position == Position.Bottom ||
                       LeftExpression.Position == Position.CenterY;
            }

            public bool IsTopOrLeft()
            {
                if (LeftExpression.Position == Position.Top) return true;
                if (LeftExpression.Position == Position.Left) return true;
                return false;
            }

            public Position Position => LeftExpression.Position;

            public void UpdateMarginFromRelativeWidthConstraint(Assertion leftOrTopAssertion)
            {
                _relativeSizeDelta = leftOrTopAssertion?.RightExpression.Constant ?? 0;
            }
        }

        private enum Position
        {
            Top,
            Right,
            Left,
            Bottom,
            CenterX,
            CenterY,
            Center,
            Constant,
            Width,
            Height
        }

        private struct LeftExpression
        {
            public View View { get; set; }
            public Position Position { get; set; }
            public string Name { get; set; }
        }

        private struct RightExpression
        {
            public bool IsParent { get; set; }
            public View View { get; set; }
            public string Name { get; set; }
            public Position Position { get; set; }
            public double? Constant { get; set; }
            public bool IsConstant => !IsParent && View == null && Constant != null;
        }

        public static double GetCenterX(this Rectangle rectangle)
        {
            return rectangle.Left + (rectangle.Width / 2);
        }

        public static double GetCenterY(this Rectangle rectangle)
        {
            return rectangle.Top + (rectangle.Height / 2);
        }

        public static int ToConst(this int i)
        {
            return i;
        }

        private static int ToConst(this float i)
        {
            return (int)i;
        }

        public static void ConstrainLayout(this RelativeLayout relativeLayout, Expression<Func<bool>> constraints)
        {
            var constraintExpressions = FindConstraints(constraints.Body);
            var viewAndRule = ConvertConstraintsToRules(relativeLayout, constraintExpressions);
            UpdateLayoutParamsWithRules(relativeLayout, viewAndRule);
        }

        private static void UpdateLayoutParamsWithRules(RelativeLayout relativeLayout, IEnumerable<Assertion> viewAndRule)
        {
            var assertions = viewAndRule.ToList();
            var assertionsGroupedByView = assertions.GroupBy(i => i.View);

            foreach (var viewAndRules in assertionsGroupedByView)
            {
                var view = viewAndRules.Key;

                var widthAssertion = GetExplicitWidthConstraint(viewAndRules);
                var heightAssertion = GetExplicitHeightConstraint(viewAndRules);
				var xConstraint = GetXConstraint(viewAndRules, widthAssertion);
				var yConstraint = GetYConstraint(viewAndRules, heightAssertion);
                widthAssertion = widthAssertion ?? GetRelativeWidthConstraint(viewAndRules);
                heightAssertion = heightAssertion ?? GetRelativeHeightConstraint(viewAndRules);
				var widthConstraint =
				    widthAssertion?.GetSizeConstraint(Position.Width, GetDependentViews(view, assertions, Position.Left));
                var heightConstraint =
                    heightAssertion?.GetSizeConstraint(Position.Height, GetDependentViews(view, assertions, Position.Top));

                relativeLayout.Children.Add(view, xConstraint, yConstraint, widthConstraint, heightConstraint);
            }
        }

        private static List<View> GetDependentViews(View view, List<Assertion> allAssertions, Position position)
        {
            /*
            First we must check if the view is fully constrained.
            If a view has both a top and bottom constraint it is fully constrained.
            Or if a view has both a left and right constraint it is also fully constrained.
            */
            var oppositePosition = position == Position.Top ? Position.Bottom : Position.Right;
            var viewAssertions = allAssertions.Where(assertion => assertion.View.Equals(view)).ToList();
            var fullyConstrained =
                viewAssertions.Any(assertion => assertion.Position == position)
                && viewAssertions.Any(assertion => assertion.Position == oppositePosition);
            if (!fullyConstrained) return null;

            // Get all dependent views in either direction/position (top and bottom) or (left and right)
            var dependentViews = (
                from assertion in allAssertions
                where assertion.View.Equals(view)
                      && (assertion.Position == position || assertion.Position == oppositePosition)
                      && !assertion.RightExpression.IsParent
                select assertion.RightExpression.View).Distinct().ToList();
            
            return !dependentViews.Any() ? null : dependentViews;
        }

        private static Constraint GetXConstraint(IGrouping<View, Assertion> assertions, Assertion widthAssertion)
        {
            return GetXorYConstraint(assertions, widthAssertion, "X", i => i.IsXConstraint());
        }


        private static Constraint GetXorYConstraint(IGrouping<View, Assertion> assertions, 
                                                    Assertion sizeAssertion, string xorY,
                                                    Func<Assertion, bool> positionConstraintFunc)
        {
            var xAssertions = assertions.Where(positionConstraintFunc).ToList();
            if (xAssertions.Count == 0) return null;
            if (xAssertions.Count == 1)
            {
                return xAssertions[0].GetPositionConstraint(sizeAssertion);
            }
{'fraction_non_alphanumeric': 0.0813512330366263, 'fraction_numerical': 0.0021158616664234643, 'mean_word_length': 1.907991513437058, 'pattern_counts': {'":': 0, '<': 30, '<?xml version=': 0, '>': 98, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1529772', 'n_tokens_mistral': 9767, 'n_tokens_neox': 9084, 'n_words': 2618}