using Daifugou;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using CardGame;
using System.Collections.Generic;
using System.Linq;

namespace DaifugouTest
{
    
    
    /// <summary>
    ///GameTest のテスト クラスです。すべての
    ///GameTest 単体テストをここに含めます
    ///</summary>
    [TestClass()]
    public class GameTest
    {
        static CardGame.Card S(int rank)
        {
            return new CardGame.Card(CardGame.Suit.Spade, rank);
        }

        static CardGame.Card D(int rank)
        {
            return new CardGame.Card(CardGame.Suit.Diamond, rank);
        }

        static CardGame.Card H(int rank)
        {
            return new CardGame.Card(CardGame.Suit.Heart, rank);
        }

        static CardGame.Card C(int rank)
        {
            return new CardGame.Card(CardGame.Suit.Club, rank);
        }

        static readonly CardGame.Card Joker = new CardGame.Card(CardGame.Suit.Joker, 1);
        static readonly CardGame.Card Joker2 = new CardGame.Card(CardGame.Suit.Joker, 2);

        private TestContext testContextInstance;

        /// <summary>
        ///現在のテストの実行についての情報および機能を
        ///提供するテスト コンテキストを取得または設定します。
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        #region 追加のテスト属性
        // 
        //テストを作成するときに、次の追加属性を使用することができます:
        //
        //クラスの最初のテストを実行する前にコードを実行するには、ClassInitialize を使用
        //[ClassInitialize()]
        //public static void MyClassInitialize(TestContext testContext)
        //{
        //}
        //
        //クラスのすべてのテストを実行した後にコードを実行するには、ClassCleanup を使用
        //[ClassCleanup()]
        //public static void MyClassCleanup()
        //{
        //}
        //
        //各テストを実行する前にコードを実行するには、TestInitialize を使用
        //[TestInitialize()]
        //public void MyTestInitialize()
        //{
        //}
        //
        //各テストを実行した後にコードを実行するには、TestCleanup を使用
        //[TestCleanup()]
        //public void MyTestCleanup()
        //{
        //}
        //
        #endregion

        System.Random rnd = new System.Random();

        /// <summary>
        ///IsSameRank のテスト
        ///</summary>
        [TestMethod()]
        public void IsSameRankTest()
        {
            // 手動で設定したOKリスト。
            var okList = new[] {
                new Pile { H(1), C(1) },
                new Pile { H(1), C(1), S(1) },
                new Pile { H(2), D(2) },
                new Pile { D(3), H(3), C(3), S(3) },
                new Pile { Joker, D(4), H(4) },
                new Pile { D(5), Joker, S(5), Joker2 },
                new Pile { D(6), H(6), C(6), S(6), Joker },
                new Pile { D(7), H(7), C(7), S(7), Joker, Joker2 },
                new Pile { D(8), C(8), S(8), H(8) },
                new Pile { S(9), D(9) },
                new Pile { H(10), D(10), S(10) },
            };

            foreach (var ok in okList)
            {
                int r = Game.IsSameRank(ok);
                Assert.AreNotEqual(r, 0);
            }

            // 手動で設定したNGリスト。
            var ngList = new[] {
                new Pile { H(1), C(2) },
                new Pile { H(1), C(5), S(1) },
                new Pile { H(2), D(3) },
                new Pile { D(3), H(4), C(3), S(3) },
                new Pile { Joker, D(1), H(4) },
                new Pile { D(5), Joker, S(11), Joker2 },
                new Pile { D(6), H(6), C(12), S(6), Joker },
                new Pile { D(13), H(7), C(7), S(7), Joker, Joker2 },
                new Pile { D(1), C(8), S(8), H(8) },
                new Pile { S(2), D(9) },
                new Pile { D(10), D(11), D(12) },
            };

            foreach (var ng in ngList)
            {
                int r = Game.IsSameRank(ng);
                Assert.AreEqual(r, 0);
            }

            // 自動生成したOKリスト。
            for (int rank = 1; rank <= 13; ++rank)
                for (int i = 0; i < 32; ++i)
                {
                    // ランク一緒でスイート違いの札山生成。
                    Pile list = new Pile();
                    if ((i & 1) != 0) list.Add(S(rank));
                    if ((i & 2) != 0) list.Add(D(rank));
                    if ((i & 4) != 0) list.Add(H(rank));
                    if ((i & 8) != 0) list.Add(C(rank));
                    if ((i & 16) != 0) list.Add(Joker);

                    if (list.Count > 1)
                    {
                        int r = Game.IsSameRank(list.OrderBy(x => rnd.Next()));
                        Assert.AreEqual(Game.Rank(rank), r, string.Format("{0}, {1}", rank, i));
                    }
                }
        }

        /// <summary>
        ///IsSameSuit のテスト
        ///</summary>
        [TestMethod()]
        public void IsSameSuitTest()
        {
            // 手動で設定したOKリスト。
            var okList = new[] {
                new Pile { H(1), H(3) },
                new Pile { C(2), C(4), C(6) },
                new Pile { D(2), D(12) },
                new Pile { S(3), S(6), S(9), S(10) },
                new Pile { D(8), D(1), D(9), D(4) },
                new Pile { S(9), S(5) },
                new Pile { H(10), H(1), H(13) },
            };

            foreach (var ok in okList)
            {
                bool r = Game.IsSameSuit(ok);
                Assert.AreEqual(r, true);
            }

            // 手動で設定したNGリスト。
            var ngList = new[] {
                new Pile { H(1), D(2) },
                new Pile { H(1), C(5), S(1) },
                new Pile { H(2), D(3) },
                new Pile { D(3), H(4), C(3), S(3) },
                new Pile { D(1), C(8), S(8), H(8) },
                new Pile { S(2), D(9) },
                new Pile { D(10), S(10), C(10) },
            };

            foreach (var ng in ngList)
            {
                bool r = Game.IsSameSuit(ng);
                Assert.AreEqual(r, false);
            }

            // 自動生成したOKリスト。
            for (int i = 0; i < 200; ++i)
            {
                // 適当な数字の列を生成
                var n =
                    from x in Enumerable.Range(1, 13)
                    where rnd.Next() < int.MaxValue / 2
                    orderby rnd.Next()
                    select x;

                // どんな数字でもスイート一緒ならIsSameSuit == true
                Assert.AreEqual(Game.IsSameSuit(n.Select(x => H(x))), true);
                Assert.AreEqual(Game.IsSameSuit(n.Select(x => C(x))), true);
                Assert.AreEqual(Game.IsSameSuit(n.Select(x => D(x))), true);
                Assert.AreEqual(Game.IsSameSuit(n.Select(x => S(x))), true);
            }
        }

        /// <summary>
        ///IsSequence のテスト
        ///</summary>
        [TestMethod()]
        public void IsSequenceTest()
        {
            // 手動で設定したOKリスト。
            var okList = new[] {
                new Pile { H(1), H(2) },
                new Pile { C(2), C(1), C(13) },
                new Pile { D(11), D(12) },
                new Pile { S(3), S(4), S(5), S(6) },
                new Pile { D(8), D(11), D(10), D(9) },
                new Pile { S(9), S(8) },
                new Pile { H(2), H(10), H(12), H(13), H(11), H(9), H(1) },
                new Pile { D(4), Joker, D(6) },
                new Pile { S(4), Joker, S(6), Joker2, S(8) },
                new Pile { C(4), Joker, C(5), Joker2, C(8) },
            };

            foreach (var ok in okList)
            {
                int r = Game.IsSequence(ok);
                Assert.AreNotEqual(r, 0);
            }

            // 手動で設定したNGリスト。（連番になってない）
            var ngList = new[] {
                new Pile { H(2), H(3) },
                new Pile { C(1), C(5), C(2) },
                new Pile { D(5), D(3) },
                new Pile { D(3), D(4), D(5), D(7) },
                new Pile { H(1), H(12), H(11), H(10) },
                new Pile { S(2), S(9) },
                new Pile { D(10), D(11), D(8) },
                new Pile { D(3), Joker, D(6) },
                new Pile { S(3), Joker, S(6), Joker2, S(8) },
                new Pile { C(4), Joker, C(5), Joker2, C(9) },
            };

            foreach (var ng in ngList)
            {
                int r = Game.IsSequence(ng);
                Assert.AreEqual(r, 0);
            }

            // 手動で設定したNGリスト。（スイート違い）
            ngList = new[] {
                new Pile { H(1), D(2) },
                new Pile { C(2), S(1), C(13) },
                new Pile { D(11), C(12) },
                new Pile { S(3), H(4), S(5), S(6) },
                new Pile { S(8), D(11), D(10), D(9) },
                new Pile { D(9), S(8) },
                new Pile { H(2), H(10), C(12), H(13), H(11), H(9), H(1) },
                new Pile { D(4), Joker, C(6) },
                new Pile { S(4), Joker, H(6), Joker2, S(8) },
                new Pile { C(4), Joker, C(5), Joker2, S(8) },
            };

            foreach (var ng in ngList)
            {
                int r = Game.IsSequence(ng);
                Assert.AreEqual(r, 0);
            }

            // 自動生成したOKリスト。
            for (int i = 0; i < 400; ++i)
            {
                // 適当な連番の数字を生成
                var n =
                    from x in Enumerable.Range(rnd.Next(3, 10), rnd.Next(3, 8))
                    where x <= 15
                    orderby rnd.Next()
                    select x >= 14 ? x - 13 : x;

                // カード化
                Card[] p = null;

                switch (rnd.Next(0, 3))
                {
                    case 0: p = n.Select(x => H(x)).ToArray(); break;
                    case 1: p = n.Select(x => D(x)).ToArray(); break;
                    case 2: p = n.Select(x => C(x)).ToArray(); break;
                    case 3: p = n.Select(x => S(x)).ToArray(); break;
                }

                // 16分の1の確率でジョーカー混入。
                if (rnd.Next() < int.MaxValue / 16)
                {
                    int ii = rnd.Next(0, p.Length - 1);
                    p[ii] = Joker;
                }

                // 32分の1の確率でジョーカーもう1枚混入。
                if (rnd.Next() < int.MaxValue / 32)
                {
                    int ii = rnd.Next(0, p.Length - 1);
                    p[ii] = Joker;
                }

                // どんな数字でもスイート一緒ならIsSameSuit == true
                Assert.AreNotEqual(Game.IsSequence(p), 0);
            }
        }
    }
}
