using System;
namespace GoFish
{
    using System.Collections.Generic;
    using System.Linq;

    public class Player
    {
        public static Random Random = new Random();

        private List<Card> hand = new List<Card>();
        private List<Values> books = new List<Values>();

        /// <summary>
        /// Karty w ręku gracza
        /// </summary>
        public IEnumerable<Card> Hand => hand;

        /// <summary>
        /// Zestawy, jakie gracz wyłożył
        /// </summary>
        public IEnumerable<Values> Books => books;

        public readonly string Name;



        /// <summary>
        /// Zwraca aktualny stan gracza - liczbę kart i zestawów.
        /// </summary>
        public string Status =>
            $"{Name} - liczba kart: {hand.Count()}; wyłożone zestawy: {books.Count()}";

        /// <summary>
        /// Konstruktor tworzący obiekt reprezentujący gracza
        /// </summary>
        /// <param name="name">Imię gracza</param>
        public Player(string name) => Name = name;

        /// <summary>
        /// Drugi konstruktor (używany w testach jednostkowych)
        /// </summary>
        /// <param name="name">Imię gracza</param>
        /// <param name="cards">Początkowe karty</param>
        public Player(string name, IEnumerable<Card> cards)
        {
            Name = name;
            hand.AddRange(cards);
        }

        /// <summary>
        /// Pobranie do pięciu kart z kupki
        /// </summary>
        /// <param name="stock">Kupka, z której pobierane są następne karty</param>
        public void GetNextHand(Deck stock)
        {
            while ((stock.Count() > 0) && (hand.Count < 5))
            {
                hand.Add(stock.Deal(0));
            }
        }
        /// <summary>
        /// Jeśli mam karty, które pasują do żądanej wartości, zwracam je. Jeśli 
        /// zabrakło mi kart, dobieram następną z kupki.
        /// </summary>
        /// <param name="value">Wartość żądanej karty</param>
        /// <param name="deck">Kupka, z której dobierane są następne karty</param>
        /// <returns>Karty otrzymane od innego gracza</returns>
        public IEnumerable<Card> DoYouHaveAny(Values value, Deck deck)
        {
            var matchingCards = hand.Where(card => card.Value == value)
                .OrderBy(Card => Card.Suit);
            hand = hand.Where(card => card.Value != value).ToList();

            if (hand.Count() == 0)
                GetNextHand(deck);

            return matchingCards;
        }

        /// <summary>
        /// Gdy gracz otrzymuje karty od innej osoby, dodaje je do własnych
        /// i wykłada zestawy pasujących kart.
        /// </summary>
        /// <param name="cards">Dodawane karty otrzymane od innego gracza</param>
        public void AddCardsAndPullOutBooks(IEnumerable<Card> cards)
        {
            hand.AddRange(cards);

            var foundBooks = hand
                .GroupBy(card => card.Value)
                .Where(group => group.Count() == 4)
                .Select(group => group.Key);

            books.AddRange(foundBooks);
            books.Sort();

            hand = hand
                .Where(card => !books.Contains(card.Value))
                .ToList();
        }

        /// <summary>
        /// Pobieranie karty z kupki i dodawanie je do kart gracza
        /// </summary>
        /// <param name="stock">Kupka, z której pobierane są karty</param>
        public void DrawCard(Deck stock)
        {
            if (stock.Count > 0)
                AddCardsAndPullOutBooks(new List<Card>() { stock.Deal(0) });
        }

        /// <summary>
        /// Wybieranie losowej wartości spośród kart gracza
        /// </summary>
        /// <returns>Wartość losowo wybranej karty z kart gracza</returns>
        public Values RandomValueFromHand() => hand.OrderBy(card => card.Value)
            .Select(card => card.Value)
            .Skip(Random.Next(hand.Count()))
            .First();

        public override string ToString() => Name;
    }
}
