#region Copyright & License Information
/*
 * Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
 * This file is part of OpenRA, which is free software. It is made
 * available to you under the terms of the GNU General Public License
 * as published by the Free Software Foundation, either version 3 of
 * the License, or (at your option) any later version. For more
 * information, see COPYING.
 */
#endregion

using System.Collections.Generic;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Mods.Common.Graphics;
using OpenRA.Traits;

namespace OpenRA.Mods.Common.Traits.Render
{
	class WithBridgeSpriteBodyInfo : WithSpriteBodyInfo
	{
		public readonly string Type = "GroundLevelBridge";

		[Desc("Offset to search for the 'A' neighbour")]
		public readonly CVec AOffset = CVec.Zero;

		[Desc("Position to search for the 'B' neighbour")]
		public readonly CVec BOffset = CVec.Zero;

		[SequenceReference]
		[Desc("Sequences to use when both neighbours are alive.")]
		public readonly string[] Sequences = { "idle" };

		[SequenceReference]
		public readonly string[] ADestroyedSequences = { "adestroyed" };

		[SequenceReference]
		public readonly string[] BDestroyedSequences = { "bdestroyed" };

		[SequenceReference]
		public readonly string[] ABDestroyedSequences = { "abdestroyed" };

		public override object Create(ActorInitializer init) { return new WithBridgeSpriteBody(init, this); }

		public override IEnumerable<IActorPreview> RenderPreviewSprites(ActorPreviewInitializer init, RenderSpritesInfo rs, string image, int facings, PaletteReference p)
		{
			if (!EnabledByDefault)
				yield break;

			var anim = new Animation(init.World, image);
			anim.PlayFetchIndex(RenderSprites.NormalizeSequence(anim, init.GetDamageState(), Sequences.First()), () => 0);

			yield return new SpriteActorPreview(anim, () => WVec.Zero, () => 0, p, rs.Scale);
		}
	}

	class WithBridgeSpriteBody : WithSpriteBody, INotifyRemovedFromWorld
	{
		readonly WithBridgeSpriteBodyInfo bridgeInfo;
		readonly BridgeLayer bridgeLayer;
		readonly Actor self;

		public WithBridgeSpriteBody(ActorInitializer init, WithBridgeSpriteBodyInfo info)
			: base(init, info)
		{
			self = init.Self;
			bridgeInfo = info;
			bridgeLayer = init.World.WorldActor.Trait<BridgeLayer>();
		}

		protected override void TraitEnabled(Actor self)
		{
			base.TraitEnabled(self);

			if (bridgeInfo.AOffset != CVec.Zero)
				UpdateNeighbour(bridgeInfo.AOffset);

			if (bridgeInfo.BOffset != CVec.Zero)
				UpdateNeighbour(bridgeInfo.BOffset);

			SetDirty();
		}

		void UpdateNeighbour(CVec offset)
		{
			var neighbour = bridgeLayer[self.Location + offset];
			if (neighbour == null)
				return;

			var body = neighbour.TraitOrDefault<WithBridgeSpriteBody>();
			if (body != null && body.bridgeInfo.Type == bridgeInfo.Type)
				body.SetDirty();
		}

		void INotifyRemovedFromWorld.RemovedFromWorld(Actor self)
		{
			UpdateNeighbour(bridgeInfo.AOffset);
			UpdateNeighbour(bridgeInfo.BOffset);
		}

		void SetDirty()
		{
			self.World.AddFrameEndTask(w =>
			{
				var aDestroyed = bridgeInfo.AOffset != CVec.Zero && NeighbourIsDestroyed(bridgeInfo.AOffset);
				var bDestroyed = bridgeInfo.BOffset != CVec.Zero && NeighbourIsDestroyed(bridgeInfo.BOffset);

				var sequence = DefaultAnimation.CurrentSequence.Name;
				if (aDestroyed && bDestroyed && bridgeInfo.ABDestroyedSequences.Any())
					sequence = bridgeInfo.ABDestroyedSequences.Random(Game.CosmeticRandom);
				else if (aDestroyed && bridgeInfo.ADestroyedSequences.Any())
					sequence = bridgeInfo.ADestroyedSequences.Random(Game.CosmeticRandom);
				else if (bDestroyed && bridgeInfo.BDestroyedSequences.Any())
					sequence = bridgeInfo.BDestroyedSequences.Random(Game.CosmeticRandom);
				else
					sequence = bridgeInfo.Sequences.Random(Game.CosmeticRandom);

				DefaultAnimation.PlayRepeating(NormalizeSequence(self, sequence));
			});
		}

		bool NeighbourIsDestroyed(CVec offset)
		{
			var neighbour = bridgeLayer[self.Location + offset];
			if (neighbour == null)
				return false;

			var segment = neighbour.TraitOrDefault<IBridgeSegment>();
			if (segment == null)
				return false;

			return segment.DamageState == DamageState.Dead;
		}
	}
}
