using System;
using UnityEngine;
using UnityEngine.UI;

namespace Cryville.Common.Unity.UI {
	[AddComponentMenu("Layout/Flow Layout Group")]
	public class FlowLayoutGroup : LayoutGroup {
		[SerializeField] Vector2 m_spacing;
		public Vector2 Spacing {
			get { return m_spacing; }
			set { SetProperty(ref m_spacing, value); }
		}

		[SerializeField][Range(0, 1)] float m_itemAlignmentRatio;
		public float ItemAlignmentRatio {
			get { return m_itemAlignmentRatio; }
			set { SetProperty(ref m_itemAlignmentRatio, value); }
		}

		[SerializeField][Range(0, 1)] float m_itemAlignmentStretchingRatio;
		public float ItemAlignmentStretchingRatio {
			get { return m_itemAlignmentStretchingRatio; }
			set { SetProperty(ref m_itemAlignmentStretchingRatio, value); }
		}

		/// <inheritdoc />
		public sealed override void CalculateLayoutInputHorizontal() { base.CalculateLayoutInputHorizontal(); CalcAlongAxis(0); }
		/// <inheritdoc />
		public sealed override void CalculateLayoutInputVertical() { CalcAlongAxis(1); }
		/// <inheritdoc />
		public sealed override void SetLayoutHorizontal() { SetChildrenAlongAxis(0); }
		/// <inheritdoc />
		public sealed override void SetLayoutVertical() { SetChildrenAlongAxis(1); }

		void CalcAlongAxis(int axis) {
			if (axis == 0) {
				SetLayoutInputForAxis(0, 0, 1, axis); // TODO
			}
			else {
				float width = rectTransform.rect.width - padding.horizontal;
				float x = 0, y = padding.top;
				float lineHeight = 0;
				for (int i = 0; i < rectChildren.Count; i++) {
					RectTransform child = rectChildren[i];
					float minWidth = LayoutUtility.GetMinWidth(child);
					float childWidth = Math.Max(minWidth, Math.Min(width, LayoutUtility.GetPreferredWidth(child)));
					float childHeight = LayoutUtility.GetPreferredHeight(child);

					if (childWidth > width - x) {
						x = 0;
						if (i > 0) y += lineHeight + m_spacing.y;
						lineHeight = 0;
					}

					x += childWidth + m_spacing.x;
					if (childHeight > lineHeight) lineHeight = childHeight;
				}
				SetLayoutInputForAxis(y + lineHeight, y + lineHeight, 0, 1);
			}
		}

		void SetChildrenAlongAxis(int axis) {
			float width = rectTransform.rect.width - padding.horizontal;
			float x = 0, y = padding.top;
			float lineHeight = 0;
			int firstItemIndexOfLine = 0;
			for (int i = 0; i < rectChildren.Count; i++) {
				RectTransform child = rectChildren[i];
				float minWidth = LayoutUtility.GetMinWidth(child);
				float childWidth = Math.Max(minWidth, Math.Min(width, LayoutUtility.GetPreferredWidth(child)));
				float childHeight = LayoutUtility.GetPreferredHeight(child);

				if (childWidth > width - x) {
					AlignItemsInLine(firstItemIndexOfLine, i, y, lineHeight);
					x = 0;
					if (i > 0) y += lineHeight + m_spacing.y;
					lineHeight = 0;
					firstItemIndexOfLine = i;
				}

				SetChildAlongAxis(child, 0, x + padding.left, childWidth);
				SetChildAlongAxis(child, 1, y, childHeight);

				x += childWidth + m_spacing.x;
				if (childHeight > lineHeight) lineHeight = childHeight;
			}
			AlignItemsInLine(firstItemIndexOfLine, rectChildren.Count, y, lineHeight);
		}
		void AlignItemsInLine(int startIndex, int endIndex, float y, float lineHeight) {
			for (int i = startIndex; i < endIndex; i++) {
				RectTransform child = rectChildren[i];
				float childHeight = LayoutUtility.GetPreferredHeight(child) * (1 - m_itemAlignmentStretchingRatio) + lineHeight * m_itemAlignmentStretchingRatio;
				SetChildAlongAxis(child, 1, y + (lineHeight - childHeight) * m_itemAlignmentRatio, childHeight);
			}
		}
	}
}
