#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using Content.Server.GameObjects.Components.Power.ApcNetComponents;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Utility;
using Content.Shared.Arcade;
using Content.Shared.GameObjects;
using Content.Shared.GameObjects.EntitySystems.ActionBlocker;
using Content.Shared.Interfaces.GameObjects.Components;
using Robust.Server.GameObjects;
using Robust.Server.Player;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Random;

namespace Content.Server.GameObjects.Components.Arcade
{
    [RegisterComponent]
    [ComponentReference(typeof(IActivate))]
    public class BlockGameArcadeComponent : Component, IActivate
    {
        [Dependency] private readonly IRobustRandom _random = default!;

        public override string Name => "BlockGameArcade";
        public override uint? NetID => ContentNetIDs.BLOCKGAME_ARCADE;

        [ComponentDependency] private readonly PowerReceiverComponent? _powerReceiverComponent = default!;

        private bool Powered => _powerReceiverComponent?.Powered ?? false;
        private BoundUserInterface? UserInterface => Owner.GetUIOrNull(BlockGameUiKey.Key);

        private BlockGame? _game;

        private IPlayerSession? _player;
        private readonly List<IPlayerSession> _spectators = new();

        public override void HandleMessage(ComponentMessage message, IComponent? component)
        {
            base.HandleMessage(message, component);
            switch (message)
            {
                case PowerChangedMessage powerChanged:
                    OnPowerStateChanged(powerChanged);
                    break;
            }
        }

        void IActivate.Activate(ActivateEventArgs eventArgs)
        {
            if(!eventArgs.User.TryGetComponent(out ActorComponent? actor))
            {
                return;
            }
            if (!Powered)
            {
                return;
            }
            if(!ActionBlockerSystem.CanInteract(actor.PlayerSession.AttachedEntity)) return;

            UserInterface?.Toggle(actor.PlayerSession);
            RegisterPlayerSession(actor.PlayerSession);
        }

        private void RegisterPlayerSession(IPlayerSession session)
        {
            if (_player == null) _player = session;
            else _spectators.Add(session);

            UpdatePlayerStatus(session);
            _game?.UpdateNewPlayerUI(session);
        }

        private void DeactivePlayer(IPlayerSession session)
        {
            if (_player != session) return;

            var temp = _player;
            _player = null;
            if (_spectators.Count != 0)
            {
                _player = _spectators[0];
                _spectators.Remove(_player);
                UpdatePlayerStatus(_player);
            }
            _spectators.Add(temp);

            UpdatePlayerStatus(temp);
        }

        private void UnRegisterPlayerSession(IPlayerSession session)
        {
            if (_player == session)
            {
                DeactivePlayer(_player);
            }
            else
            {
                _spectators.Remove(session);
                UpdatePlayerStatus(session);
            }
        }

        private void UpdatePlayerStatus(IPlayerSession session)
        {
            UserInterface?.SendMessage(new BlockGameMessages.BlockGameUserStatusMessage(_player == session), session);
        }

        public override void Initialize()
        {
            base.Initialize();
            if (UserInterface != null)
            {
                UserInterface.OnReceiveMessage += UserInterfaceOnOnReceiveMessage;
                UserInterface.OnClosed += UnRegisterPlayerSession;
            }
            _game = new BlockGame(this);
        }

        private void OnPowerStateChanged(PowerChangedMessage e)
        {
            if (e.Powered) return;

            UserInterface?.CloseAll();
            _player = null;
            _spectators.Clear();
        }

        private void UserInterfaceOnOnReceiveMessage(ServerBoundUserInterfaceMessage obj)
        {
            switch (obj.Message)
            {
                case BlockGameMessages.BlockGamePlayerActionMessage playerActionMessage:
                    if (obj.Session != _player) break;

                    if (!ActionBlockerSystem.CanInteract(Owner))
                    {
                        DeactivePlayer(obj.Session);
                        break;
                    }

                    if (playerActionMessage.PlayerAction == BlockGamePlayerAction.NewGame)
                    {
                        if(_game?.Started == true) _game = new BlockGame(this);
                        _game?.StartGame();
                    }
                    else
                    {
                        _game?.ProcessInput(playerActionMessage.PlayerAction);
                    }

                    break;
            }
        }

        public void DoGameTick(float frameTime)
        {
            _game?.GameTick(frameTime);
        }

        private class BlockGame
        {
            //note: field is 10(0 -> 9) wide and 20(0 -> 19) high

            private readonly BlockGameArcadeComponent _component;

            private readonly List<BlockGameBlock> _field = new();

            private BlockGamePiece _currentPiece;

            private BlockGamePiece _nextPiece
            {
                get => _internalNextPiece;
                set
                {
                    _internalNextPiece = value;
                    SendNextPieceUpdate();
                }
            }
            private BlockGamePiece _internalNextPiece;

            private void SendNextPieceUpdate()
            {
                _component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameVisualUpdateMessage(_nextPiece.BlocksForPreview(), BlockGameMessages.BlockGameVisualType.NextBlock));
            }

            private void SendNextPieceUpdate(IPlayerSession session)
            {
                _component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameVisualUpdateMessage(_nextPiece.BlocksForPreview(), BlockGameMessages.BlockGameVisualType.NextBlock), session);
            }

            private bool _holdBlock = false;
            private BlockGamePiece? _heldPiece
            {
                get => _internalHeldPiece;
                set
                {
                    _internalHeldPiece = value;
                    SendHoldPieceUpdate();
                }
            }

            private BlockGamePiece? _internalHeldPiece = null;

            private void SendHoldPieceUpdate()
            {
                if(_heldPiece.HasValue) _component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameVisualUpdateMessage(_heldPiece.Value.BlocksForPreview(), BlockGameMessages.BlockGameVisualType.HoldBlock));
                else _component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameVisualUpdateMessage(new BlockGameBlock[0], BlockGameMessages.BlockGameVisualType.HoldBlock));
            }

            private void SendHoldPieceUpdate(IPlayerSession session)
            {
                if(_heldPiece.HasValue) _component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameVisualUpdateMessage(_heldPiece.Value.BlocksForPreview(), BlockGameMessages.BlockGameVisualType.HoldBlock), session);
                else _component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameVisualUpdateMessage(new BlockGameBlock[0], BlockGameMessages.BlockGameVisualType.HoldBlock), session);
            }

            private Vector2i _currentPiecePosition;
            private BlockGamePieceRotation _currentRotation;
            private float _softDropModifier = 0.1f;

            private float Speed =>
                -0.03f * Level + 1 * (!_softDropPressed ? 1 : _softDropModifier);

            private const float _pressCheckSpeed = 0.08f;

            private bool _running;
            public bool Paused => !(_running && _started);
            private bool _started;
            public bool Started => _started;
            private bool _gameOver;

            private bool _leftPressed;
            private bool _rightPressed;
            private bool _softDropPressed;

            private int Points
            {
                get => _internalPoints;
                set
                {
                    if (_internalPoints == value) return;
                    _internalPoints = value;
                    SendPointsUpdate();
                }
            }
            private int _internalPoints;

            private BlockGameSystem.HighScorePlacement? _highScorePlacement = null;

            private void SendPointsUpdate()
            {
                _component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameScoreUpdateMessage(Points));
            }

            private void SendPointsUpdate(IPlayerSession session)
            {
                _component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameScoreUpdateMessage(Points));
            }

            public int Level
            {
                get => _level;
                set
                {
                    _level = value;
                    SendLevelUpdate();
                }
            }
            private int _level = 0;
            private void SendLevelUpdate()
            {
                _component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameLevelUpdateMessage(Level));
            }

            private void SendLevelUpdate(IPlayerSession session)
            {
                _component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameLevelUpdateMessage(Level));
            }

            private int ClearedLines
            {
                get => _clearedLines;
                set
                {
                    _clearedLines = value;

                    if (_clearedLines < LevelRequirement) return;

                    _clearedLines -= LevelRequirement;
                    Level++;
                }
            }

            private int _clearedLines = 0;
            private int LevelRequirement => Math.Min(100, Math.Max(Level * 10 - 50, 10));

            public BlockGame(BlockGameArcadeComponent component)
            {
                _component = component;
                _allBlockGamePieces = (BlockGamePieceType[]) Enum.GetValues(typeof(BlockGamePieceType));
                _internalNextPiece = GetRandomBlockGamePiece(_component._random);
                InitializeNewBlock();
            }

            private void SendHighscoreUpdate()
            {
                var entitySystem = EntitySystem.Get<BlockGameSystem>();
                _component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameHighScoreUpdateMessage(entitySystem.GetLocalHighscores(), entitySystem.GetGlobalHighscores()));
            }

            private void SendHighscoreUpdate(IPlayerSession session)
            {
                var entitySystem = EntitySystem.Get<BlockGameSystem>();
                _component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameHighScoreUpdateMessage(entitySystem.GetLocalHighscores(), entitySystem.GetGlobalHighscores()), session);
            }

            public void StartGame()
            {
                _component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameSetScreenMessage(BlockGameMessages.BlockGameScreen.Game));

                FullUpdate();

                _running = true;
                _started = true;
            }

            private void FullUpdate()
            {
                UpdateAllFieldUI();
                SendHoldPieceUpdate();
                SendNextPieceUpdate();
                SendPointsUpdate();
                SendHighscoreUpdate();
                SendLevelUpdate();
            }

            private void FullUpdate(IPlayerSession session)
            {
                UpdateFieldUI(session);
                SendPointsUpdate(session);
                SendNextPieceUpdate(session);
                SendHoldPieceUpdate(session);
                SendHighscoreUpdate(session);
                SendLevelUpdate(session);
            }

            public void GameTick(float frameTime)
            {
                if (!_running) return;

                InputTick(frameTime);

                FieldTick(frameTime);
            }

            private float _accumulatedLeftPressTime;
            private float _accumulatedRightPressTime;
            private void InputTick(float frameTime)
            {
                bool anythingChanged = false;
                if (_leftPressed)
                {
                    _accumulatedLeftPressTime += frameTime;

                    while (_accumulatedLeftPressTime >= _pressCheckSpeed)
                    {

                        if (_currentPiece.Positions(_currentPiecePosition.AddToX(-1), _currentRotation)
                            .All(MoveCheck))
                        {
                            _currentPiecePosition = _currentPiecePosition.AddToX(-1);
                            anythingChanged = true;
                        }

                        _accumulatedLeftPressTime -= _pressCheckSpeed;
                    }
                }

                if (_rightPressed)
                {
                    _accumulatedRightPressTime += frameTime;

                    while (_accumulatedRightPressTime >= _pressCheckSpeed)
                    {
                        if (_currentPiece.Positions(_currentPiecePosition.AddToX(1), _currentRotation)
                            .All(MoveCheck))
                        {
                            _currentPiecePosition = _currentPiecePosition.AddToX(1);
                            anythingChanged = true;
                        }

                        _accumulatedRightPressTime -= _pressCheckSpeed;
                    }
                }

                if(anythingChanged) UpdateAllFieldUI();
            }

            private float _accumulatedFieldFrameTime;
            private void FieldTick(float frameTime)
            {
                _accumulatedFieldFrameTime += frameTime;

                var checkTime = Speed;

                while (_accumulatedFieldFrameTime >= checkTime)
                {
                    if (_softDropPressed) AddPoints(1);

                    InternalFieldTick();

                    _accumulatedFieldFrameTime -= checkTime;
                }
            }

            private void InternalFieldTick()
            {
                if (_currentPiece.Positions(_currentPiecePosition.AddToY(1), _currentRotation)
                    .All(DropCheck))
                {
                    _currentPiecePosition = _currentPiecePosition.AddToY(1);
                }
                else
                {
                    var blocks = _currentPiece.Blocks(_currentPiecePosition, _currentRotation);
                    _field.AddRange(blocks);

                    //check loose conditions
                    if (IsGameOver)
                    {
                        InvokeGameover();
                        return;
                    }

                    InitializeNewBlock();
                }

                CheckField();

                UpdateAllFieldUI();
            }

            private void CheckField()
            {
                int pointsToAdd = 0;
                int consecutiveLines = 0;
                int clearedLines = 0;
                for (int y = 0; y < 20; y++)
                {
                    if (CheckLine(y))
                    {
                        //line was cleared
                        y--;
                        consecutiveLines++;
                        clearedLines++;
                    }
                    else if(consecutiveLines != 0)
                    {
                        var mod = consecutiveLines switch
                        {
                            1 => 40,
                            2 => 100,
                            3 => 300,
                            4 => 1200,
                            _ => 0
                        };
                        pointsToAdd += mod * (_level + 1);
                    }
                }

                ClearedLines += clearedLines;
                AddPoints(pointsToAdd);
            }

            private bool CheckLine(int y)
            {
                for (var x = 0; x < 10; x++)
                {
                    if (!_field.Any(b => b.Position.X == x && b.Position.Y == y)) return false;
                }

                //clear line
                _field.RemoveAll(b => b.Position.Y == y);
                //move everything down
                FillLine(y);

                return true;
            }

            private void AddPoints(int amount)
            {
                if (amount == 0) return;

                Points += amount;
            }

            private void FillLine(int y)
            {
                for (int c_y = y; c_y > 0; c_y--)
                {
                    for (int j = 0; j < _field.Count; j++)
                    {
                        if(_field[j].Position.Y != c_y-1) continue;

                        _field[j] = new BlockGameBlock(_field[j].Position.AddToY(1), _field[j].GameBlockColor);
                    }
                }
            }

            private void InitializeNewBlock()
            {
                InitializeNewBlock(_nextPiece);
                _nextPiece = GetRandomBlockGamePiece(_component._random);
                _holdBlock = false;

                _component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameVisualUpdateMessage(_nextPiece.BlocksForPreview(), BlockGameMessages.BlockGameVisualType.NextBlock));
            }

            private void InitializeNewBlock(BlockGamePiece piece)
            {
                _currentPiecePosition = new Vector2i(5,0);

                _currentRotation = BlockGamePieceRotation.North;

                _currentPiece = piece;
                UpdateAllFieldUI();
            }

            private bool LowerBoundCheck(Vector2i position) => position.Y < 20;
            private bool BorderCheck(Vector2i position) => position.X >= 0 && position.X < 10;
            private bool ClearCheck(Vector2i position) => _field.All(block => !position.Equals(block.Position));

            private bool DropCheck(Vector2i position) => LowerBoundCheck(position) && ClearCheck(position);
            private bool MoveCheck(Vector2i position) => BorderCheck(position) && ClearCheck(position);
            private bool RotateCheck(Vector2i position) => BorderCheck(position) && LowerBoundCheck(position) && ClearCheck(position);

            public void ProcessInput(BlockGamePlayerAction action)
            {
                if (_running)
                {
                    switch (action)
                    {
                        case BlockGamePlayerAction.StartLeft:
                            _leftPressed = true;
                            break;
                        case BlockGamePlayerAction.StartRight:
                            _rightPressed = true;
                            break;
                        case BlockGamePlayerAction.Rotate:
                            TrySetRotation(Next(_currentRotation, false));
                            break;
                        case BlockGamePlayerAction.CounterRotate:
                            TrySetRotation(Next(_currentRotation, true));
                            break;
                        case BlockGamePlayerAction.SoftdropStart:
                            _softDropPressed = true;
                            if (_accumulatedFieldFrameTime > Speed) _accumulatedFieldFrameTime = Speed; //to prevent jumps
                            break;
                        case BlockGamePlayerAction.Harddrop:
                            PerformHarddrop();
                            break;
                        case BlockGamePlayerAction.Hold:
                            HoldPiece();
                            break;
                    }
                }

                switch (action)
                {
                    case BlockGamePlayerAction.EndLeft:
                        _leftPressed = false;
                        break;
                    case BlockGamePlayerAction.EndRight:
                        _rightPressed = false;
                        break;
                    case BlockGamePlayerAction.SoftdropEnd:
                        _softDropPressed = false;
                        break;
                    case BlockGamePlayerAction.Pause:
                        _running = false;
                        _component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameSetScreenMessage(BlockGameMessages.BlockGameScreen.Pause, _started));
                        break;
                    case BlockGamePlayerAction.Unpause:
                        if (!_gameOver && _started)
                        {
                            _running = true;
                            _component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameSetScreenMessage(BlockGameMessages.BlockGameScreen.Game));
                        }
                        break;
                    case BlockGamePlayerAction.ShowHighscores:
                        _running = false;
                        _component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameSetScreenMessage(BlockGameMessages.BlockGameScreen.Highscores, _started));
                        break;
                }
            }

            private void TrySetRotation(BlockGamePieceRotation rotation)
            {
                if(!_running) return;

                if (!_currentPiece.CanSpin) return;

                if (!_currentPiece.Positions(_currentPiecePosition, rotation)
                    .All(RotateCheck))
                {
                    return;
                }

                _currentRotation = rotation;
                UpdateAllFieldUI();
            }

            private void HoldPiece()
            {
                if (!_running) return;

                if (_holdBlock) return;

                var tempHeld = _heldPiece;
                _heldPiece = _currentPiece;
                _holdBlock = true;

                if (!tempHeld.HasValue)
                {
                    InitializeNewBlock();
                    return;
                }

                InitializeNewBlock(tempHeld.Value);
            }

            private void PerformHarddrop()
            {
                int spacesDropped = 0;
                while (_currentPiece.Positions(_currentPiecePosition.AddToY(1), _currentRotation)
                    .All(DropCheck))
                {
                    _currentPiecePosition = _currentPiecePosition.AddToY(1);
                    spacesDropped++;
                }
                AddPoints(spacesDropped * 2);

                InternalFieldTick();
            }

            public void UpdateAllFieldUI()
            {
                if (!_started) return;

                var computedField = ComputeField();
                _component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameVisualUpdateMessage(computedField.ToArray(), BlockGameMessages.BlockGameVisualType.GameField));
            }

            public void UpdateFieldUI(IPlayerSession session)
            {
                if (!_started) return;

                var computedField = ComputeField();
                _component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameVisualUpdateMessage(computedField.ToArray(), BlockGameMessages.BlockGameVisualType.GameField), session);
            }

            private bool IsGameOver => _field.Any(block => block.Position.Y == 0);

            private void InvokeGameover()
            {
                _running = false;
                _gameOver = true;

                if (_component._player?.AttachedEntity != null)
                {
                    var blockGameSystem = EntitySystem.Get<BlockGameSystem>();

                    _highScorePlacement = blockGameSystem.RegisterHighScore(_component._player.AttachedEntity.Name, Points);
                    SendHighscoreUpdate();
                }
                _component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameGameOverScreenMessage(Points, _highScorePlacement?.LocalPlacement, _highScorePlacement?.GlobalPlacement));
            }

            public void UpdateNewPlayerUI(IPlayerSession session)
            {
                if (_gameOver)
                {
                    _component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameGameOverScreenMessage(Points, _highScorePlacement?.LocalPlacement, _highScorePlacement?.GlobalPlacement), session);
                }
                else
                {
                    if (Paused)
                    {
                        _component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameSetScreenMessage(BlockGameMessages.BlockGameScreen.Pause, Started), session);
                    }
                    else
                    {
                        _component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameSetScreenMessage(BlockGameMessages.BlockGameScreen.Game, Started), session);
                    }
                }

                FullUpdate(session);
            }

            public List<BlockGameBlock> ComputeField()
            {
                var result = new List<BlockGameBlock>();
                result.AddRange(_field);
                result.AddRange(_currentPiece.Blocks(_currentPiecePosition, _currentRotation));
                return result;
            }

            private enum BlockGamePieceType
            {
                I,
                L,
                LInverted,
                S,
                SInverted,
                T,
                O
            }

            private enum BlockGamePieceRotation
            {
                North,
                East,
                South,
                West
            }

            private static BlockGamePieceRotation Next(BlockGamePieceRotation rotation, bool inverted)
            {
                return rotation switch
                {
                    BlockGamePieceRotation.North => inverted ? BlockGamePieceRotation.West : BlockGamePieceRotation.East,
                    BlockGamePieceRotation.East => inverted ? BlockGamePieceRotation.North : BlockGamePieceRotation.South,
                    BlockGamePieceRotation.South => inverted ? BlockGamePieceRotation.East : BlockGamePieceRotation.West,
                    BlockGamePieceRotation.West => inverted ? BlockGamePieceRotation.South : BlockGamePieceRotation.North,
                    _ => throw new ArgumentOutOfRangeException(nameof(rotation), rotation, null)
                };
            }

            private readonly BlockGamePieceType[] _allBlockGamePieces;

            private List<BlockGamePieceType> _blockGamePiecesBuffer = new();

            private BlockGamePiece GetRandomBlockGamePiece(IRobustRandom random)
            {
                if (_blockGamePiecesBuffer.Count == 0)
                {
                    _blockGamePiecesBuffer = _allBlockGamePieces.ToList();
                }

                var chosenPiece = random.Pick(_blockGamePiecesBuffer);
                _blockGamePiecesBuffer.Remove(chosenPiece);
                return BlockGamePiece.GetPiece(chosenPiece);
            }

            private struct BlockGamePiece
            {
                public Vector2i[] Offsets;
                private BlockGameBlock.BlockGameBlockColor _gameBlockColor;
                public bool CanSpin;

                public Vector2i[] Positions(Vector2i center,
                    BlockGamePieceRotation rotation)
                {
                    return RotatedOffsets(rotation).Select(v => center + v).ToArray();
                }

                private Vector2i[] RotatedOffsets(BlockGamePieceRotation rotation)
                {
                    Vector2i[] rotatedOffsets = (Vector2i[])Offsets.Clone();
                    //until i find a better algo
                    var amount = rotation switch
                    {
                        BlockGamePieceRotation.North => 0,
                        BlockGamePieceRotation.East => 1,
                        BlockGamePieceRotation.South => 2,
                        BlockGamePieceRotation.West => 3,
                        _ => 0
                    };

                    for (var i = 0; i < amount; i++)
                    {
                        for (var j = 0; j < rotatedOffsets.Length; j++)
                        {
                            rotatedOffsets[j] = rotatedOffsets[j].Rotate90DegreesAsOffset();
                        }
                    }

                    return rotatedOffsets;
                }

                public BlockGameBlock[] Blocks(Vector2i center,
                    BlockGamePieceRotation rotation)
                {
                    var positions = Positions(center, rotation);
                    var result = new BlockGameBlock[positions.Length];
                    var i = 0;
                    foreach (var position in positions)
                    {
                        result[i++] = position.ToBlockGameBlock(_gameBlockColor);
                    }

                    return result;
                }

                public BlockGameBlock[] BlocksForPreview()
                {
                    var xOffset = 0;
                    var yOffset = 0;
                    foreach (var offset in Offsets)
                    {
                        if (offset.X < xOffset) xOffset = offset.X;
                        if (offset.Y < yOffset) yOffset = offset.Y;
                    }

                    return Blocks(new Vector2i(-xOffset, -yOffset), BlockGamePieceRotation.North);
                }

                public static BlockGamePiece GetPiece(BlockGamePieceType type)
                {
                    //switch statement, hardcoded offsets
                    return type switch
                    {
                        BlockGamePieceType.I => new BlockGamePiece
                        {
                            Offsets = new[]
                            {
                                new Vector2i(0, -1), new Vector2i(0, 0), new Vector2i(0, 1), new Vector2i(0, 2),
                            },
                            _gameBlockColor = BlockGameBlock.BlockGameBlockColor.LightBlue,
                            CanSpin = true
                        },
                        BlockGamePieceType.L => new BlockGamePiece
                        {
                            Offsets = new[]
                            {
                                new Vector2i(0, -1), new Vector2i(0, 0), new Vector2i(0, 1), new Vector2i(1, 1),
                            },
                            _gameBlockColor = BlockGameBlock.BlockGameBlockColor.Orange,
                            CanSpin = true
                        },
                        BlockGamePieceType.LInverted => new BlockGamePiece
                        {
                            Offsets = new[]
                            {
                                new Vector2i(0, -1), new Vector2i(0, 0), new Vector2i(-1, 1),
                                new Vector2i(0, 1),
                            },
                            _gameBlockColor = BlockGameBlock.BlockGameBlockColor.Blue,
                            CanSpin = true
                        },
                        BlockGamePieceType.S => new BlockGamePiece
                        {
                            Offsets = new[]
                            {
                                new Vector2i(0, -1), new Vector2i(1, -1), new Vector2i(-1, 0),
                                new Vector2i(0, 0),
                            },
                            _gameBlockColor = BlockGameBlock.BlockGameBlockColor.Green,
                            CanSpin = true
                        },
                        BlockGamePieceType.SInverted => new BlockGamePiece
                        {
                            Offsets = new[]
                            {
                                new Vector2i(-1, -1), new Vector2i(0, -1), new Vector2i(0, 0),
                                new Vector2i(1, 0),
                            },
                            _gameBlockColor = BlockGameBlock.BlockGameBlockColor.Red,
                            CanSpin = true
                        },
                        BlockGamePieceType.T => new BlockGamePiece
                        {
                            Offsets = new[]
                            {
                                new Vector2i(0, -1),
                                new Vector2i(-1, 0), new Vector2i(0, 0), new Vector2i(1, 0),
                            },
                            _gameBlockColor = BlockGameBlock.BlockGameBlockColor.Purple,
                            CanSpin = true
                        },
                        BlockGamePieceType.O => new BlockGamePiece
                        {
                            Offsets = new[]
                            {
                                new Vector2i(0, -1), new Vector2i(1, -1), new Vector2i(0, 0),
                                new Vector2i(1, 0),
                            },
                            _gameBlockColor = BlockGameBlock.BlockGameBlockColor.Yellow,
                            CanSpin = false
                        },
                        _ => new BlockGamePiece {Offsets = new[] {new Vector2i(0, 0)}}
                    };
                }
            }
        }
    }
}
