using Cryville.EEW.Core.Map;
using System;
using System.Collections.Generic;
using System.Drawing;
using UnityEngine;
using Color = UnityEngine.Color;

namespace Cryville.EEW.Unity.Map.Element {
	class TsunamiHeightElement : MapElement {
		[SerializeField] SpriteRenderer _barRenderer;
		[SerializeField] SpriteRenderer _risingMark;
		[SerializeField] SpriteRenderer _outOfRangeMark;
		[SerializeField] SpriteRenderer _missingMark;

		float _baseBarSize;

		PointF _tilePos;
		public override RectangleF? AABB => new(_tilePos, SizeF.Empty);

		public void SetLocation(PointF location, ref int order) {
			_tilePos = MapTileUtils.WorldToTilePos(location);
			Vector3 pos = _tilePos.ToVector2();
			pos.z = OrderToZ(ref order);
			transform.localPosition = pos;
		}

		sealed class GlobalHeightScaler {
			static GlobalHeightScaler s_instance;
			public static GlobalHeightScaler Instance => s_instance ??= new();

			readonly List<float> _heights = new();
			public void PushHeight(float height) {
				var i = _heights.BinarySearch(height);
				if (i < 0) i = ~i;
				_heights.Insert(i, height);
				UpdateMaxHeight();
			}
			public void PopHeight(float height) {
				var i = _heights.BinarySearch(height);
				if (i < 0) return;
				_heights.RemoveAt(i);
				UpdateMaxHeight();
			}
			void UpdateMaxHeight() {
				float newMaxHeight = _heights.Count == 0 ? 0 : _heights[^1];
				if (newMaxHeight == MaxHeight) return;
				MaxHeight = newMaxHeight;
				Update?.Invoke();
			}
			public event Action Update;
			public float MaxHeight { get; private set; }
		}

		float? m_height;
		public float Height {
			get => m_height.Value;
			set {
				if (m_height == value) return;
				if (m_height != null) GlobalHeightScaler.Instance.PopHeight(m_height.Value);
				else GlobalHeightScaler.Instance.Update += UpdateHeight;
				m_height = value;
				GlobalHeightScaler.Instance.PushHeight(value);
				UpdateHeight();
			}
		}
		void UpdateHeight() {
			float h = _baseBarSize * (Math.Max(0, Height) / Math.Max(3, GlobalHeightScaler.Instance.MaxHeight) * 12);
			_barRenderer.size = new(_baseBarSize, h + _baseBarSize);
			UpdateMarkHeight(_risingMark, h);
			UpdateMarkHeight(_outOfRangeMark, h);
		}
		void UpdateMarkHeight(SpriteRenderer mark, float h) {
			mark.transform.localPosition = new(0, _baseBarSize / 2 + mark.size.y / 2 + h);
		}

		[SerializeField]
		Color m_color = Color.white;
		public Color Color {
			get => m_color;
			set {
				if (m_color == value) return;
				m_color = value;
				_barRenderer.color = value;
				_risingMark.color = value;
				_outOfRangeMark.color = value;
				_missingMark.color = value;
			}
		}

		bool m_isRising;
		public bool IsRising {
			get => m_isRising;
			set {
				if (m_isRising == value) return;
				m_isRising = value;
				_risingMark.gameObject.SetActive(value);
				UpdateHeight();
			}
		}

		bool m_isOutOfRange;
		public bool IsOutOfRange {
			get => m_isOutOfRange;
			set {
				if (m_isOutOfRange == value) return;
				m_isOutOfRange = value;
				_outOfRangeMark.gameObject.SetActive(value);
				UpdateHeight();
			}
		}

		bool m_isMissing;
		public bool IsMissing {
			get => m_isMissing;
			set {
				if (m_isMissing == value) return;
				m_isMissing = value;
				_missingMark.gameObject.SetActive(value);
				_barRenderer.gameObject.SetActive(!value);
			}
		}

		float m_width;
		public float Width {
			get => m_width;
			set {
				if (m_width == value) return;
				m_width = value;
				OnSetScale();
			}
		}

		void Awake() {
			_baseBarSize = _barRenderer.size.x;
		}
		void OnDestroy() {
			if (m_height != null) {
				GlobalHeightScaler.Instance.PopHeight(m_height.Value);
				GlobalHeightScaler.Instance.Update -= UpdateHeight;
			}
		}

		protected override void OnSetScale() {
			transform.localScale = ComputedScale * m_width / 256 * Vector3.one;
		}
	}
}
