using System;
using System.Collections.Generic;
using System.Linq;
using NMeCab.Core;
using Xunit;

namespace LibNMeCabTest
{
    public class Element : IComparable<Element>
    {
        public int Priority { get; set; }

        public int Order { get; set; }

        public int CompareTo(Element other)
        {
            return Priority.CompareTo(other.Priority);
        }

        public override int GetHashCode()
        {
            return Priority;
        }

        public override bool Equals(object obj)
        {
            var other = obj as Element;
            return other != null
                   && Priority == other.Priority
                   && Order == other.Order;
        }

        public override string ToString()
        {
            return $"priority:{Priority} order:{Order}";
        }
    }


    public class PriorityQueueTest
    {
        private void EmptyExceptionTest<T>(PriorityQueue<T> queue)
            where T : IComparable<T>
        {
            try
            {
                queue.Pop(); //空なら例外発生
            }
            catch (InvalidOperationException)
            {
                return;
            }

            Assert.True(false, "Not Throwed Empty Exception");
        }

        [Fact]
        public void TestMethod1()
        {
            var queue = new PriorityQueue<Element>();
            var collection = new List<Element>();
            var count = 0;

            for (var i = 0; i < 5; i++)
            {
                //追加 優先度昇順
                for (var j = 0; j < 5; j++)
                {
                    var item = new Element {Priority = j, Order = count};
                    queue.Push(item);
                    collection.Add(item);
                    count++;
                    Assert.Equal(queue.Count, count);
                }
            }

            //並べ直し
            collection = (from e in collection
                orderby e.Priority, e.Order
                select e).ToList();

            //取り出し
            foreach (var expected in collection)
            {
                var actual = queue.Pop();
                count--;

                Assert.Equal(expected, actual);
                Assert.Equal(count, queue.Count);
            }

            EmptyExceptionTest(queue);
        }

        [Fact]
        public void TestMethod2()
        {
            var queue = new PriorityQueue<Element>();
            var collection = new List<Element>();
            var count = 0;

            for (var i = 0; i < 5; i++)
            {
                //追加 優先度降順
                for (var j = 5; j >= 0; j--)
                {
                    var item = new Element {Priority = j, Order = count};
                    queue.Push(item);
                    collection.Add(item);
                    count++;

                    Assert.Equal(count, queue.Count);
                }
            }

            //並べ直し
            collection = (from e in collection
                orderby e.Priority, e.Order
                select e).ToList();

            //取り出し
            foreach (var expected in collection)
            {
                var actual = queue.Pop();
                count--;

                Assert.Equal(expected, actual);
                Assert.Equal(count, queue.Count);
            }

            EmptyExceptionTest(queue);
        }

        [Fact]
        public void TestMethod3()
        {
            var queue = new PriorityQueue<Element>();
            var collection = new List<Element>();
            var order = 0;
            var count = 0;
            var rnd = new Random();

            //追加と取り出しを一定数繰り返す
            for (var i = 0; i < 1000; i++)
            {
                //ランダム優先度でランダム個追加
                var repeat = rnd.Next(1, 10);
                for (var j = 0; j < repeat; j++)
                {
                    var item = new Element
                    {
                        Priority = rnd.Next(10),
                        Order = order
                    };
                    collection.Add(item);
                    queue.Push(item);
                    order++;
                    count++;

                    Assert.Equal(count, queue.Count);
                }

                //並べ直し
                collection = (from e in collection
                    orderby e.Priority, e.Order
                    select e).ToList();

                //ランダム個取り出し
                repeat = rnd.Next(1, collection.Count);
                for (var j = 0; j < repeat; j++)
                {
                    var actual = queue.Pop();
                    var expected = collection[j];
                    count--;

                    Assert.Equal(expected, actual);
                    Assert.Equal(count, queue.Count);
                }

                collection.RemoveRange(0, repeat);
            }

            while (queue.Count > 0) queue.Pop(); //空にする
            EmptyExceptionTest(queue);
        }

        [Fact]
        public void TestMethod4()
        {
            var queue = new PriorityQueue<string>();

            for (var i = 0; i < 10; i++)
            {
                queue.Push("abc");
            }

            queue.Clear(); //テスト

            Assert.Equal(0, queue.Count);
            EmptyExceptionTest(queue);
        }

        [Fact]
        public void TestMethod5()
        {
            var queue = new PriorityQueue<int>();

            //10万件挿入
            for (var i = 0; i < 100000; i++)
            {
                queue.Push(i % 5);
            }

            //取り出し
            while (queue.Count > 0)
            {
                queue.Pop();
            }
        }
    }
}
