using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using QwirkleClassLibrary.Tiles;

namespace QwirkleClassLibrary.Boards
{
    /// <summary>
    /// This class is used to create the board for our Qwirkle Game. It uses others classes, such as Cell and Tile, to take care of the tiles placements during the game.
    /// </summary>
    [DataContract]
    public class Board : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler? PropertyChanged;

        [DataMember]
        private ObservableCollection<Cell> cells = new ObservableCollection<Cell>();
        
        public ObservableCollection<Cell> ReadCells
        {
            get { return cells; }
            set
            {
                cells = value;
                OnPropertyChanged(nameof(ReadCells));
            }
        }

        [DataMember]
        public double Rows { get; set; }
        
        [DataMember]
        public double Columns { get; set; }

        /// <summary>
        /// This is the constructor for the board. The parameters 'rows' and 'cols' are used to calculate the size of the board.
        /// </summary>
        /// <param name="rows">The numbers of rows in the board.</param>
        /// <param name="cols">The number of columns in the board.</param>
        public Board(int cols, int rows)
        {
            Rows = rows;
            Columns = cols;

            for (int a = 0; a < Rows; a++)
            {
                for (int b = 0; b < Columns; b++)
                {
                    Cell localcell = new(b, a);
                    cells.Add(localcell);
                }
            }
        }

        /// <summary>
        /// This method is used to check if a cell in the board whether it already contains a tile or not.
        /// </summary>
        /// <returns>Returns a boolean : true if the cell doesn't contain any tile, false if it already contains a tile.</returns>
        public bool HasOccupiedCase()
        {
            foreach (var cell in cells)
            {
                if (!cell.IsFree)
                {
                    OnPropertyChanged(nameof(ReadCells));
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// This method is used to add a tile specified by the player, to the coordinates x, y of the board, also provided by the player.
        /// </summary>
        /// <param name="x">The position of the tile in the x-axis of the board.</param>
        /// <param name="y">The position of the tile in the y-axis of the board.</param>
        /// <param name="tile">The tile the player want to place in the board.</param>
        /// <returns></returns>
        public bool AddTileInCell(int x, int y, Tile tile)
        {
            foreach (var t in cells)
            {
                if (t.GetX != x || t.GetY != y) continue;
                if (t.IsFree)
                {
                    OnPropertyChanged(nameof(ReadCells));
                    return t.SetTile(tile);
                }
            }
            OnPropertyChanged(nameof(ReadCells));

            return false;
        }

        /// <summary>
        /// A getter for the cells attribute of the Board Class.
        /// </summary>
        /// <returns>The cells of the board in a List format.</returns>
        public List<Cell> GetCells()
        {
            return cells.ToList();
        }

        /// <summary>
        /// A getter for the cell in the coordinates x, y of the board.
        /// </summary>
        /// <param name="x">The position of the cell in the x-axis of the board.</param>
        /// <param name="y">The position of the cell in the y-axis of the board.</param>
        /// <returns></returns>
        public Cell? GetCell(int x, int y)
        {
            foreach (var t in cells)
            {
                if (t.GetX == x && t.GetY == y)
                {
                    return t;
                }
            }

            return null;
        }
        protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
