using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;

namespace nft.core.schedule
{
	/// <summary>
	/// Priority queue implementation
	/// </summary>
	[Serializable]
	public class ClockEventQueue : IClockEventQueue
	{
		public ClockEventQueue(long wrap_ticks) {
            this.wrapTicks = wrap_ticks;
            prevTicks = 0;
        }

        /// <summary>
        /// time ticks will modified to the modulation of wrapTicks before evaluation;
        /// </summary>
        protected readonly long wrapTicks;
        
        /// <summary>
		/// Actual data structure that realizes the priority queue.
		/// </summary>
        protected readonly List<HandlerWrapper> list = new List<HandlerWrapper>();

        /// <summary>
        /// remenber the previously evaluated ticks;
        /// </summary>
        protected long prevTicks = -1L;
        
        /// <summary>
        /// remenber the last evaluated index;
        /// </summary>
        protected int prevIndex = -1;

        protected virtual long Wrap(Time t) {
            return t.Ticks % wrapTicks;
        }

        protected virtual long Wrap(long l) {
            return l % wrapTicks;
        }

		/// <summary>
		/// Inserts a new object into the queue.
		/// </summary>
		public void Add( Time time, ClockHandler h ) 
		{
            HandlerWrapper hw = new HandlerWrapper(h, Wrap(time));
            int i = list.BinarySearch(hw);
            if (i < 0) i = ~i;
            list.Insert(i, hw);
            if (i <= prevIndex && Wrap(time) < prevTicks) {
                prevIndex++;
            }
		}

		/// <summary>
		/// Removes specified handler which assigned to specified time.
		/// </summary>
		public void Remove( Time time, ClockHandler h ) 
		{
            HandlerWrapper hw = new HandlerWrapper(h, Wrap(time));
            int i = list.BinarySearch(hw);
            if (i >= 0) {
                RemoveAt(i);
            }
		}

		/// <summary>
		/// Removes specified handler which assigned to specified time.
		/// This function search all indices in sequence.
		/// </summary>
		public void Remove( ClockHandler h ) 
		{
			int n = list.Count;
            for(int i=n-1; i>0; i--){
                HandlerWrapper hw = list[i];
                if (hw.handler == (Delegate)h) {
                    RemoveAt(i);
                }
            }
		}

        protected void RemoveAt(int index) {
            list.RemoveAt(index);
            if (prevIndex >= index) {
                prevIndex--;
            }
        }

        /// <summary>
        /// Count of registerd handlers.
        /// </summary>
        public int Count {
            get {
                return list.Count;
            }
        }

		public void Dispatch(Clock clock)
		{
            long current_ticks = clock.Ticks;
            if (list.Count == 0) return;
            long nextTicks = Wrap(current_ticks);
            if (prevTicks > nextTicks) {
                prevIndex = -1;
            }
            int idx = prevIndex+1;
            while (list.Count > idx && list[idx].ticks <= nextTicks) {
                InvokeAt(clock, idx);
                idx++;
			}
            prevIndex = idx-1;
            prevTicks = nextTicks;
		}

		/// <summary>
		/// FireHandler by index
		/// </summary>
		/// <param name="index">the index for 'list' field</param>
        protected virtual void InvokeAt(Clock clock, int index)
		{
            HandlerWrapper hw = list[index];
            if (!hw.Invoke(clock)) {
                RemoveAt(index);
            }
		}

		[Serializable]
		protected class HandlerWrapper : IComparable<HandlerWrapper>
		{
            public readonly Delegate handler;
            public readonly long ticks;
			public HandlerWrapper(ClockHandler h, long t)
			{
				handler = h;
                ticks = t;
			}
			public bool Invoke(Clock clock)
			{
                return (bool)handler.DynamicInvoke(new object[]{clock});
			}

            public override bool Equals(object obj) {
                if (obj is HandlerWrapper) {
                    HandlerWrapper other = (HandlerWrapper)obj;
                    return other.ticks == ticks && other.handler == handler;
                } else {
                    return false;
                }
            }

            public override int GetHashCode() {
                return handler.GetHashCode();
            }

            public int CompareTo(HandlerWrapper other) {
                return Math.Sign(ticks - other.ticks);
            }
        }
	}

	[Serializable]
	class OneShotClockEventQueue : ClockEventQueue
	{
        public OneShotClockEventQueue() : base(0L) {
        }

        protected override long Wrap(Time t) {
            return t.Ticks;
        }

        protected override long Wrap(long current_ticks)
		{
            return current_ticks;
		}

        protected override void InvokeAt(Clock clock, int index) {
            HandlerWrapper hw = list[index];
            hw.Invoke(clock);
            RemoveAt(index); // Remove anywhere.
        }
	}
}

