using System;
using System.Collections;
using System.Diagnostics;
using freetrain.contributions.population;
using freetrain.framework.plugin;
using freetrain.world.rail;

namespace freetrain.world.structs
{
	/// <summary>
	/// StationListener implementation that uses
	/// Population object to calculate population.
	/// </summary>
	[Serializable]
	public class SpecialStationListenerImpl : rail.SpecialStationListener
	{
		public const int MaxStationCount = 4;
		/// <param name="pop">Population pattern</param>
		/// <param name="loc">The location used to decide if this object
		/// can subscribe to a given station.</param>
		public SpecialStationListenerImpl( Population pop, Location loc ) {
			this.population = pop;
			this.location = loc;
			stations = new ArrayList(MaxStationCount);
			if( population!=null )
				attachToStation();	// attach to the existing station if any
		}

		/// <summary>
		/// Station to which this structure sends population to.
		/// </summary>
		//private Station station;
		private ArrayList stations;

		private readonly Location location;

		private readonly Population population;


		/// <summary>
		/// Should be called when the owner is removed.
		/// </summary>
		public void onRemoved() {
			// remove from the currently attached station
			foreach(Station station in stations)
			{
				station.speciallisteners.remove(this);
			}
			stations.Clear();
		}

		public int getPopulation( Station s ) {
			int p =population.calcPopulation(World.world.clock);
			p /= stations.Count;
			return p;  // SpecialStationListener is return population DIRECT
		}

		public int getEntering( Station s ) {
			int e =population.calcEntering(World.world.clock);
			e /= stations.Count;
			return e;
		}

		public bool advertiseStation( Station s ) {
			// keep stations within 4
			if(stations.Count<MaxStationCount)
				s.speciallisteners.add(this);
			else	
			{
				int dmax = location.distanceTo(s.baseLocation);
				Station remove = null;
				foreach(Station station in stations)
				{
					int d = location.distanceTo(station.baseLocation);
					if(d>dmax)
					{
						remove = station;
						dmax = d;
					}
				}
				if(remove!=null)
				{
					remove.speciallisteners.remove(this);
					stations[stations.IndexOf(remove)]=s;
				}
				return false;
			}			
			stations.Add(s);
			return true;
		}

		public void onStationRemoved( Station s ) {
			stations.Remove(s);
		}

		/// <summary>
		/// Finds the nearest station and attaches to it.
		/// </summary>
		private void attachToStation() {
			foreach( Station s in World.world.stations ) {
				if( !s.withinSpecialReach(location) )
					continue;
				advertiseStation(s);					
			}
		}
	}
}
