#region Copyright & License Information
/*
 * Copyright 2007-2015 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. For more information,
 * see COPYING.
 */
#endregion

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

namespace OpenRA.Mods.Common.Traits
{
	public class SelectionDecorationsInfo : ITraitInfo, ISelectionDecorationsInfo
	{
		public readonly string Palette = "chrome";

		[Desc("Visual bounds for selection box. If null, it uses AutoSelectionSize.",
		"The first two values define the bounds' size, the optional third and fourth",
		"values specify the position relative to the actors' center. Defaults to selectable bounds.")]
		public readonly int[] VisualBounds = null;

		[Desc("Health bar, production progress bar etc.")]
		public readonly bool RenderSelectionBars = true;

		public readonly bool RenderSelectionBox = true;

		public readonly Color SelectionBoxColor = Color.White;

		public object Create(ActorInitializer init) { return new SelectionDecorations(init.Self, this); }

		public int[] SelectionBoxBounds { get { return VisualBounds; } }
	}

	public class SelectionDecorations : IPostRenderSelection
	{
		// depends on the order of pips in TraitsInterfaces.cs!
		static readonly string[] PipStrings = { "pip-empty", "pip-green", "pip-yellow", "pip-red", "pip-gray", "pip-blue", "pip-ammo", "pip-ammoempty" };
		static readonly string[] TagStrings = { "", "tag-fake", "tag-primary" };

		public readonly SelectionDecorationsInfo Info;
		readonly Actor self;

		public SelectionDecorations(Actor self, SelectionDecorationsInfo info)
		{
			this.self = self;
			Info = info;
		}

		IEnumerable<WPos> ActivityTargetPath()
		{
			if (!self.IsInWorld || self.IsDead)
				yield break;

			var activity = self.GetCurrentActivity();
			if (activity != null)
			{
				var targets = activity.GetTargets(self);
				yield return self.CenterPosition;

				foreach (var t in targets.Where(t => t.Type != TargetType.Invalid))
					yield return t.CenterPosition;
			}
		}

		public IEnumerable<IRenderable> RenderAfterWorld(WorldRenderer wr)
		{
			if (self.World.FogObscures(self))
				yield break;

			if (Info.RenderSelectionBox)
				yield return new SelectionBoxRenderable(self, Info.SelectionBoxColor);

			if (Info.RenderSelectionBars)
				yield return new SelectionBarsRenderable(self);

			if (!self.Owner.IsAlliedWith(wr.World.RenderPlayer))
				yield break;

			if (self.World.LocalPlayer != null && self.World.LocalPlayer.PlayerActor.Trait<DeveloperMode>().PathDebug)
				yield return new TargetLineRenderable(ActivityTargetPath(), Color.Green);

			var b = self.VisualBounds;
			var pos = wr.ScreenPxPosition(self.CenterPosition);
			var tl = wr.Viewport.WorldToViewPx(pos + new int2(b.Left, b.Top));
			var bl = wr.Viewport.WorldToViewPx(pos + new int2(b.Left, b.Bottom));
			var tm = wr.Viewport.WorldToViewPx(pos + new int2((b.Left + b.Right) / 2, b.Top));

			foreach (var r in DrawControlGroup(wr, self, tl))
				yield return r;

			foreach (var r in DrawPips(wr, self, bl))
				yield return r;

			foreach (var r in DrawTags(wr, self, tm))
				yield return r;
		}

		IEnumerable<IRenderable> DrawControlGroup(WorldRenderer wr, Actor self, int2 basePosition)
		{
			var group = self.World.Selection.GetControlGroupForActor(self);
			if (group == null)
				yield break;

			var pipImages = new Animation(self.World, "pips");
			var pal = wr.Palette(Info.Palette);
			pipImages.PlayFetchIndex("groups", () => (int)group);
			pipImages.Tick();

			var pos = basePosition - (0.5f * pipImages.Image.Size).ToInt2() + new int2(9, 5);
			yield return new UISpriteRenderable(pipImages.Image, pos, 0, pal, 1f);
		}

		IEnumerable<IRenderable> DrawPips(WorldRenderer wr, Actor self, int2 basePosition)
		{
			var pipSources = self.TraitsImplementing<IPips>();
			if (!pipSources.Any())
				yield break;

			var pipImages = new Animation(self.World, "pips");
			pipImages.PlayRepeating(PipStrings[0]);

			var pipSize = pipImages.Image.Size.ToInt2();
			var pipxyBase = basePosition + new int2(1 - pipSize.X / 2, -(3 + pipSize.Y / 2));
			var pipxyOffset = new int2(0, 0);
			var pal = wr.Palette(Info.Palette);
			var width = self.VisualBounds.Width;

			foreach (var pips in pipSources)
			{
				var thisRow = pips.GetPips(self);
				if (thisRow == null)
					continue;

				foreach (var pip in thisRow)
				{
					if (pipxyOffset.X + pipSize.X >= width)
						pipxyOffset = new int2(0, pipxyOffset.Y - pipSize.Y);

					pipImages.PlayRepeating(PipStrings[(int)pip]);
					pipxyOffset += new int2(pipSize.X, 0);

					yield return new UISpriteRenderable(pipImages.Image, pipxyBase + pipxyOffset, 0, pal, 1f);
				}

				// Increment row
				pipxyOffset = new int2(0, pipxyOffset.Y - (pipSize.Y + 1));
			}
		}

		IEnumerable<IRenderable> DrawTags(WorldRenderer wr, Actor self, int2 basePosition)
		{
			var tagImages = new Animation(self.World, "pips");
			var pal = wr.Palette(Info.Palette);
			var tagxyOffset = new int2(0, 6);

			foreach (var tags in self.TraitsImplementing<ITags>())
			{
				foreach (var tag in tags.GetTags())
				{
					if (tag == TagType.None)
						continue;

					tagImages.PlayRepeating(TagStrings[(int)tag]);
					var pos = basePosition + tagxyOffset - (0.5f * tagImages.Image.Size).ToInt2();
					yield return new UISpriteRenderable(tagImages.Image, pos, 0, pal, 1f);

					// Increment row
					tagxyOffset = tagxyOffset.WithY(tagxyOffset.Y + 8);
				}
			}
		}
	}
}
