using Minesweeper.Core;
using Minesweeper.ECS.Components;
using Unity.Collections;
using Unity.Entities;

namespace Minesweeper.ECS
{
    public sealed class BoardEcsSyncService : IBoardEcsSyncService
    {
        public void SyncBoard(IBoardService boardService)
        {
            if (!TryGetEntityManager(out var entityManager))
            {
                return;
            }

            ClearCells(entityManager);

            var boardEntity = GetOrCreateSingleton<BoardConfigComponent>(entityManager);
            entityManager.SetComponentData(boardEntity, new BoardConfigComponent
            {
                Width = boardService.Width,
                Height = boardService.Height,
                MinesCount = boardService.MinesCount
            });

            var archetype = entityManager.CreateArchetype(typeof(CellComponent));
            var cells = boardService.GetCells();
            for (var i = 0; i < cells.Count; i++)
            {
                var cell = cells[i];
                var entity = entityManager.CreateEntity(archetype);
                entityManager.SetComponentData(entity, new CellComponent
                {
                    X = cell.X,
                    Y = cell.Y,
                    IsMine = ToByte(cell.IsMine),
                    IsOpened = ToByte(cell.IsOpened),
                    IsFlagged = ToByte(cell.IsFlagged),
                    NeighborMines = cell.NeighborMines
                });
            }
        }

        public void SyncGameState(GameState state, bool hasFirstClick)
        {
            if (!TryGetEntityManager(out var entityManager))
            {
                return;
            }

            var stateEntity = GetOrCreateSingleton<GameStateComponent>(entityManager);
            entityManager.SetComponentData(stateEntity, new GameStateComponent
            {
                State = state,
                HasFirstClick = ToByte(hasFirstClick)
            });
        }

        private static bool TryGetEntityManager(out EntityManager entityManager)
        {
            var world = World.DefaultGameObjectInjectionWorld;
            if (world == null || !world.IsCreated)
            {
                entityManager = default;
                return false;
            }

            entityManager = world.EntityManager;
            return true;
        }

        private static void ClearCells(EntityManager entityManager)
        {
            var query = entityManager.CreateEntityQuery(typeof(CellComponent));
            entityManager.DestroyEntity(query);
            query.Dispose();
        }

        private static Entity GetOrCreateSingleton<T>(EntityManager entityManager) where T : unmanaged, IComponentData
        {
            var query = entityManager.CreateEntityQuery(typeof(T));
            Entity entity;

            if (query.IsEmptyIgnoreFilter)
            {
                entity = entityManager.CreateEntity(typeof(T));
            }
            else
            {
                var entities = query.ToEntityArray(Allocator.Temp);
                entity = entities[0];
                for (var i = 1; i < entities.Length; i++)
                {
                    entityManager.DestroyEntity(entities[i]);
                }

                entities.Dispose();
            }

            query.Dispose();
            return entity;
        }

        private static byte ToByte(bool value)
        {
            return value ? (byte)1 : (byte)0;
        }
    }
}
