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

    public class GameState
    {
        public readonly IEnumerable<Player> Players;
        public readonly IEnumerable<Player> Opponents;
        public readonly Player HumanPlayer;
        public bool GameOver { get; private set; } = false;

        public readonly Deck Stock;

        /// <summary>
        /// Konstruktor generuje graczy i rozdaje im karty
        /// </summary>
        /// <param name="humanPlayerName">Imię gracza-człowieka</param>
        /// <param name="opponentNames">Nazwy graczy sterowanych przez komputer</param>
        /// <param name="stock">Potasowana talia rozdawanych kart</param>
        public GameState(string humanPlayerName, IEnumerable<string> opponentNames, Deck stock)
        {
            this.Stock = stock;

            HumanPlayer = new Player(humanPlayerName);
            HumanPlayer.GetNextHand(Stock);

            var opponents = new List<Player>();
            foreach (string name in opponentNames)
            {
                var player = new Player(name);
                player.GetNextHand(stock);
                opponents.Add(player);
            }
            Opponents = opponents;
            Players = new List<Player>() { HumanPlayer }.Concat(Opponents);
        }

        /// <summary>
        /// Pobieranie losowego gracza innego niż aktualny
        /// </summary>
        /// <param name="currentPlayer">Aktualny gracz</param>        
        /// <returns>Losowy gracz, którego aktualny gracz może poprosić o kartę</returns>
        public Player RandomPlayer(Player currentPlayer) =>
            Players
                .Where(player => player != currentPlayer)
                .Skip(Player.Random.Next(Players.Count() - 1))
                .First();



        /// <summary>
        /// Rozegranie kolejki przez jednego gracza
        /// </summary>
        /// <param name="player">Gracz proszący o kartę</param>
        /// <param name="playerToAsk">Gracz proszony o kartę</param>
        /// <param name="valueToAskFor">Wartość, o jaką gracz prosi</param>
        /// <param name="stock">Kupka, z której pobierane są karty</param>
        /// <returns>Tekst opisujący, co się stało</returns>
        public string PlayRound(Player player, Player playerToAsk,
                                Values valueToAskFor, Deck stock)
        {
            var valuePlural = valueToAskFor;
            var message = $"{player.Name} prosi gracza {playerToAsk.Name}"
                        + $" o kartę {valuePlural}{Environment.NewLine}";
            var cards = playerToAsk.DoYouHaveAny(valueToAskFor, stock);
            if (cards.Count() > 0)
            {
                player.AddCardsAndPullOutBooks(cards);
                message += $"{playerToAsk.Name} - liczba kart {valueToAskFor}"
                            + $" to {cards.Count()}";
            }
            else
            {
                player.DrawCard(stock);
                message += $"{player.Name} dobrał(a) kartę";
            }

            if (player.Hand.Count() == 0)
            {
                player.GetNextHand(stock);
                message += $"{Environment.NewLine}{player.Name} nie ma już kart,"
                            + $" dobrał(a) ${player.Hand.Count()} z kupki";
            }

            return message;
        }

        /// <summary>
        /// Sprawdza zwycięzcę, badając, czy któremuś z graczy pozostały karty. Przypisuje true
        /// do GameOver, jeśli gra się skończyła i zneleziono zwycięzcę.
        /// </summary>        
        /// <returns>Łańcuch znaków ze zwycięzcami lub pusty łańcuch znaków, jeśli nie ma zwycięzcy</returns>
        public string CheckForWinner()
        {
            var playerCards = Players.Select(player => player.Hand.Count()).Sum();
            if (playerCards > 0) return "";
            GameOver = true;
            var winningBookCount = Players.Select(player => player.Books.Count()).Max();
            var winners = Players.Where(player => player.Books.Count() == winningBookCount);
            if (winners.Count() == 1) return $"Wygrał {winners.First().Name}";
            return $"Wygrali {string.Join(", ", winners)}";
        }
    }
}
