using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CardGame
{
    [Flags]
    public enum Suit
    {
        None = 0,
        Spade = 1,
        Diamond = 2,
        Heart = 4,
        Club = 8,
        Joker = 16,
    }

    public class Card : IComparable<Card>
    {
        public const int RankOfJoker = 14;

        public Suit Suit { set; get; }

        int rank;

        public int Rank
        {
            set
            {
                if (value <= 0) throw new ArgumentOutOfRangeException();
                if (value > 13) throw new ArgumentOutOfRangeException();
                this.rank = value;
            }
            get
            {
                return this.rank;
            }
        }

        public Card(Suit suit, int rank)
        {
            this.Suit = suit;
            this.Rank = rank;
        }

        static internal int ToInt(Suit suit)
        {
            switch (suit)
            {
                case Suit.Spade: return 0;
                case Suit.Diamond: return 1;
                case Suit.Heart: return 2;
                case Suit.Club: return 3;
                case Suit.Joker: return 4;
                default: return 0;
            }
        }

        static Suit ToSuit(int i)
        {
            switch (i)
            {
                case 0: return Suit.Spade;
                case 1: return Suit.Diamond;
                case 2: return Suit.Heart;
                case 3: return Suit.Club;
                case 4: return Suit.Joker;
                default: return Suit.None;
            }
        }

        internal Card(int x)
        {
            this.Suit = ToSuit(x / 13);
            this.Rank = (x % 13) + 1;
        }

        internal int ToInt()
        {
            int x = ToInt(this.Suit) * 13;
            x += this.rank - 1;
            return x;
        }

        #region デッキの生成

        /// <summary>
        /// ソート済みのデッキを作る。
        /// ジョーカーは1枚。
        /// </summary>
        /// <returns>ソート済みのデッキ。</returns>
        public static IEnumerable<Card> SortedDeck()
        {
            return SortedDeck(false);
        }

        /// <summary>
        /// ソート済みのデッキを作る。
        /// ジョーカーの枚数を選べる。
        /// </summary>
        /// <param name="twoJokers">trueならジョーカー1枚、falseなら1枚。</param>
        /// <returns>ソート済みのデッキ。</returns>
        public static IEnumerable<Card> SortedDeck(bool twoJokers)
        {
            int max = twoJokers ? 54 : 53;

            var deck =
                from i in Enumerable.Range(0, max)
                select new Card(i);

            return deck;
        }

        /// <summary>
        /// シャッフルされたデッキを作る。
        /// ジョーカーの枚数を選べる。
        /// </summary>
        /// <returns>シャッフルされたデッキ。</returns>
        public static IEnumerable<Card> ShuffledDeck()
        {
            return ShuffledDeck(false);
        }

        /// <summary>
        /// シャッフルされたデッキを作る。
        /// ジョーカーは1枚。
        /// </summary>
        /// <param name="twoJokers">trueならジョーカー1枚、falseなら1枚。</param>
        /// <returns>シャッフルされたデッキ。</returns>
        public static IEnumerable<Card> ShuffledDeck(bool twoJokers)
        {
            Random rnd = new Random();
            var deck = SortedDeck(twoJokers).OrderBy(x => rnd.Next());

            return deck;
        }

        public static IEnumerable<Card> Shuffle(IEnumerable<Card> cards)
        {
            Random rnd = new Random();
            return cards.OrderBy(x => rnd.Next());
        }

        public static IEnumerable<Card> Shuffle(IEnumerable<Card> cards, int count)
        {
            Random rnd = new Random();
            return cards.OrderBy(x => rnd.Next()).Take(count);
        }

        #endregion
        #region 配布

        /// <summary>
        /// カードを配る。
        /// </summary>
        /// <param name="cards">配布元のデッキ。</param>
        /// <param name="hands">配布先一覧。</param>
        public static void Deal(IEnumerable<Card> cards, ICollection<Card>[] hands)
        {
            int i = 0;
            foreach (var card in cards)
            {
                hands[i].Add(card);
                ++i;
                if (i >= hands.Count()) i = 0;
            }
        }

        #endregion
        #region シリアライズ

        /// <summary>
        /// カードは重複なしで高々54枚なんだから、longのビットフラグで記録できるよね。
        /// </summary>
        /// <param name="cards">カード一覧。</param>
        /// <returns>ビットフラグ化したもの。</returns>
        public static long Serialize(IEnumerable<Card> cards)
        {
            long flag = 0;

            foreach (var card in cards)
            {
                flag |= 1L << card.ToInt();
            }

            return flag;
        }

        /// <summary>
        /// カードは重複なしで高々54枚なんだから、longのビットフラグで記録できるよね。
        /// </summary>
        /// <param name="cards">ビットフラグ化したもの。</param>
        /// <returns>カード一覧。</returns>
        public static IEnumerable<Card> Deserialize(long flag)
        {
            for (int i = 0; i < 54; ++i)
            {
                bool bit = (flag & (1L << i)) != 0L;
                if (bit)
                    yield return new Card(i);
            }
        }

        #endregion
        #region IComparable<Card> メンバ

        public int CompareTo(Card other)
        {
            return this.ToInt().CompareTo(other.ToInt());
        }

        #endregion
    }
}
