using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Myra;
using Myra.Graphics2D.Brushes;
using Myra.Graphics2D.TextureAtlases;
using Myra.Graphics2D.UI;
using Myra.Graphics2D.UI.File;
using Myra.Graphics2D.UI.Properties;
using Newtonsoft.Json;
using Sledgemapper.Shared.Entities;
using Sledgemapper.UI;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

namespace Sledgemapper
{
    public class Sledgemapper : Game
    {
        private readonly CommunicationManager _communicationManager;
        private readonly State _state;
        private readonly GraphicsDeviceManager _graphics;
        private SpriteBatch _spriteBatch;
        private readonly Desktop _desktop;
        private KeyboardState oldState;
        private MouseState oldMouseState;
        private Vector3 _viewportCenter = new(0, 0, 0);
        private bool _isDraggin;
        private Dictionary<string, SpriteFont> _fonts;
        private Texture2D _eye;
        private Texture2D _location;
        private Texture2D _comment;
        private readonly Session _sessionData;
        private AuthenticateResponse _authResponse;
        private MainWidget _mainWidget;
        private bool _showCellNumbers;
        private readonly Settings _settings;

        public Sledgemapper()
        {
            _graphics = new GraphicsDeviceManager(this);

            _graphics.GraphicsProfile = GraphicsProfile.Reach;
            _graphics.PreferMultiSampling = false;

            Content.RootDirectory = "Content";
            _desktop = new Desktop();
            MyraEnvironment.Game = this;
            _sessionData = new Session();
            IsFixedTimeStep = false;
            _communicationManager = new CommunicationManager(_sessionData);
            _communicationManager.Connection.Reconnected += OnHubReconnected;
            _communicationManager.Connection.Reconnecting += OnHubReconnecting;
            _communicationManager.Connection.Closed += OnHubDisconnected;
            _state = new State();
            _settings = new Settings();
        }

        private async Task OnHubDisconnected(Exception arg)
        {
            _mainWidget.lblConnectionStatus.Text = "Disconnected";
        }

        private async Task OnHubReconnecting(Exception arg)
        {
            _mainWidget.lblConnectionStatus.Text = "Reconnecting";
        }

        private async Task OnHubReconnected(string arg)
        {
            _mainWidget.lblConnectionStatus.Text = "Connected";
        }

        protected override void Initialize()
        {
            IsMouseVisible = true;
            Window.AllowUserResizing = true;
            base.Initialize();
        }

        private void AddItemToToolGrid(Grid grid, EventHandler eventAction, string folder)
        {
            var tilesFolderContent = Content.LoadContentFolder<Texture2D>(folder);
            var indexX = 0;
            var indexY = 0;
            foreach (var item in tilesFolderContent)
            {
                var tileButton = new ImageButton { Image = new TextureRegion(item.Value), GridColumn = indexY, GridRow = indexX, Id = item.Key, Width = 40, Height = 40 };
                tileButton.Click += eventAction;
                grid.Widgets.Add(tileButton);
                indexY++;
                if (indexY == 4)
                {
                    indexY = 0;
                    indexX++;
                }
            }
        }

        protected override void LoadContent()
        {
            _spriteBatch = new SpriteBatch(GraphicsDevice);
            MyraEnvironment.Game = this;

            _mainWidget = new MainWidget();

            _mainWidget.MenuConnectSync.Selected += OnMenuConnectSyncSelected;
            _mainWidget.MenuFileLoad.Selected += OnMenuFileLoadSelected;
            _mainWidget.MenuFileSave.Selected += OnMenuFileSaveSelected;
            _mainWidget.MenuFileSettings.Selected += OneMenuFileSettingsSelected;
            _mainWidget.MenuConnectLogin.Selected += OnMenuConnectLoginSelected;
            _mainWidget.MenuConnectNew.Selected += OnMenuConnectNewSelected;
            _mainWidget.MenuConnectJoin.Selected += OnMenuConnectJoinSelected;
            _mainWidget.MenuConnectUpload.Selected += OnMenuConnectUploadSelected;
            _mainWidget.MenuViewCenterOnSelection.Selected += OnMenuViewCenterOnSelectionSelected;
            _mainWidget.MenuViewShowCellNUmbers.Selected += OnMenuViewShowCellNUmbersSelected;
            _mainWidget.MenuViewShowNotes.Selected += OnMenuViewNotesSelected;
            _mainWidget.MenuConnectNew.Enabled = false;
            _mainWidget.MenuConnectJoin.Enabled = false;
            _mainWidget.MenuConnectSync.Enabled = false;
            _mainWidget.MenuConnectUpload.Enabled = false;

            AddItemToToolGrid(_mainWidget.GridTiles, OnTileButtonClicked, "tiles");
            AddItemToToolGrid(_mainWidget.GridWalls, OnWallButtonClicked, "walls");
            AddItemToToolGrid(_mainWidget.GridOverlays, OnOverlayButtonClicked, "overlays");

            _fonts = Content.LoadContentFolder<SpriteFont>("fonts");
            _eye = Content.Load<Texture2D>("eye");
            _location = Content.Load<Texture2D>("location");
            _comment = Content.Load<Texture2D>("comment");
            _desktop.Root = _mainWidget;
        }

        private void OneMenuFileSettingsSelected(object sender, EventArgs e)
        {
            var propertyGrid = new PropertyGrid
            {
                Object = _settings,
                Width = 350
            };

            var _windowEditor = new Window
            {
                Title = "Object Editor",
                Content = propertyGrid
            };
            _windowEditor.ShowModal(_desktop);
        }

        private void OnMenuViewShowCellNUmbersSelected(object sender, EventArgs e)
        {
            _showCellNumbers = !_showCellNumbers;
        }

        private void OnMenuViewCenterOnSelectionSelected(object sender, EventArgs e)
        {
            CenterOnSelectedTile();
        }

        private void OnMenuViewNotesSelected(object sender, EventArgs e)
        {
            Window window = new()
            {
                Title = "Notes"
            };

            var content = new NoteList();

            foreach (var note in _sessionData.Notes.Values)
            {
                var item = new NoteListItem();
                item.LblNoteText.Text = $"{note.ToString()} - {note.Text}";
                item.BtnNoteCenter.Image = new TextureRegion(_location);
                item.BtnNoteView.Image = new TextureRegion(_eye);
                item.BtnNoteCenter.Click += (s, e) => { CenterOnTile(note.X, note.Y); };
                item.BtnNoteView.Click += (s, e) => { EditNote(note); window.Close(); };
                content.StackNotesList.AddChild(item);
            }

            window.Content = content;
            window.ShowModal(_desktop);
        }

        private void EditNote(Note note)
        {
            _state.SelectedNote = new Note { X = note.X, Y = note.Y, Text = note.Text };
            var noteWindow = new NoteWindow();

            Window window = new()
            {
                Title = $" Note on {note.X}:{note.Y}"
            };
            noteWindow.NoteText.Text = note.Text;
            noteWindow.BtnOk.Click += OnButtonNoteOkClick;
            noteWindow.BtnCancel.Click += OnButtonNoteCancelClick;

            window.Content = noteWindow;
            window.ShowModal(_desktop);
            noteWindow.NoteText.SetKeyboardFocus();
        }

        protected override void Update(GameTime gameTime)
        {
            KeyboardState newState = Keyboard.GetState();

            if (IsActive && GraphicsDevice.Viewport.Bounds.Contains(Mouse.GetState().Position) && !_desktop.IsMouseOverGUI && !_desktop.HasModalWidget)
            {

                var mouseState = Mouse.GetState();

                var screenPosition = new Point(mouseState.Position.X - (int)_viewportCenter.X, mouseState.Position.Y - (int)_viewportCenter.Y);

                _state.HoveredTile.X = screenPosition.X / _state.TileSize;
                _state.HoveredTile.Y = screenPosition.Y / _state.TileSize;
                if (screenPosition.X < 0)
                {
                    _state.HoveredTile.X--;
                }

                if (screenPosition.Y < 0)
                {
                    _state.HoveredTile.Y--;
                }

                if (_state.InsertMode == InsertMode.Wall)
                {
                    _state.SelectClosestWall(screenPosition);
                }

                if (_state.InsertMode == InsertMode.Overlay)
                {
                    _state.SelectOverlay(screenPosition);
                }

                // if (newState.IsKeyDown(Keys.LeftControl) && newState.IsKeyDown(Keys.C) && !oldState.IsKeyDown(Keys.C))
                // {
                //     CenterOnSelectedTile();
                // }

                if (!newState.IsKeyDown(Keys.LeftControl) && mouseState.LeftButton == ButtonState.Pressed && mouseState.LeftButton == oldMouseState.LeftButton)
                {
                    _viewportCenter = new Vector3(_viewportCenter.X + mouseState.Position.X - oldMouseState.Position.X, _viewportCenter.Y + mouseState.Position.Y - oldMouseState.Position.Y, 0);
                    if (mouseState.Position != oldMouseState.Position)
                    {
                        _isDraggin = true;
                    }
                }

                if (mouseState.LeftButton == ButtonState.Released && mouseState.LeftButton != oldMouseState.LeftButton)
                {
                    if (_isDraggin)
                    {
                        _isDraggin = false;
                    }
                    else
                    {
                        _state.SelectedTile.X = _state.HoveredTile.X;
                        _state.SelectedTile.Y = _state.HoveredTile.Y;
                        _communicationManager.Connection?.SendAsync("UpdatePosition", _sessionData.SessionName, _sessionData.SessionId, _state.SelectedTile);

                    }
                }

                if (mouseState.RightButton == ButtonState.Released && mouseState.RightButton != oldMouseState.RightButton)
                {
                    _state.SelectedNote.X = _state.HoveredTile.X;
                    _state.SelectedNote.Y = _state.HoveredTile.Y;

                    var popup = new VerticalStackPanel { Padding = new Myra.Graphics2D.Thickness(1), Spacing = 2, Background = new SolidBrush(Color.DarkGray) };

                    if (!_sessionData.Notes.ContainsKey(_state.SelectedNote.ToString()))
                    {
                        var newNoteButton = new TextButton { Text = "New Note", Width = 80, Height = 20, Padding = new Myra.Graphics2D.Thickness(2), HorizontalAlignment = HorizontalAlignment.Left };
                        newNoteButton.Click += OnContextMenuNewNoteClick;
                        popup.AddChild(newNoteButton);
                    }
                    else
                    {
                        _sessionData.Notes.TryGetValue(_state.SelectedNote.ToString(), out var n);
                        _state.SelectedNote = n;
                        var viewNoteButton = new TextButton { Text = "View Note", Width = 80, Height = 20, Padding = new Myra.Graphics2D.Thickness(2), HorizontalAlignment = HorizontalAlignment.Left };
                        var deleteNoteButton = new TextButton { Text = "Delete Note", Width = 80, Height = 20, Padding = new Myra.Graphics2D.Thickness(2), HorizontalAlignment = HorizontalAlignment.Left };
                        viewNoteButton.Click += OnContextMenuViewNoteClick;
                        deleteNoteButton.Click += OnContextMenuDeleteNoteClick;


                        popup.AddChild(viewNoteButton);
                        popup.AddChild(deleteNoteButton);
                    }

                    _desktop.ShowContextMenu(popup, mouseState.Position);
                }

                if (newState.IsKeyDown(Keys.LeftControl)
                && mouseState.LeftButton == ButtonState.Released
                && mouseState.LeftButton != oldMouseState.LeftButton)
                {
                    switch (_state.InsertMode)
                    {
                        case InsertMode.Tile:
                            _state.SelectedTile.X = _state.HoveredTile.X;
                            _state.SelectedTile.Y = _state.HoveredTile.Y;
                            _communicationManager.Connection?.SendAsync("UpdatePosition", _sessionData.SessionName, _sessionData.SessionId, _state.SelectedTile);

                            _sessionData.NewTile(_state.SelectedTile, _state.CurrentTileId);
                            break;

                        case InsertMode.Wall:
                            _sessionData.NewWall(_state.SelectedWall, _state.CurrentWallId);

                            break;
                        case InsertMode.Overlay:
                            _sessionData.NewOverlay(_state.SelectedOverlay, _state.CurrentOverlayId);
                            break;
                    }
                }

                if (newState.IsKeyDown(Keys.LeftControl) && mouseState.ScrollWheelValue != oldMouseState.ScrollWheelValue)
                {
                    // var center = new Point(GraphicsDevice.Viewport.Width / 2, GraphicsDevice.Viewport.Height / 2);
                    var center = new Point((Window.ClientBounds.Width + 200) / 2 - _state.TileSize / 2, Window.ClientBounds.Height / 2 - _state.TileSize / 2);

                    var tx = (center.X - (int)_viewportCenter.X) / _state.TileSize;
                    var ty = (center.Y - (int)_viewportCenter.Y) / _state.TileSize;

                    if (mouseState.ScrollWheelValue > oldMouseState.ScrollWheelValue)
                    {
                        _state.TileSize = Math.Min(120, _state.TileSize + 10);

                    }
                    else if (mouseState.ScrollWheelValue < oldMouseState.ScrollWheelValue)
                    {
                        _state.TileSize = Math.Max(10, _state.TileSize - 10);
                    }

                    CenterOnTile(tx, ty);
                }

                oldMouseState = mouseState;
            }


            if (newState.IsKeyDown(Keys.Delete))
            {
                switch (_state.InsertMode)
                {
                    case InsertMode.Tile:
                        _state.SelectedTile.X = _state.HoveredTile.X;
                        _state.SelectedTile.Y = _state.HoveredTile.Y;
                        _sessionData.DeleteTile(_state.SelectedTile);
                        break;
                    case InsertMode.Wall:
                        _sessionData.DeleteWall(_state.SelectedWall);
                        break;
                    case InsertMode.Overlay:
                        _sessionData.DeleteOverlay(_state.SelectedOverlay);
                        break;
                }
            }

            foreach (var key in newState.GetPressedKeys())
            {
                switch (key)
                {
                    case Keys.Left:
                        if (oldState.IsKeyUp(Keys.Left) && newState.IsKeyDown(Keys.Left))
                        {
                            _state.SelectedTile.X--;
                            _communicationManager.Connection?.SendAsync("UpdatePosition", _sessionData.SessionName, _state.SelectedTile);
                        }
                        break;
                    case Keys.Right:
                        if (oldState.IsKeyUp(Keys.Right) && newState.IsKeyDown(Keys.Right))
                        {
                            _state.SelectedTile.X++;
                            _communicationManager.Connection?.SendAsync("UpdatePosition", _sessionData.SessionName, _state.SelectedTile);
                        }
                        break;
                    case Keys.Up:
                        if (oldState.IsKeyUp(Keys.Up) && newState.IsKeyDown(Keys.Up))
                        {
                            _state.SelectedTile.Y--;
                            _communicationManager.Connection?.SendAsync("UpdatePosition", _sessionData.SessionName, _state.SelectedTile);
                        }
                        break;
                    case Keys.Down:
                        if (oldState.IsKeyUp(Keys.Down) && newState.IsKeyDown(Keys.Down))
                        {
                            _state.SelectedTile.Y++;
                            _communicationManager.Connection?.SendAsync("UpdatePosition", _sessionData.SessionName, _state.SelectedTile);
                        }
                        break;
                }

            }

            oldState = newState;

            base.Update(gameTime);
        }

        private void CenterOnSelectedTile()
        {
            CenterOnTile(_state.SelectedTile.X, _state.SelectedTile.Y);
        }

        private void CenterOnTile(int x, int y)
        {
            var center = new Point((Window.ClientBounds.Width + 200) / 2 - _state.TileSize / 2, Window.ClientBounds.Height / 2 - _state.TileSize / 2);
            var dx = center.X - x * _state.TileSize - _viewportCenter.X;
            var dy = center.Y - y * _state.TileSize - _viewportCenter.Y;
            _viewportCenter.X += dx;
            _viewportCenter.Y += dy;
        }

        protected override void Draw(GameTime gameTime)
        {
            if (_spriteBatch is null)
            {
                return;
            }
            GraphicsDevice.Clear(_settings.BackgroundColor);

            var visibleTilesX = GraphicsDevice.Viewport.Width / _state.TileSize + 1;
            var visibleTilesY = GraphicsDevice.Viewport.Height / _state.TileSize + 1;

            _spriteBatch.Begin(transformMatrix: Matrix.CreateTranslation(_viewportCenter));

            DrawTiles();

            DrawWalls();
            DrawOverlays();
            DrawNotes();
            DrawGrid(visibleTilesX, visibleTilesY);

            if (string.IsNullOrWhiteSpace(_sessionData.SessionName))
            {
                var isoffscreen = IsOffscreen(_state.SelectedTile);
                if (isoffscreen)
                {
                    var center = new Point((Window.ClientBounds.Width + 200) / 2 - (int)_viewportCenter.X, Window.ClientBounds.Height / 2 - (int)_viewportCenter.Y);

                    var cx = center.X / _state.TileSize;
                    var cy = center.Y / _state.TileSize;

                    var p1 = new Vector2(cx * _state.TileSize, cy * _state.TileSize);
                    var p2 = new Vector2(_state.SelectedTile.X * _state.TileSize, _state.SelectedTile.Y * _state.TileSize);

                    var p3 = new Vector2(Window.ClientBounds.Width - _viewportCenter.X, 0 - _viewportCenter.Y);
                    var p4 = new Vector2(Window.ClientBounds.Width - _viewportCenter.X, Window.ClientBounds.Height - _viewportCenter.Y);
                    var ua1 = ((p4.X - p3.X) * (p1.Y - p3.Y) - (p4.Y - p3.Y) * (p1.X - p3.X)) / ((p4.Y - p3.Y) * (p2.X - p1.X) - (p4.X - p3.X) * (p2.Y - p1.Y));

                    p3 = new Vector2(200 - _viewportCenter.X, 0 - _viewportCenter.Y);
                    p4 = new Vector2(Window.ClientBounds.Width - _viewportCenter.X, 0 - _viewportCenter.Y);
                    var ua2 = ((p4.X - p3.X) * (p1.Y - p3.Y) - (p4.Y - p3.Y) * (p1.X - p3.X)) / ((p4.Y - p3.Y) * (p2.X - p1.X) - (p4.X - p3.X) * (p2.Y - p1.Y));

                    p3 = new Vector2(200 - _viewportCenter.X, 0 - _viewportCenter.Y);
                    p4 = new Vector2(200 - _viewportCenter.X, Window.ClientBounds.Height - _viewportCenter.Y);
                    var ua3 = ((p4.X - p3.X) * (p1.Y - p3.Y) - (p4.Y - p3.Y) * (p1.X - p3.X)) / ((p4.Y - p3.Y) * (p2.X - p1.X) - (p4.X - p3.X) * (p2.Y - p1.Y));

                    p3 = new Vector2(200 - _viewportCenter.X, Window.ClientBounds.Height - _viewportCenter.Y);
                    p4 = new Vector2(Window.ClientBounds.Width - _viewportCenter.X, Window.ClientBounds.Height - _viewportCenter.Y);
                    var ua4 = ((p4.X - p3.X) * (p1.Y - p3.Y) - (p4.Y - p3.Y) * (p1.X - p3.X)) / ((p4.Y - p3.Y) * (p2.X - p1.X) - (p4.X - p3.X) * (p2.Y - p1.Y));

                    var uas = new List<float> { ua1, ua2, ua3, ua4 };
                    if (uas.Any(u => u > 0 && u < 1))
                    {
                        var ua = uas.Where(u => u > 0 && u < 1).Min();

                        var i = uas.IndexOf(ua);
                        var x = (p1.X + ua * (p2.X - p1.X));// + _viewportCenter.X;
                        var y = (p1.Y + ua * (p2.Y - p1.Y));// + _viewportCenter.Y;

                        switch (i)
                        {
                            case 0:
                                _spriteBatch.DrawPolygon(Vector2.Zero, new Vector2[] { new Vector2(x, y), new Vector2(x - 20, y + 10), new Vector2(x - 20, y - 10), new Vector2(x, y) }, Color.Red, 2);
                                break;
                            case 1:
                                y += 20;
                                _spriteBatch.DrawPolygon(Vector2.Zero, new Vector2[] { new Vector2(x, y), new Vector2(x - 10, y + 20), new Vector2(x + 10, y + 20), new Vector2(x, y) }, Color.Red, 2);
                                break;
                            case 2:
                                x += 0;
                                _spriteBatch.DrawPolygon(Vector2.Zero, new Vector2[] { new Vector2(x, y), new Vector2(x + 20, y + 10), new Vector2(x + 20, y - 10), new Vector2(x, y) }, Color.Red, 2);
                                break;
                            case 3:
                                y -= 20;
                                _spriteBatch.DrawPolygon(Vector2.Zero, new Vector2[] { new Vector2(x, y), new Vector2(x - 10, y - 20), new Vector2(x + 10, y - 20), new Vector2(x, y) }, Color.Red, 2);
                                break;
                        }
                    }
                }


                _spriteBatch.DrawRectangle(new Rectangle((_state.SelectedTile.X * _state.TileSize) - 2, (_state.SelectedTile.Y * _state.TileSize) - 2, _state.TileSize + 3, _state.TileSize + 3), Color.Red, 2);
            }

            DrawPlayers();

            var startWall = new Vector2(_state.SelectedWall.X * _state.TileSize, _state.SelectedWall.Y * _state.TileSize);
            if (_state.InsertMode == InsertMode.Wall)
            {
                _spriteBatch.DrawLine(startWall, _state.TileSize, MathHelper.ToRadians(90 * _state.SelectedWall.Rotation), Color.Red, 2);
            }

            var overlay = new Vector2(_state.SelectedOverlay.X * _state.TileSize, _state.SelectedOverlay.Y * _state.TileSize);
            if (_state.InsertMode == InsertMode.Overlay && _state.SelectedOverlay.Intersection)
            {
                _spriteBatch.DrawCircle(overlay, _state.TileSize / 3f, 100, Color.Red, 2);
            }

            _spriteBatch.End();

            try
            {
                _desktop?.Render();
            }
            catch
            {
                System.Console.WriteLine("Desktop render error");
            }
            base.Draw(gameTime);
        }

        private void DrawGrid(int visibleTilesX, int visibleTilesY)
        {
            for (var i = -1; i < visibleTilesX + 2; i++)
            {
                var posX1 = i * _state.TileSize - _viewportCenter.X;
                var posY1 = -_viewportCenter.Y;
                posX1 -= posX1 % _state.TileSize;
                posY1 -= posY1 % _state.TileSize;
                var posX2 = i * _state.TileSize - _viewportCenter.X;
                var posY2 = GraphicsDevice.Viewport.Height - _viewportCenter.Y;
                posX2 -= posX2 % _state.TileSize;
                posY2 -= posY2 % _state.TileSize;

                _spriteBatch.DrawLine(
                   posX1, posY1,
                    posX2,
                    posY2,
                    _settings.GridColor);
            }

            for (var i = -1; i < visibleTilesY + 2; i++)
            {
                var posX1 = -_viewportCenter.X;
                var posY1 = i * _state.TileSize - _viewportCenter.Y;
                posX1 -= posX1 % _state.TileSize;
                posY1 -= posY1 % _state.TileSize;
                var posX2 = GraphicsDevice.Viewport.Width - _viewportCenter.X;
                var posY2 = i * _state.TileSize - _viewportCenter.Y;
                posX2 -= posX2 % _state.TileSize;
                posY2 -= posY2 % _state.TileSize;

                _spriteBatch.DrawLine(posX1, posY1,
                    posX2,
                    posY2,
                 _settings.GridColor);
            }


            if (_showCellNumbers && _state.TileSize >= 30)
            {
                var ffont = _fonts.FirstOrDefault(m => int.Parse(m.Key.Replace("font", "")) > _state.TileSize / 8).Value ?? _fonts.Last().Value;
                var fscale = 1f;

                for (var i = -1; i < visibleTilesX + 2; i++)
                {
                    for (var j = -1; j < visibleTilesY + 2; j++)
                    {
                        var screenPosition = new Point(i * _state.TileSize - (int)_viewportCenter.X, j * _state.TileSize - (int)_viewportCenter.Y);

                        var x = screenPosition.X / _state.TileSize;
                        var y = screenPosition.Y / _state.TileSize;

                        _spriteBatch.DrawString(ffont,
                        $"{x}:{y}",
                        new Vector2(
                            x * _state.TileSize + 2,
                            y * _state.TileSize + 2
                            ),
                        Color.Black,
                        0,
                        Vector2.Zero,
                        fscale,
                        SpriteEffects.None,
                        0);
                    }
                }
            }
        }

        private void DrawNotes()
        {
            if (_state.TileSize < 30)
            {
                return;
            }

            foreach (var note in _sessionData.Notes.Values)
            {
                _spriteBatch.Draw(
                    _comment,
                    new Rectangle(
                    note.X * _state.TileSize + _state.TileSize - (int)(_state.TileSize / 2) + _state.TileSize / 25,
                    note.Y * _state.TileSize + _state.TileSize / 8 + _state.TileSize / 25,
                    (int)(_state.TileSize / 2.5), (int)(_state.TileSize / 2.5 / 1.136)
                    ), Color.Black * .2f
                    );

                _spriteBatch.Draw(
                    _comment,
                    new Rectangle(
                    note.X * _state.TileSize + _state.TileSize - (int)(_state.TileSize / 2),
                    note.Y * _state.TileSize + _state.TileSize / 8,
                    (int)(_state.TileSize / 2.5), (int)(_state.TileSize / 2.5 / 1.136)
                    ), _settings.NoteColor
                    );
            }
        }

        private void DrawOverlays()
        {
            foreach (var tile in _sessionData.Overlays.Values)
            {
                var content = Content.Load<Texture2D>($"overlays/{tile.ID}");
                float posX;
                float posY;

                if (tile.Intersection)
                {
                    posX = tile.X * _state.TileSize;
                    posY = tile.Y * _state.TileSize;

                    _spriteBatch.Draw(content, new Vector2(posX + _state.TileSize / 25, posY + _state.TileSize / 25), null, Color.Black * .2f, MathHelper.ToRadians(90 * tile.Rotation), new Vector2(content.Width / 2, content.Height / 2), ((float)_state.TileSize - 10) / content.Width, SpriteEffects.None, 0);
                    _spriteBatch.Draw(content, new Vector2(posX, posY), null, _settings.OverlayTintColor, MathHelper.ToRadians(90 * tile.Rotation), new Vector2(content.Width / 2, content.Height / 2), ((float)_state.TileSize - 10) / content.Width, SpriteEffects.None, 0);
                }
                else
                {
                    posX = tile.X * _state.TileSize + _state.TileSize / 2f;
                    posY = tile.Y * _state.TileSize + _state.TileSize / 2f;
                }
                _spriteBatch.Draw(content, new Vector2(posX + _state.TileSize / 25, posY + _state.TileSize / 25), null, Color.Black * .2f, MathHelper.ToRadians(90 * tile.Rotation), new Vector2(content.Width / 2, content.Height / 2), ((float)_state.TileSize - 10) / content.Width, SpriteEffects.None, 0);
                _spriteBatch.Draw(content, new Vector2(posX, posY), null, _settings.OverlayTintColor, MathHelper.ToRadians(90 * tile.Rotation), new Vector2(content.Width / 2, content.Height / 2), ((float)_state.TileSize - 10) / content.Width, SpriteEffects.None, 0);
            }
        }

        private void DrawPlayers()
        {
            foreach (var player in _sessionData.Players.Copy())
            {
                var color = player.Color.ToColor();
                _spriteBatch.DrawRectangle(new Rectangle(player.Position.X * _state.TileSize - 4, player.Position.Y * _state.TileSize - 4, _state.TileSize + 7, _state.TileSize + 7), color, 2);
                var ffont = _fonts.FirstOrDefault(m => int.Parse(m.Key.Replace("font", "")) > _state.TileSize).Value ?? _fonts.Last().Value;
                var fscale = _state.TileSize / ((float)ffont.LineSpacing * 2);
                _spriteBatch.DrawString(ffont,
                player.Initials,
                new Vector2(player.Position.X * _state.TileSize + 2, player.Position.Y * _state.TileSize + _state.TileSize - 2 - ffont.LineSpacing * fscale),
                color,
                0,
                Vector2.Zero,
                fscale,
                SpriteEffects.None,
                0);
            }

            foreach (var player in _sessionData.Players.Copy())
            {
                var isOffscreen = IsOffscreen(player.Position);
                if (isOffscreen)
                {
                    DrawPlayerPointer(player);
                }
            }
        }

        private void DrawPlayerPointer(Player player)
        {
            var center = new Point((Window.ClientBounds.Width + 200) / 2 - (int)_viewportCenter.X, Window.ClientBounds.Height / 2 - (int)_viewportCenter.Y);

            var cx = center.X / _state.TileSize;
            var cy = center.Y / _state.TileSize;

            var p1 = new Vector2(cx * _state.TileSize, cy * _state.TileSize);
            var p2 = new Vector2(player.Position.X * _state.TileSize, player.Position.Y * _state.TileSize);

            var p3 = new Vector2(Window.ClientBounds.Width - _viewportCenter.X, 0 - _viewportCenter.Y);
            var p4 = new Vector2(Window.ClientBounds.Width - _viewportCenter.X, Window.ClientBounds.Height - _viewportCenter.Y);
            var ua1 = ((p4.X - p3.X) * (p1.Y - p3.Y) - (p4.Y - p3.Y) * (p1.X - p3.X)) / ((p4.Y - p3.Y) * (p2.X - p1.X) - (p4.X - p3.X) * (p2.Y - p1.Y));

            p3 = new Vector2(200 - _viewportCenter.X, 0 - _viewportCenter.Y);
            p4 = new Vector2(Window.ClientBounds.Width - _viewportCenter.X, 0 - _viewportCenter.Y);
            var ua2 = ((p4.X - p3.X) * (p1.Y - p3.Y) - (p4.Y - p3.Y) * (p1.X - p3.X)) / ((p4.Y - p3.Y) * (p2.X - p1.X) - (p4.X - p3.X) * (p2.Y - p1.Y));

            p3 = new Vector2(200 - _viewportCenter.X, 0 - _viewportCenter.Y);
            p4 = new Vector2(200 - _viewportCenter.X, Window.ClientBounds.Height - _viewportCenter.Y);
            var ua3 = ((p4.X - p3.X) * (p1.Y - p3.Y) - (p4.Y - p3.Y) * (p1.X - p3.X)) / ((p4.Y - p3.Y) * (p2.X - p1.X) - (p4.X - p3.X) * (p2.Y - p1.Y));

            p3 = new Vector2(200 - _viewportCenter.X, Window.ClientBounds.Height - _viewportCenter.Y);
            p4 = new Vector2(Window.ClientBounds.Width - _viewportCenter.X, Window.ClientBounds.Height - _viewportCenter.Y);
            var ua4 = ((p4.X - p3.X) * (p1.Y - p3.Y) - (p4.Y - p3.Y) * (p1.X - p3.X)) / ((p4.Y - p3.Y) * (p2.X - p1.X) - (p4.X - p3.X) * (p2.Y - p1.Y));

            var uas = new List<float> { ua1, ua2, ua3, ua4 };
            if (uas.Any(u => u > 0 && u < 1))
            {
                var ua = uas.Where(u => u > 0 && u < 1).Min();
                var i = uas.IndexOf(ua);
                var x = (p1.X + ua * (p2.X - p1.X));// + _viewportCenter.X;
                var y = (p1.Y + ua * (p2.Y - p1.Y));// + _viewportCenter.Y;
                Vector2[] vertexes = new Vector2[0];
                switch (i)
                {
                    case 0:
                        vertexes = new Vector2[] { new Vector2(x, y), new Vector2(x - 20, y + 10), new Vector2(x - 20, y - 10), new Vector2(x, y) };
                        break;
                    case 1:
                        y += 20;
                        vertexes = new Vector2[] { new Vector2(x, y), new Vector2(x - 10, y + 20), new Vector2(x + 10, y + 20), new Vector2(x, y) };
                        break;
                    case 2:
                        x += 0;
                        vertexes = new Vector2[] { new Vector2(x, y), new Vector2(x + 20, y + 10), new Vector2(x + 20, y - 10), new Vector2(x, y) };
                        break;
                    case 3:
                        y -= 20;
                        vertexes = new Vector2[] { new Vector2(x, y), new Vector2(x - 10, y - 20), new Vector2(x + 10, y - 20), new Vector2(x, y) };
                        break;
                }
                _spriteBatch.DrawPolygon(Vector2.Zero, vertexes, player.Color.ToColor(), 2);
            }
        }

        private bool IsOffscreen(Tile position)
        {
            var visibleTilesX = GraphicsDevice.Viewport.Width / _state.TileSize + 1;
            var visibleTilesY = GraphicsDevice.Viewport.Height / _state.TileSize + 1;

            var screenPositionTopLeft = new Point(200 - _state.TileSize + 0 * _state.TileSize - (int)_viewportCenter.X, 0 * _state.TileSize + _state.TileSize - (int)_viewportCenter.Y);
            var screenPositionBottomRight = new Point(visibleTilesX * _state.TileSize - (int)_viewportCenter.X, visibleTilesY * _state.TileSize - 20 - (int)_viewportCenter.Y);
            var tileTopLeft = new Point(screenPositionTopLeft.X / _state.TileSize, screenPositionTopLeft.Y / _state.TileSize);
            var tileBottomRight = new Point(screenPositionBottomRight.X / _state.TileSize, screenPositionBottomRight.Y / _state.TileSize);

            if (position.X < tileTopLeft.X || position.Y < tileTopLeft.Y || position.X > tileBottomRight.X || position.Y > tileBottomRight.Y)
            {
                return true;
            }
            return false;

        }

        private void DrawTiles()
        {
            foreach (var tile in _sessionData.Map.Values)
            {
                var content = Content.Load<Texture2D>($"tiles/{tile.ID}");

                var posX = tile.X * _state.TileSize + _state.TileSize / 2f;
                var posY = tile.Y * _state.TileSize + _state.TileSize / 2f;

                _spriteBatch.Draw(content, new Vector2(posX, posY),
                null, Color.White, MathHelper.ToRadians(90 * tile.Rotation), new Vector2(content.Width / 2, content.Height / 2), ((float)_state.TileSize) / content.Width, SpriteEffects.None, 0);
            }
        }

        private void DrawWalls()
        {
            foreach (var wall in _sessionData.Walls.Values)
            {
                var content = Content.Load<Texture2D>($"walls/{wall.ID}");
                var scale = _state.TileSize / (float)content.Height;
                var offset = scale * content.Width / 2f;
                var posX = wall.X * _state.TileSize;
                var posY = wall.Y * _state.TileSize;
                if (wall.Rotation == 1)
                {
                    posX -= (int)offset;
                }
                else if (wall.Rotation == 0)
                {
                    posY += (int)offset;
                }
                _spriteBatch.Draw(content, new Vector2(posX, posY), null, Color.White, MathHelper.ToRadians(90 * (wall.Rotation - 1)), new Vector2(0, 0), scale, SpriteEffects.None, 0);
            }
        }

        private async void OnButtonJoinSessionClicked(object sender, EventArgs e)
        {
            Container container = ((TextButton)sender).Parent;
            while (!(container is Window))
            {
                container = container.Parent;
            }
            var localWindow = (Window)container;
            var localContent = localWindow.Content as SessionWindow;
            var isValid = ValidateTextbox(localContent.TxtSession);
            if (!isValid)
            {
                return;
            }

            if (_communicationManager.Connection.State != HubConnectionState.Connected)
            {
                _mainWidget.lblConnectionStatus.Text = "Connecting";
                await _communicationManager.Connection.StartAsync();
                UpdateConnectionState(_communicationManager.Connection);
            }

            var successful = false;
            try
            {
                var result = await _communicationManager.Connection?.InvokeAsync<Session>("JoinSession", localContent.TxtSession.Text);
                if (result != null)
                {
                    _sessionData.Map = result.Map;
                    _sessionData.Walls = result.Walls;
                    _sessionData.Overlays = result.Overlays;

                    _sessionData.MapEntityAdded -= OnMapEntityAdded;
                    _sessionData.MapEntityDeleted -= OnMapEntityDeleted;
                    _sessionData.MapEntityAdded += OnMapEntityAdded;
                    _sessionData.MapEntityDeleted += OnMapEntityDeleted;
                    _sessionData.SessionId = result.SessionId;
                    _sessionData.SessionName = result.SessionName;
                }
                successful = result != null;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

            if (successful)
            {
                _sessionData.SessionName = localContent.TxtSession.Text;
                _mainWidget.lblSessionName.Text = _sessionData.SessionName;
                localWindow.Close();
            }
        }

        private async void OnButtonNewSessionClicked(object sender, EventArgs e)
        {
            Container container = ((TextButton)sender).Parent;
            while (!(container is Window))
            {
                container = container.Parent;
            }
            var localWindow = (Window)container;
            var localContent = localWindow.Content as SessionWindow;
            var isValid = ValidateTextbox(localContent.TxtSession);
            if (!isValid)
            {
                return;
            }

            if (_communicationManager.Connection.State != HubConnectionState.Connected)
            {
                _mainWidget.lblConnectionStatus.Text = "Connecting";
                await _communicationManager.Connection.StartAsync();
                UpdateConnectionState(_communicationManager.Connection);
            }

            var successful = false;
            try
            {
                var result = await _communicationManager.Api.NewSession(localContent.TxtSession.Text);

                if (result)
                {
                    _sessionData.SessionName = localContent.TxtSession.Text;
                    _sessionData.MapEntityAdded -= OnMapEntityAdded;
                    _sessionData.MapEntityDeleted -= OnMapEntityDeleted;
                    _sessionData.MapEntityAdded += OnMapEntityAdded;
                    _sessionData.MapEntityDeleted += OnMapEntityDeleted;
                }
                successful = result;
                var result2 = await _communicationManager.Connection?.InvokeAsync<Session>("JoinSession", localContent.TxtSession.Text);
                _sessionData.SessionId = result2.SessionId;
                _sessionData.SessionName = localContent.TxtSession.Text;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

            if (successful)
            {
                _sessionData.SessionName = localContent.TxtSession.Text;
                _communicationManager.SessionData.Map = _sessionData.Map;
                _communicationManager.SessionData.Overlays = _sessionData.Overlays;
                _communicationManager.SessionData.Walls = _sessionData.Walls;
                _mainWidget.lblSessionName.Text = _sessionData.SessionName;

                localWindow.Close();
            }
        }

        private void UpdateConnectionState(HubConnection connection)
        {
            switch (connection.State)
            {
                case HubConnectionState.Connected:
                    _mainWidget.lblConnectionStatus.Text = "Connected";
                    break;
                case HubConnectionState.Connecting:
                    _mainWidget.lblConnectionStatus.Text = "Connecting";
                    break;
                case HubConnectionState.Disconnected:
                    _mainWidget.lblConnectionStatus.Text = "Disconnected";

                    break;
                case HubConnectionState.Reconnecting:
                    _mainWidget.lblConnectionStatus.Text = "Reconnecting";

                    break;
            }
        }

        private async void OnButtonNoteOkClick(object sender, EventArgs e)
        {
            var button = ((TextButton)sender);
            Container container = button.Parent;
            while (!(container is Window))
            {
                container = container.Parent;
            }

            var localWindow = (Window)container;
            var localContent = localWindow.Content as NoteWindow;
            var note = new Note
            {
                X = _state.SelectedNote.X,
                Y = _state.SelectedNote.Y,
                Text = localContent.NoteText.Text
            };
            _sessionData.NewNote(note);
            localWindow.Close();
        }

        private async void OnButtonNoteCancelClick(object sender, EventArgs e)
        {
            var button = ((TextButton)sender);
            Container container = button.Parent;
            while (!(container is Window))
            {
                container = container.Parent;
            }

            var localWindow = (Window)container;

            localWindow.Close();
        }
        private async void OnButtonLoginClick(object sender, EventArgs e)
        {
            var button = ((TextButton)sender);
            Container container = button.Parent;
            while (!(container is Window))
            {
                container = container.Parent;
            }

            var localWindow = (Window)container;
            var localContent = localWindow.Content as LoginRegisterWindow;

            if (!button.Enabled)
            {
                return;
            }

            var isValid = true;
            isValid &= ValidateTextbox(localContent.TxtEmail);
            isValid &= ValidateTextbox(localContent.TxtPassword);

            if (!isValid)
            {
                localContent.LblLoginError.Text = "Username or password is not valid";
                localContent.LblLoginError.Visible = true;

                return;
            }

            var successful = false;
            try
            {
                button.Text = "Wait...";
                localContent.LblLoginError.Text = "";
                localContent.LblLoginError.Visible = false;
                _authResponse = await _communicationManager.Login(new AuthenticateModel
                {
                    Username = localContent.TxtEmail.Text,
                    Password = localContent.TxtPassword.Text
                });
                successful = _authResponse != null;
            }
            catch (Refit.ApiException refitException)
            {
                localContent.LblLoginError.Text = refitException.Content;
                localContent.LblLoginError.Visible = true;
            }
            catch (Exception ex)
            {
                localContent.LblLoginError.Text = "Can't connect to the server";
                localContent.LblLoginError.Visible = true;
            }
            finally
            {
                button.Enabled = true;
                button.Text = "Login";
            }

            if (successful)
            {
                _mainWidget.MenuConnectNew.Enabled = true;
                _mainWidget.MenuConnectJoin.Enabled = true;
                _mainWidget.MenuConnectSync.Enabled = true;
                _mainWidget.MenuConnectUpload.Enabled = true;
                _mainWidget.lblUsername.Text = $"{_authResponse.Username} - {_authResponse.Initials}";
                localWindow.Close();
            }
        }

        private async void OnButtonRegisterClick(object sender, EventArgs e)
        {
            var button = ((TextButton)sender);
            Container container = button.Parent;
            while (!(container is Window))
            {
                container = container.Parent;
            }

            var localWindow = (Window)container;
            var localContent = localWindow.Content as LoginRegisterWindow;

            if (!button.Enabled)
            {
                return;
            }

            var isValid = true;
            isValid &= ValidateTextbox(localContent.TxtEmail);
            isValid &= ValidateTextbox(localContent.TxtPassword);
            isValid &= ValidateTextbox(localContent.TxtFirstname);
            isValid &= ValidateTextbox(localContent.TxtLastname);
            isValid &= ValidateTextbox(localContent.TxtInitials);

            if (!isValid)
            {
                localContent.LblLoginError.Text = "Please complete all the fields";
                localContent.LblLoginError.Visible = true;
                return;
            }

            var successful = false;
            try
            {
                button.Text = "Wait...";
                localContent.LblLoginError.Text = "";
                localContent.LblLoginError.Visible = false;

                var result = await _communicationManager.Register(new RegisterModel
                {
                    Username = localContent.TxtEmail.Text,
                    Password = localContent.TxtPassword.Text,
                    FirstName = localContent.TxtFirstname.Text,
                    LastName = localContent.TxtLastname.Text,
                    Initials = localContent.TxtInitials.Text
                });
                if (result.IsSuccessStatusCode)
                {
                    _authResponse = await _communicationManager.Login(new AuthenticateModel
                    {
                        Username = localContent.TxtEmail.Text,
                        Password = localContent.TxtPassword.Text
                    });
                    successful = true;
                }
                else
                {
                    localContent.LblLoginError.Text = result.ReasonPhrase;
                    localContent.LblLoginError.Visible = true;
                }
            }
            catch (Refit.ApiException refitException)
            {
                localContent.LblLoginError.Text = refitException.Content;
                localContent.LblLoginError.Visible = true;
            }
            catch (Exception ex)
            {
                localContent.LblLoginError.Text = "Can't connect to the server";
                localContent.LblLoginError.Visible = true;
            }
            finally
            {
                button.Enabled = true;
                button.Text = "Register";
            }
            if (successful)
            {
                _mainWidget.MenuConnectNew.Enabled = true;
                _mainWidget.MenuConnectJoin.Enabled = true;
                _mainWidget.MenuConnectSync.Enabled = true;
                _mainWidget.MenuConnectUpload.Enabled = true;

                _mainWidget.lblUsername.Text = $"{_authResponse.Username} - {_authResponse.Initials}";

                localWindow.Close();
            }
        }

        private void OnContextMenuNewNoteClick(object sender, EventArgs e)
        {
            _desktop.HideContextMenu();
            var noteWindow = new NoteWindow();

            Window window = new()
            {
                Title = $" Note on {_state.SelectedTile.X}:{_state.SelectedTile.Y}"
            };

            noteWindow.BtnOk.Click += OnButtonNoteOkClick;
            noteWindow.BtnCancel.Click += OnButtonNoteCancelClick;
            window.Content = noteWindow;
            window.ShowModal(_desktop);
            noteWindow.NoteText.SetKeyboardFocus();
        }

        private void OnContextMenuDeleteNoteClick(object sender, EventArgs e)
        {
            _desktop.HideContextMenu();
            _sessionData.DeleteNote(_state.SelectedNote);
        }

        private void OnContextMenuViewNoteClick(object sender, EventArgs e)
        {
            _desktop.HideContextMenu();
            EditNote(_state.SelectedNote);
        }

        private void OnOverlayButtonClicked(object sender, EventArgs e)
        {
            _state.CurrentOverlayId = ((ImageButton)sender).Id;
            _mainWidget.ClearSelection();
            ((ImageButton)sender).Border = new SolidBrush(Color.Red);
            ((ImageButton)sender).BorderThickness = new Myra.Graphics2D.Thickness(2);
            _state.InsertMode = InsertMode.Overlay;
        }

        private void OnTileButtonClicked(object sender, EventArgs e)
        {
            _state.CurrentTileId = ((ImageButton)sender).Id;
            _mainWidget.ClearSelection();
            ((ImageButton)sender).Border = new SolidBrush(Color.Red);
            ((ImageButton)sender).BorderThickness = new Myra.Graphics2D.Thickness(2);
            _state.InsertMode = InsertMode.Tile;
        }

        private void OnWallButtonClicked(object sender, EventArgs e)
        {
            _state.CurrentWallId = ((ImageButton)sender).Id;
            _mainWidget.ClearSelection();
            ((ImageButton)sender).Border = new SolidBrush(Color.Red);
            ((ImageButton)sender).BorderThickness = new Myra.Graphics2D.Thickness(2);
            _state.InsertMode = InsertMode.Wall;
        }

        private void OnMenuConnectJoinSelected(object sender, EventArgs e)
        {
            if (!((MenuItem)sender).Enabled)
            {
                return;
            }

            Window window = new()
            {
                Title = "Join mapping session"
            };

            var content = new SessionWindow();
            content.BtnLogin.Text = "Join";
            content.BtnLogin.Click += OnButtonJoinSessionClicked;

            window.Content = content;

            window.ShowModal(_desktop);
            content.TxtSession.SetKeyboardFocus();
        }

        private void OnMenuConnectLoginSelected(object sender, EventArgs e)
        {
            Window window = new()
            {
                Title = "Login"
            };

            var content = new LoginRegisterWindow();
            content.RdoLogin.IsPressed = true;
            content.RdoLogin.Click += (s, e) =>
            {
                content.TxtFirstname.Visible = false;
                content.TxtLastname.Visible = false;
                content.TxtInitials.Visible = false;
                content.LblFirstname.Visible = false;
                content.LblLastname.Visible = false;
                content.LblInitials.Visible = false;
                content.BtnLogin.Visible = true;
                content.BtnRegister.Visible = false;
                window.Title = "Login";
            };

            content.RdoRegister.Click += (s, e) =>
            {
                content.TxtFirstname.Visible = true;
                content.TxtLastname.Visible = true;
                content.TxtInitials.Visible = true;
                content.LblFirstname.Visible = true;
                content.LblLastname.Visible = true;
                content.LblInitials.Visible = true;
                content.BtnLogin.Visible = false;
                content.BtnRegister.Visible = true;
                window.Title = "Register";
            };

            content.BtnRegister.Click += OnButtonRegisterClick;
            content.BtnLogin.Click += OnButtonLoginClick;
            
            window.Content = content;
            window.ShowModal(_desktop);
            content.TxtEmail.SetKeyboardFocus();
        }

        private async void OnMenuConnectSyncSelected(object sender, EventArgs e)
        {
            if (!((MenuItem)sender).Enabled)
            {
                return;
            }

            var serverMap = await _communicationManager.Api.Session(_sessionData.SessionName);
            _sessionData.Overlays = serverMap.Overlays;
            _sessionData.Map = serverMap.Map;
            _sessionData.Walls = serverMap.Walls;
        }

        private async void OnMenuConnectUploadSelected(object sender, EventArgs e)
        {
            if (!((MenuItem)sender).Enabled)
            {
                return;
            }

            await _communicationManager.Api.SaveSnapshot(_sessionData, _sessionData.SessionName);
        }

        private void OnMenuConnectNewSelected(object sender, EventArgs e)
        {
            if (!((MenuItem)sender).Enabled)
            {
                return;
            }

            Window window = new()
            {
                Title = "New mapping session"
            };

            var content = new SessionWindow();
            content.BtnLogin.Text = "Join";
            content.BtnLogin.Click += OnButtonNewSessionClicked;
            window.Content = content;

            window.ShowModal(_desktop);
            content.TxtSession.SetKeyboardFocus();
        }

        private void OnMenuFileSaveSelected(object sender, EventArgs e)
        {
            FileDialog dialog = new(FileDialogMode.SaveFile)
            {
                Filter = "*.map"
            };

            dialog.Closed += (s, a) =>
            {
                if (!dialog.Result)
                {
                    return;
                }

                using StreamWriter file = File.CreateText(dialog.FilePath);
                JsonSerializer serializer = new();
                serializer.Serialize(file, _sessionData);
            };

            dialog.ShowModal(_desktop);
        }

        private void OnMenuFileLoadSelected(object sender, EventArgs e)
        {
            var dialog = new FileDialog(FileDialogMode.OpenFile)
            {
                Filter = "*.map"
            };

            dialog.Closed += (s, a) =>
            {
                if (!dialog.Result)
                {
                    return;
                }
                using StreamReader file = File.OpenText(dialog.FilePath);
                JsonSerializer serializer = new();
                var loadData = (Session)serializer.Deserialize(file, typeof(Session));
                _sessionData.Map = loadData.Map;
                _sessionData.Overlays = loadData.Overlays;
                _sessionData.Walls = loadData.Walls;
            };

            dialog.ShowModal(_desktop);
        }

        private void OnMapEntityAdded(object sender, MapEntityAddedEventArgs e)
        {
            _communicationManager.Enqueue(e.MapEntity, TileAction.Add);
        }

        private void OnMapEntityDeleted(object sender, MapEntityDeletedEventArgs e)
        {
            _communicationManager.Enqueue(e.MapEntity, TileAction.Delete);
        }

        private bool ValidateTextbox(TextBox textBox)
        {
            var valid = !string.IsNullOrWhiteSpace(textBox.Text);
            if (!valid)
            {
                textBox.Background = new SolidBrush(Color.Red);
            }
            else
            {
                textBox.Background = new SolidBrush(new Color(51, 51, 51)); ;
            }
            return valid;

        }
    }
}
