using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using testexetrisathlon.SoundManagement;
using static System.Console;

//┌─┐
//│ │
//└─┘

//          ▀▀▀██████▄▄▄
//                 ▀▀▀████▄
//          ▄███████▀   ▀███▄
//        ▄███████▀       ▀███▄
//      ▄████████           ███▄
//     ██████████▄           ███▌
//     ▀█████▀ ▀███▄         ▐███
//       ▀█▀     ▀███▄       ▐███
//                 ▀███▄     ███▌
//    ▄██▄           ▀███▄  ▐███
//  ▄██████▄           ▀███▄███
// █████▀▀████▄▄        ▄█████
// ████▀   ▀▀█████▄▄▄▄█████████▄
//  ▀▀         ▀▀██████▀▀   ▀▀██

namespace testexetrisathlon
{
    internal static class Program
    {
        public const string Sqr = "■";
        private const string Intro = "Intro";
        private const string GameOver = "GameOver";
        public static int[,] Grid = new int[23, 10];
        public static int[,] DroppedTetrominoeLocationGrid = new int[23, 10];
        private static Stopwatch _dropTimer = new Stopwatch();
        private static int _dropTime;
        private static int _dropRate = 300;
        public static bool IsDropped;
        private static Tetrominoe _tet;
        private static Tetrominoe _nextTet;
        private static ConsoleKeyInfo _key;
        private static bool _isKeyPressed;
        private static int _linesCleared;
        private static int _score;
        private static int _level = 1;
        private static readonly Assembly Assembly = Assembly.GetExecutingAssembly();
        private static readonly ConsoleColor[] Colors = {BackgroundColor, ForegroundColor};
        public static bool Debug;

        public static readonly Random Rnd = new Random();
        private static ISoundManager _soundManager;
        public static IBeeper Beeper;
        private static string InGame => SettingsMan.UsingAltTrack ? "InGame2" : "InGame1";

#if DEBUG
        private static void Main()
        {
            Debug = true;
#else
        private static void Main(string[] args)
        {
            Debug = args.Contains("debug");
#endif
            Beeper = new Beeper();
            BackgroundColor = ConsoleColor.Red;
            ForegroundColor = ConsoleColor.Yellow;
            _soundManager = new SoundManager();
            _soundManager.Init(new Dictionary<string, string>
            {
                {"Intro", "testexetrisathlon.Intro.mp3"},
                {"InGame1", "testexetrisathlon.InGame1.mp3"},
                {"InGame2", "testexetrisathlon.InGame2.mp3"},
                {"GameOver", "testexetrisathlon.GameOver.mp3"}
            });
            SizeSetter.SetWindowSize(42, 29);
            if (Debug)
                SizeSetter.SetWindowSize(50, 40);
            SetCursorPosition(0, 0);
            bool playing = true;
            GameState state = GameState.Menu;
            try
            {
                while (playing)
                    switch (state)
                    {
                        case GameState.Menu:
                            Clear();
                            _soundManager.SetCurrent(Intro);
                            DrawSymbol();
                            SetCursorPosition(12, 18);
                            Write("HighScore: " + SettingsMan.HighScore);
                            SetCursorPosition(12, 20);
                            Write("Controls: Space");
                            SetCursorPosition(13, 21);
                            Write("Up, Down, Right");
                            SetCursorPosition(13, 22);
                            Write("Left");
                            SetCursorPosition(12, 24);
                            Write("Press s to start");
                            SetCursorPosition(12, 25);
                            Write("Press x to exit");
                            SetCursorPosition(12, 26);
                            Write("Press v for settings");
                            SetCursorPosition(0, 28);
                            Write("Icon made by Freepik from www.flaticon.com");
                            string tmp = ReadKey(true).KeyChar.ToString().ToLower();
                            switch (tmp)
                            {
                                case "s":
                                    state = GameState.Game;
                                    Clear();
                                    DrawBorder();
                                    break;
                                case "x":
                                    state = GameState.Exit;
                                    break;
                                case "v":
                                    SettingsMenu();
                                    break;
                            }
                            break;
                        case GameState.Game:
                            _soundManager.SetCurrent(InGame);
                            _dropTimer.Start();
                            SetCursorPosition(25, 0);
                            WriteLine("Level " + _level);
                            SetCursorPosition(25, 1);
                            WriteLine("Score " + _score + "/" + (Math.Pow(_level, 2) * 100));
                            SetCursorPosition(25, 2);
                            WriteLine("LinesCleared " + _linesCleared);
                            SetCursorPosition(25, 4);
                            WriteLine("HighScore " + SettingsMan.HighScore);
                            _nextTet = new Tetrominoe();
                            _tet = _nextTet;
                            _tet.Spawn();
                            _nextTet = new Tetrominoe();
                            Update();
                            state = GameState.GameOver;
                            break;
                        case GameState.GameOver:
                            SettingsMan.HighScore = _score;
                            _soundManager.SetCurrent(GameOver);
                            string input = "";
                            while (input != "y" && input != "n")
                            {
                                Clear();
                                DrawBorder();
                                Draw();
                                SetCursorPosition(0, 0);
                                WriteLine("┌───────────────────┐");
                                WriteLine("│     Game Over     │");
                                WriteLine("│   Replay? (Y/N)   │");
                                WriteLine("├───────────────────┤");
                                input = ReadKey().KeyChar.ToString().ToLower();
                            }
                            Grid = new int[23, 10];
                            DroppedTetrominoeLocationGrid = new int[23, 10];
                            _dropTimer = new Stopwatch();
                            _dropRate = 300;
                            IsDropped = false;
                            _isKeyPressed = false;
                            _linesCleared = 0;
                            _score = 0;
                            _level = 1;
                            GC.Collect();
                            Clear();
                            DrawBorder();
                            state = input == "y" ? GameState.Game : GameState.Menu;
                            break;
                        case GameState.Exit:
                            playing = false;
                            break;
                        default: throw new ArgumentOutOfRangeException();
                    }
            }
            finally
            {
                _soundManager.Dispose();
            }
            BackgroundColor = Colors[0];
            ForegroundColor = Colors[1];
            SetCursorPosition(0, 0);
            Clear();
            Beeper.Dispose();
        }

        private static void DrawSymbol()
        {
            SetCursorPosition(0, 1);
            Write(
                "               ▀▀▀██████▄▄▄\r\n" +
                "                      ▀▀▀████▄\r\n" +
                "               ▄███████▀   ▀███▄\r\n" +
                "             ▄███████▀       ▀███▄\r\n" +
                "           ▄████████           ███▄\r\n" +
                "          ██████████▄           ███▌\r\n" +
                "          ▀█████▀ ▀███▄         ▐███\r\n" +
                "            ▀█▀     ▀███▄       ▐███\r\n" +
                "                      ▀███▄     ███▌\r\n" +
                "         ▄██▄           ▀███▄  ▐███\r\n" +
                "       ▄██████▄           ▀███▄███\r\n" +
                "      █████▀▀████▄▄        ▄█████\r\n" +
                "      ████▀   ▀▀█████▄▄▄▄█████████▄\r\n" +
                "       ▀▀         ▀▀██████▀▀   ▀▀██\r\n\r\n" +
                "     testexetrisathlon v." + Assembly.GetName().Version);
        }

        private static void SettingsMenu()
        {
            Clear();
            DrawSymbol();
            bool barActive = true;
            int currentSetting = 0;
            while (barActive)
            {
                bool curr = SettingsMan.UsingAltTrack;
                SetCursorPosition(3, 20);
                ForegroundColor = currentSetting == 0 ? ConsoleColor.White : ConsoleColor.Yellow;
                Write("Volume: " + new string('=', SettingsMan.Volume * 2) + "[" + SettingsMan.Volume.ToString("00") +
                      "]" + new string('=', 20 - (SettingsMan.Volume * 2)));
                SetCursorPosition(5, 22);
                ForegroundColor = currentSetting == 1 ? ConsoleColor.White : ConsoleColor.Yellow;
                Write($"{(curr ? "  Using" : "Not using")} alternative soundtrack  ");
                ForegroundColor = ConsoleColor.Yellow;
                switch (currentSetting)
                {
                    case 0:
                        switch (ReadKey().Key)
                        {
                            case ConsoleKey.LeftArrow:
                                SettingsMan.Volume--;
                                break;
                            case ConsoleKey.RightArrow:
                                SettingsMan.Volume++;
                                break;
                            case ConsoleKey.Enter:
                                barActive = false;
                                break;
                            case ConsoleKey.Tab:
                                currentSetting = 1;
                                break;
                        }
                        break;
                    case 1:
                        switch (ReadKey().Key)
                        {
                            case ConsoleKey.LeftArrow:
                            case ConsoleKey.RightArrow:
                            case ConsoleKey.Spacebar:
                                SettingsMan.UsingAltTrack = !curr;
                                break;
                            case ConsoleKey.Enter:
                                barActive = false;
                                break;
                            case ConsoleKey.Tab:
                                currentSetting = 0;
                                break;
                        }
                        break;
                }
                _soundManager.SetVolume(SettingsMan.Volume * 10);
            }
        }

        private static void Update()
        {
            while (true)
            {
                _dropTime = (int) _dropTimer.ElapsedMilliseconds;
                if (_dropTime > _dropRate)
                {
                    _dropTime = 0;
                    _dropTimer.Restart();
                    _tet.Drop();
                }
                if (IsDropped)
                {
                    _tet = _nextTet;
                    _nextTet = new Tetrominoe();
                    _tet.Spawn();
                    IsDropped = false;
                    _score += 10;
                }
                for (int j = 0; j < 10; j++)
                    if (DroppedTetrominoeLocationGrid[0, j] == 1)
                        return;
                if (Debug)
                {
                    SetCursorPosition(0, 25);
                    WriteLine("!DEBUG MODE ENABLED!");
                }
                Input();
                ClearBlock();
            }
        }

        private static void ClearBlock()
        {
            int combo = 0;
            for (int i = 0; i < 23; i++)
                if (Enumerable.Range(0, 10).All(s => DroppedTetrominoeLocationGrid[i, s] != 0))
                {
                    _linesCleared++;
                    combo++;
                    Beeper.Beep(400, 200);
                    for (int j = 0; j < 10; j++) DroppedTetrominoeLocationGrid[i, j] = 0;
                    int[,] newDroppedTetrominoeLocationGrid = new int[23, 10];
                    for (int k = 1; k < i; k++)
                    for (int l = 0; l < 10; l++)
                        newDroppedTetrominoeLocationGrid[k + 1, l] = DroppedTetrominoeLocationGrid[k, l];
                    for (int k = 1; k < i; k++)
                    for (int l = 0; l < 10; l++)
                        DroppedTetrominoeLocationGrid[k, l] = 0;
                    for (int k = 0; k < 23; k++)
                    for (int l = 0; l < 10; l++)
                        if (newDroppedTetrominoeLocationGrid[k, l] == 1)
                            DroppedTetrominoeLocationGrid[k, l] = 1;
                    Draw();
                }
            _score += (int) Math.Round(Math.Sqrt(Math.Max((combo * 50) - 50, 0)) * 5);
            _level = (int) Math.Round(Math.Sqrt(_score * 0.01)) + 1;
            _dropRate = 300 - (22 * _level);
        }

        private static void Input()
        {
            _isKeyPressed = KeyAvailable;
            SetCursorPosition(0, 24);
            if (_isKeyPressed)
                _key = ReadKey();
            if ((_key.Key == ConsoleKey.LeftArrow) & !_tet.IsSomethingLeft() & _isKeyPressed)
            {
                for (int i = 0; i < 4; i++)
                    _tet.Location[i][1] -= 1;
                _tet.Update();
            }
            else if ((_key.Key == ConsoleKey.RightArrow) & !_tet.IsSomethingRight() & _isKeyPressed)
            {
                for (int i = 0; i < 4; i++)
                    _tet.Location[i][1] += 1;
                _tet.Update();
            }
            if ((_key.Key == ConsoleKey.DownArrow) & _isKeyPressed)
                _tet.Drop();
            if ((_key.Key == ConsoleKey.UpArrow) & _isKeyPressed)
                for (; _tet.IsSomethingBelow != true;)
                    _tet.Drop();
            if (!((_key.Key == ConsoleKey.Spacebar) & _isKeyPressed)) return;
            _tet.Rotate();
            _tet.Update();
        }

        public static void Draw()
        {
            for (int i = 0; i < 23; ++i)
            for (int j = 0; j < 10; j++)
            {
                SetCursorPosition((2 * j) + 1, i);
                if ((Grid[i, j] == 1) | (DroppedTetrominoeLocationGrid[i, j] == 1))
                {
                    SetCursorPosition((2 * j) + 1, i);
                    Write(Sqr);
                }
                else
                {
                    Write(" ");
                }
            }
            SetCursorPosition(25, 0);
            WriteLine("Level " + _level);
            SetCursorPosition(25, 1);
            WriteLine("Score " + _score + "/" + (Math.Pow(_level, 2) * 100));
            SetCursorPosition(25, 2);
            WriteLine("LinesCleared " + _linesCleared);
            DrawBorder();
        }

        public static void DrawBorder()
        {
            for (int lengthCount = 0; lengthCount <= 22; lengthCount++)
            {
                SetCursorPosition(0, lengthCount);
                Write("│");
                SetCursorPosition(20, lengthCount);
                Write("│");
            }
            SetCursorPosition(0, 23);
            Write("└");
            for (int widthCount = 0; widthCount <= 18; widthCount++) Write("─");
            Write("┘");
        }

        private enum GameState
        {
            Exit,
            Menu,
            Game,
            GameOver
        }
    }
}