using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine.Tilemaps;

namespace UnityEngine
{
	[Serializable]
	[CreateAssetMenu(fileName = "New Rule Override Tile", menuName = "Tiles/Rule Override Tile")]
	public class RuleOverrideTile : TileBase
	{
		[Serializable]
		public class TileSpritePair
		{
			public Sprite m_OriginalSprite;
			public Sprite m_OverrideSprite;
		}
		[Serializable]
		public class OverrideTilingRule
		{
			public bool m_Enabled;
			public RuleTile.TilingRule m_TilingRule = new RuleTile.TilingRule();
		}

		public Sprite this[Sprite originalSprite]
		{
			get
			{
				foreach (TileSpritePair spritePair in m_Sprites)
				{
					if (spritePair.m_OriginalSprite == originalSprite)
					{
						return spritePair.m_OverrideSprite;
					}
				}
				return null;
			}
			set
			{
				if (value == null)
				{
					m_Sprites = m_Sprites.Where(spritePair => spritePair.m_OriginalSprite != originalSprite).ToList();
				}
				else
				{
					foreach (TileSpritePair spritePair in m_Sprites)
					{
						if (spritePair.m_OriginalSprite == originalSprite)
						{
							spritePair.m_OverrideSprite = value;
							return;
						}
					}
					m_Sprites.Add(new TileSpritePair()
					{
						m_OriginalSprite = originalSprite,
						m_OverrideSprite = value,
					});
				}
			}
		}
		public RuleTile.TilingRule this[RuleTile.TilingRule originalRule]
		{
			get
			{
				if (!m_Tile)
					return null;

				int index = m_Tile.m_TilingRules.IndexOf(originalRule);
				if (index == -1)
					return null;
				if (m_OverrideTilingRules.Count < index + 1)
					return null;

				return m_OverrideTilingRules[index].m_Enabled ? m_OverrideTilingRules[index].m_TilingRule : null;
			}
			set
			{
				if (!m_Tile)
					return;

				int index = m_Tile.m_TilingRules.IndexOf(originalRule);
				if (index == -1)
					return;

				if (value == null)
				{
					if (m_OverrideTilingRules.Count < index + 1)
						return;
					m_OverrideTilingRules[index].m_Enabled = false;
					while (m_OverrideTilingRules.Count > 0 && !m_OverrideTilingRules[m_OverrideTilingRules.Count - 1].m_Enabled)
						m_OverrideTilingRules.RemoveAt(m_OverrideTilingRules.Count - 1);
				}
				else
				{
					while (m_OverrideTilingRules.Count < index + 1)
						m_OverrideTilingRules.Add(new OverrideTilingRule());
					m_OverrideTilingRules[index].m_Enabled = true;
					m_OverrideTilingRules[index].m_TilingRule = CloneTilingRule(value);
					m_OverrideTilingRules[index].m_TilingRule.m_Neighbors = null;
				}
			}
		}

		public RuleTile m_Tile;
		public bool m_Advanced;
		public List<TileSpritePair> m_Sprites = new List<TileSpritePair>();
		public List<OverrideTilingRule> m_OverrideTilingRules = new List<OverrideTilingRule>();
		public OverrideTilingRule m_OverrideDefault = new OverrideTilingRule();
		public RuleTile.TilingRule m_OriginalDefault
		{
			get
			{
				return new RuleTile.TilingRule()
				{
					m_Sprites = new Sprite[] { m_Tile != null ? m_Tile.m_DefaultSprite : null },
					m_ColliderType = m_Tile != null ? m_Tile.m_DefaultColliderType : Tile.ColliderType.None,
				};
			}
		}

		private RuleTile m_RuntimeTile;

		public override bool GetTileAnimationData(Vector3Int position, ITilemap tilemap, ref TileAnimationData tileAnimationData)
		{
			if (!m_RuntimeTile)
				Override();
			return m_RuntimeTile.GetTileAnimationData(position, tilemap, ref tileAnimationData);
		}
		public override void GetTileData(Vector3Int position, ITilemap tilemap, ref TileData tileData)
		{
			if (!m_RuntimeTile)
				Override();
			m_RuntimeTile.GetTileData(position, tilemap, ref tileData);
		}
		public override void RefreshTile(Vector3Int position, ITilemap tilemap)
		{
			if (!m_RuntimeTile)
				Override();
			m_RuntimeTile.RefreshTile(position, tilemap);
		}
		public override bool StartUp(Vector3Int position, ITilemap tilemap, GameObject go)
		{
			Override();
			return m_RuntimeTile.StartUp(position, tilemap, go);
		}

		public void ApplyOverrides(IList<KeyValuePair<Sprite, Sprite>> overrides)
		{
			if (overrides == null)
				throw new System.ArgumentNullException("overrides");

			for (int i = 0; i < overrides.Count; i++)
				this[overrides[i].Key] = overrides[i].Value;
		}
		public void GetOverrides(List<KeyValuePair<Sprite, Sprite>> overrides)
		{
			if (overrides == null)
				throw new System.ArgumentNullException("overrides");

			overrides.Clear();

			if (!m_Tile)
				return;

			List<Sprite> originalSprites = new List<Sprite>();

			if (m_Tile.m_DefaultSprite)
				originalSprites.Add(m_Tile.m_DefaultSprite);

			foreach (RuleTile.TilingRule rule in m_Tile.m_TilingRules)
				foreach (Sprite sprite in rule.m_Sprites)
					if (sprite && !originalSprites.Contains(sprite))
						originalSprites.Add(sprite);

			foreach (Sprite sprite in originalSprites)
				overrides.Add(new KeyValuePair<Sprite, Sprite>(sprite, this[sprite]));
		}
		public void ApplyOverrides(IList<KeyValuePair<RuleTile.TilingRule, RuleTile.TilingRule>> overrides)
		{
			if (overrides == null)
				throw new System.ArgumentNullException("overrides");

			for (int i = 0; i < overrides.Count; i++)
				this[overrides[i].Key] = overrides[i].Value;
		}
		public void GetOverrides(List<KeyValuePair<RuleTile.TilingRule, RuleTile.TilingRule>> overrides)
		{
			if (overrides == null)
				throw new System.ArgumentNullException("overrides");

			overrides.Clear();

			if (!m_Tile)
				return;

			foreach (var originalRule in m_Tile.m_TilingRules)
			{
				RuleTile.TilingRule overrideRule = this[originalRule];
				overrides.Add(new KeyValuePair<RuleTile.TilingRule, RuleTile.TilingRule>(originalRule, overrideRule));
			}
			overrides.Add(new KeyValuePair<RuleTile.TilingRule, RuleTile.TilingRule>(m_OriginalDefault, m_OverrideDefault.m_TilingRule));
		}

		public void Override()
		{
			m_RuntimeTile = m_Tile ? Instantiate(m_Tile) : new RuleTile();
			m_RuntimeTile.m_Self = this;
			if (!m_Advanced)
			{
				if (m_RuntimeTile.m_DefaultSprite)
					m_RuntimeTile.m_DefaultSprite = this[m_RuntimeTile.m_DefaultSprite];
				if (m_RuntimeTile.m_TilingRules != null)
					foreach (RuleTile.TilingRule rule in m_RuntimeTile.m_TilingRules)
						for (int i = 0; i < rule.m_Sprites.Length; i++)
							if (rule.m_Sprites[i])
								rule.m_Sprites[i] = this[rule.m_Sprites[i]];
			}
			else
			{
				if (m_OverrideDefault.m_Enabled)
				{
					m_RuntimeTile.m_DefaultSprite = m_OverrideDefault.m_TilingRule.m_Sprites.Length > 0 ? m_OverrideDefault.m_TilingRule.m_Sprites[0] : null;
					m_RuntimeTile.m_DefaultColliderType = m_OverrideDefault.m_TilingRule.m_ColliderType;
				}
				if (m_RuntimeTile.m_TilingRules != null) {
					for (int i = 0; i < m_RuntimeTile.m_TilingRules.Count; i++)
					{
						RuleTile.TilingRule originalRule = m_RuntimeTile.m_TilingRules[i];
						RuleTile.TilingRule overrideRule = this[m_Tile.m_TilingRules[i]];
						if (overrideRule == null)
							continue;
						CopyTilingRule(overrideRule, originalRule, false);
					}
				}
			}
		}
		public RuleTile.TilingRule CloneTilingRule(RuleTile.TilingRule from)
		{
			var clone = new RuleTile.TilingRule();
			CopyTilingRule(from, clone, true);
			return clone;
		}
		public void CopyTilingRule(RuleTile.TilingRule from, RuleTile.TilingRule to, bool copyRule)
		{
			if (copyRule)
			{
				to.m_Neighbors = from.m_Neighbors;
				to.m_RuleTransform = from.m_RuleTransform;
			}
			to.m_Sprites = from.m_Sprites.Clone() as Sprite[];
			to.m_AnimationSpeed = from.m_AnimationSpeed;
			to.m_PerlinScale = from.m_PerlinScale;
			to.m_Output = from.m_Output;
			to.m_ColliderType = from.m_ColliderType;
			to.m_RandomTransform = from.m_RandomTransform;
		}
	}
}
