using Microscopic_Traffic_Simulator___Model.CellularTopologyObjects;
using Microscopic_Traffic_Simulator___Model.GeneralParameters;
using Microscopic_Traffic_Simulator___Model.SimulationControl;
using System;
using System.Collections.Generic;

namespace Microscopic_Traffic_Simulator___Model.TrafficObjects
{
    /// <summary>
    /// Represents generator of cars.
    /// </summary>
    [Serializable]
    public class Generator : ISimulationEventsGenerator
    {
        /// <summary>
        /// Checks whether the generator has the pause scheduled. Not used so returns always false.
        /// </summary>
        public bool IsPauseScheduled { get { return false; } }

        /// <summary>
        /// Cell where generator puts cars on
        /// </summary>
        [NonSerialized]
        private Cell connectedCell = null;
        /// <summary>
        /// Cell where generator puts cars on
        /// </summary>
        internal Cell ConnectedCell
        {
            get { return connectedCell; }
            set { connectedCell = value; }
        }

        /// <summary>
        /// Generator priority as the simulation action generator.
        /// </summary>
        private const int GeneratorSimulationPriority = 0;

        /// <summary>
        /// Generator priority as the simulation action generator.
        /// </summary>
        public int Priority { get { return GeneratorSimulationPriority; } }

        /// <summary>
        /// List of tickets representing cars waiting in the generator queue to be 
        /// placed on the connected cell of generator. The tickets values determines initial speed of a car.
        /// </summary>
        [NonSerialized]
        private Queue<int> tickets = new Queue<int>();

        /// <summary>
        /// Event handler for hooking up events that generator's number of tickets changed from zero to one.
        /// </summary>
        internal event EventHandler WasZeroTicketsNowOneTicketIsAvailable;

        /// <summary>
        /// Get new car if there is any in the generator queue.
        /// </summary>
        /// <param name="lastTicketUsed">Flag indicating if the number of tickets dropped from
        /// one to zero.</param>
        /// <param name="transitionFunctionParameters">Reference to transition function parameters.</param>
        /// <returns>Car inserted to connected cell.</returns>
        internal Car GetNewCar(out bool lastTicketUsed, TransitionFunctionParameters transitionFunctionParameters)
        {
            //initialize lastTicketUsed parameter to false
            lastTicketUsed = false;
            //if no tickets available return
            if (tickets.Count == 0)
                return null;
            //check if connected cell is free
            if (connectedCell.Car == null)
            {
                //remove one ticket
                int speed = tickets.Dequeue();
                //check if it was the last ticket
                if (tickets.Count == 0)
                    lastTicketUsed = true;
                //create new car instance
                Car newCar = new Car(connectedCell, speed, transitionFunctionParameters);
                //insert car to connected cell
                connectedCell.Car = newCar;
                //return car
                return newCar;
            }
            else
                return null;
        }

        /// <summary>
        /// Returns time to generation of new ticket.
        /// </summary>
        /// <param name="random">Random instance used if generator is 
        /// dependent on the random variables.</param>
        /// <returns>Time to the next generation of new ticket.</returns>
        public virtual TimeSpan GetTimeToNextAction(Random random)
        {
            return TimeSpan.FromSeconds(5.0);
        }

        /// <summary>
        /// Generate new car.
        /// </summary>
        /// <param name="random">Random instance which is not used.</param>
        public virtual void PerformAction(Random random)
        {
            GenerateTicket(0);
        }

        /// <summary>
        /// Generate new ticket. In case that current number of tickets is zero add generator
        /// to set of generators with tickets.
        /// </summary>
        /// <param name="initialSpeed">Initial speed of car to be generated from new ticket.</param>
        protected void GenerateTicket(int initialSpeed)
        {
            tickets.Enqueue(initialSpeed);
            if (tickets.Count == 1)
                OnWasZeroTicketsNowOneTicketWasGenerated();
        }

        /// <summary>
        /// Initiailize tickets.
        /// </summary>
        internal void InitializeTickets()
        {
            tickets = new Queue<int>();
        }

        /// <summary>
        /// Fires event of that generator had one ticket and now one ticket was generated.
        /// </summary>
        private void OnWasZeroTicketsNowOneTicketWasGenerated()
        {
            if (WasZeroTicketsNowOneTicketIsAvailable != null)
                WasZeroTicketsNowOneTicketIsAvailable(this, EventArgs.Empty);
        }
    }
}
