using UnityEngine;
using UnityEngine.UI;

namespace Cryville.Common.Unity.UI {
	[AddComponentMenu("Layout/Single Layout Group")]
	[ExecuteAlways]
	public class SingleLayoutGroup : LayoutGroup {
		[SerializeField]
		protected bool m_ChildForceExpandWidth = true;

		[SerializeField]
		protected bool m_ChildForceExpandHeight = true;

		[SerializeField]
		protected bool m_ChildControlWidth = true;

		[SerializeField]
		protected bool m_ChildControlHeight = true;

		[SerializeField]
		protected bool m_ChildScaleWidth;

		[SerializeField]
		protected bool m_ChildScaleHeight;

		public override void CalculateLayoutInputHorizontal() {
			base.CalculateLayoutInputHorizontal();
			CalcAlongAxis(0);
		}
		public override void CalculateLayoutInputVertical() { CalcAlongAxis(1); }
		public override void SetLayoutHorizontal() { SetChildrenAlongAxis(0); }
		public override void SetLayoutVertical() { SetChildrenAlongAxis(1); }

		protected void CalcAlongAxis(int axis) {
			if (rectChildren.Count == 0) return;
			float combinedPadding = (axis == 0) ? padding.horizontal : padding.vertical;
			bool controlSize = (axis == 0) ? m_ChildControlWidth : m_ChildControlHeight;
			bool useScale = (axis == 0) ? m_ChildScaleWidth : m_ChildScaleHeight;
			bool childForceExpandSize = (axis == 0) ? m_ChildForceExpandWidth : m_ChildForceExpandHeight;
			float totalMin = combinedPadding;
			float totalPreferred = combinedPadding;
			float totalFlexible = 0f;
			RectTransform child = rectChildren[0];
			GetChildSizes(child, axis, controlSize, childForceExpandSize, out var min, out var preferred, out var flexible);
			if (useScale) {
				float scaleFactor = child.localScale[axis];
				min *= scaleFactor;
				preferred *= scaleFactor;
				flexible *= scaleFactor;
			}
			totalMin = Mathf.Max(min + combinedPadding, totalMin);
			totalPreferred = Mathf.Max(preferred + combinedPadding, totalPreferred);
			totalFlexible = Mathf.Max(flexible, totalFlexible);
			totalPreferred = Mathf.Max(totalMin, totalPreferred);
			SetLayoutInputForAxis(totalMin, totalPreferred, totalFlexible, axis);
		}

		protected void SetChildrenAlongAxis(int axis) {
			if (rectChildren.Count == 0) return;
			float size = rectTransform.rect.size[axis];
			bool controlSize = (axis == 0) ? m_ChildControlWidth : m_ChildControlHeight;
			bool useScale = (axis == 0) ? m_ChildScaleWidth : m_ChildScaleHeight;
			bool childForceExpandSize = (axis == 0) ? m_ChildForceExpandWidth : m_ChildForceExpandHeight;
			float alignmentOnAxis = GetAlignmentOnAxis(axis);
			float innerSize = size - ((axis == 0) ? padding.horizontal : padding.vertical);
			RectTransform child = rectChildren[0];
			GetChildSizes(child, axis, controlSize, childForceExpandSize, out var min2, out var preferred2, out var flexible2);
			float scaleFactor2 = useScale ? child.localScale[axis] : 1f;
			float requiredSpace = Mathf.Clamp(innerSize, min2, (flexible2 > 0f) ? size : preferred2);
			float startOffset = GetStartOffset(axis, requiredSpace * scaleFactor2);
			if (controlSize) {
				SetChildAlongAxisWithScale(child, axis, startOffset, requiredSpace, scaleFactor2);
			}
			else {
				float offsetInCell2 = (requiredSpace - child.sizeDelta[axis]) * alignmentOnAxis;
				SetChildAlongAxisWithScale(child, axis, startOffset + offsetInCell2, scaleFactor2);
			}
		}

		void GetChildSizes(RectTransform child, int axis, bool controlSize, bool childForceExpand, out float min, out float preferred, out float flexible) {
			if (!controlSize) {
				min = child.sizeDelta[axis];
				preferred = min;
				flexible = 0f;
			}
			else {
				min = LayoutUtility.GetMinSize(child, axis);
				preferred = LayoutUtility.GetPreferredSize(child, axis);
				flexible = LayoutUtility.GetFlexibleSize(child, axis);
			}
			if (childForceExpand) {
				flexible = Mathf.Max(flexible, 1f);
			}
		}
	}
}
