using System.Collections.Generic;
using System.Collections.Concurrent;
using System;

namespace Sledgemapper.Shared.Entities
{
    public class MapEntityAddedEventArgs : EventArgs
    {
        public BaseMapEntity MapEntity { get; private set; }
        public MapEntityAddedEventArgs(BaseMapEntity mapEntity) => MapEntity = mapEntity;
    }

    public class MapEntityDeletedEventArgs : EventArgs
    {
        public BaseMapEntity MapEntity { get; private set; }
        public MapEntityDeletedEventArgs(BaseMapEntity mapEntity) => MapEntity = mapEntity;
    }

    public class Session
    {
        public event EventHandler<MapEntityAddedEventArgs> MapEntityAdded;
        public event EventHandler<MapEntityDeletedEventArgs> MapEntityDeleted;

        public Session()
        {
            Map = new ConcurrentDictionary<string, Tile>();
            Overlays = new ConcurrentDictionary<string, Overlay>();
            Walls = new ConcurrentDictionary<string, Wall>();
            Notes = new ConcurrentDictionary<string, Note>();
            Players = new List<Player>();
            Colors = new List<string>();
        }

        public ConcurrentDictionary<string, Tile> Map { get; set; }
        public ConcurrentDictionary<string, Wall> Walls { get; set; }
        public ConcurrentDictionary<string, Overlay> Overlays { get; set; }
        public ConcurrentDictionary<string, Note> Notes { get; set; }
        public bool IsValid { get; set; }
        public List<Player> Players { get; set; }
        public List<string> Colors { get; set; }
        public string SessionName { get; set; }
        public int SessionId { get; set; }

        public void NewTile(Tile selectedTile, string tileId)
        {
            if (selectedTile is null || string.IsNullOrWhiteSpace(tileId))
            {
                return;
            }

            var tileExist = Map.TryGetValue(selectedTile.ToString(), out var tile);
            var newTile = new Tile { X = selectedTile.X, Y = selectedTile.Y, ID = tileId };
            if (tileExist)
            {
                Map.TryRemove(tile.ToString(), out var _);
                if (tile.ID == tileId)
                {
                    newTile.Rotation = (tile.Rotation + 1) % 4;
                }
            }

            Map.TryAdd(newTile.ToString(), newTile);
            OnRaiseMapEntityAddedEvent(new MapEntityAddedEventArgs(newTile));
        }

        public void NewOverlay(Overlay selectedOverlay, string overlayId)
        {
            if (selectedOverlay is null || string.IsNullOrWhiteSpace(overlayId))
            {
                return;
            }
            var overlayExist = Overlays.TryGetValue(selectedOverlay.ToString(), out var overlay);
            var newOverlay = new Overlay { X = selectedOverlay.X, Y = selectedOverlay.Y, ID = overlayId, Intersection = selectedOverlay.Intersection };
            if (overlayExist)
            {
                Overlays.TryRemove(overlay.ToString(), out var _);
                if (overlay.ID == overlayId)
                {
                    newOverlay.Rotation = (overlay.Rotation + 1) % 4;
                }
            }

            Overlays.TryAdd(newOverlay.ToString(), newOverlay);
            OnRaiseMapEntityAddedEvent(new MapEntityAddedEventArgs(newOverlay));
        }

        public void NewWall(Wall selectedWall, string wallId)
        {
            if (selectedWall is null || string.IsNullOrWhiteSpace(wallId))
            {
                return;
            }
            var tileExist = Walls.TryGetValue(selectedWall.ToString(), out var wall);
            var newWall = new Wall { X = selectedWall.X, Y = selectedWall.Y, ID = wallId, Rotation = selectedWall.Rotation };
            if (tileExist)
            {
                Walls.TryRemove(wall.ToString(), out var _);
            }

            Walls.TryAdd(newWall.ToString(), newWall);
            OnRaiseMapEntityAddedEvent(new MapEntityAddedEventArgs(newWall));
        }

        public void NewNote(Note selectedNote)
        {
            if (selectedNote is null)
            {
                return;
            }
            var noteExists = Notes.TryGetValue(selectedNote.ToString(), out var note);
            var newNote = new Note { X = selectedNote.X, Y = selectedNote.Y, Text=selectedNote.Text };
            if (noteExists)
            {
                Walls.TryRemove(note.ToString(), out var _);
            }

            Notes.TryAdd(newNote.ToString(), newNote);
            OnRaiseMapEntityAddedEvent(new MapEntityAddedEventArgs(newNote));
        }

        public void DeleteWall(Wall wall)
        {
            if (wall is null)
            {
                return;
            }
            var removed = Walls.TryRemove(wall.ToString(), out var _);
            if (removed)
            {
                OnRaiseMapEntityDeletedEvent(new MapEntityDeletedEventArgs(wall));
            }
        }

        public void DeleteNote(Note note)
        {
            if (note is null)
            {
                return;
            }
            var removed = Notes.TryRemove(note.ToString(), out var _);
            if (removed)
            {
                OnRaiseMapEntityDeletedEvent(new MapEntityDeletedEventArgs(note));
            }
        }

        public void DeleteOverlay(Overlay overlay)
        {
            if (overlay is null)
            {
                return;
            }
            var removed = Overlays.TryRemove(overlay.ToString(), out var _);
            if (removed)
            {
                OnRaiseMapEntityDeletedEvent(new MapEntityDeletedEventArgs(overlay));
            }
        }

        public void DeleteTile(Tile tile)
        {
            if (tile is null)
            {
                return;
            }
            var removed = Map.TryRemove(tile.ToString(), out var _);
            if (removed)
            {
                OnRaiseMapEntityDeletedEvent(new MapEntityDeletedEventArgs(tile));
            }
        }

        protected virtual void OnRaiseMapEntityAddedEvent(MapEntityAddedEventArgs e)
        {
            MapEntityAdded?.Invoke(this, e);
        }

        protected virtual void OnRaiseMapEntityDeletedEvent(MapEntityDeletedEventArgs e)
        {
            MapEntityDeleted?.Invoke(this, e);
        }
    }
}
