/*
    Ingus Zemturis
    Gupa: 110
    Compiler: Visual Studio
*/
using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Diagnostics.Eventing.Reader;
using System.Runtime.InteropServices;
using System.Threading;

namespace PLAYGROUND
{
    internal class Program
    {
        // -- GLOBALIE -- //
        static List<int> TOP = new List<int>()    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
        static List<int> MIDDLE = new List<int>() { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
        static List<int> BOTTOM = new List<int>() { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

        static Stopwatch clock = new Stopwatch();
        static Random rand = new Random();

        // Misc
        static int RightSide = TOP.Count - 1;


        // Timers
        static double DeltaTime = 0.0f; // 4 delta time
        static double TimeSurvived = 0.0f; // increases with delta time

        static double MinEnemyUpdateDelay = 0.08f; // minimum time the enemies can update
        static double EnemyUpdateDelay = 0.3f; // delay in secodns before the enemy updates
        static double enemyUpdateDelayTimer = 0.0f; // increases with deltatime, once reaches update delay resets

        static int RandomDelayMulti = 1; // enemy spawn delay multi 
        static double MinEnemySpawnDelay = 0.08f; // minimum enemny spawn delay
        static double SpawnDelay = 0.5f; // delay before spawn
        static double spawnDelayTimer = 0.0f; // increases with delta time and resets when reached spawn delay

        static double Scaling = 0; // increases with an equasion, deducts a spawn delay by it, but doesnt set the spawn delay to the result, affects spawn rates

        // Player
        static int PlayerYpoz = 2; // explanitory
        static int PlayerXpoz = 5; // explanitory
        static int lastPlayerYpoz = 2; // explanitory
        static int lastPlayerXpoz = 5; // explanitory

        // Enemies
        static List<(int x, int y)> Enemies = new List<(int x, int y)> { };


        // -- PALIDZIBAS FUNKCIJAS -- //
        // visparejas
        static string TranslateToSymbol(int id)
        {
            if (id == 0) { return " "; } // background
            if (id == 1) { return "O"; } // player
            if (id == 2) { return "%"; } // enemy
            return "?";
        }
        static void UpdateMapWithValue(int x, int y, int value)
        {
            if (y == 1)
            {
                BOTTOM[x] = value;
            }
            else if (y == 2)
            {
                MIDDLE[x] = value;
            }
            else if (y == 3)
            {
                TOP[x] = value;
            }
        }
        static bool IsPlayerDead()
        {
            foreach (var enemy in Enemies)
            {
                if (enemy.x == PlayerXpoz && enemy.y == PlayerYpoz)
                {
                    return true;
                }
            }
            return false;
        }
        static bool IsValidPosition(int x, int y)
        {
            if (x < 0)
            {
                return false;
            }
            return true;
        }

        // -- Enemy
        static void UpdateEnemies()
        {
            double updated = EnemyUpdateDelay - Scaling;
            if (updated < MinEnemyUpdateDelay)
            {
                updated = MinEnemyUpdateDelay;
            }

            if (enemyUpdateDelayTimer < updated)
            {
                return;
            }
            enemyUpdateDelayTimer = 0;

            List<int> ToDelete = new List<int> { };

            for (int index = 0; index < Enemies.Count; index++)
            {
                (int x, int y) goober = Enemies[index];
                UpdateMapWithValue(goober.x, goober.y, 0); // remove old
                goober.x -= 1;

                if (IsValidPosition(goober.x, goober.y) == false)
                {
                    ToDelete.Add(index);
                }
                else
                {
                    UpdateMapWithValue(goober.x, goober.y, 2);
                    Enemies[index] = goober;
                }
            }

            // delete wanted criminals
            foreach (int i in ToDelete)
            {
                Enemies.RemoveAt(i);
            }
        }

        static void SpawnEnemy()
        {
            double updated = (SpawnDelay - Scaling) * RandomDelayMulti;
            if (updated < MinEnemySpawnDelay) // let not become less than min
            {
                updated = MinEnemySpawnDelay;
            }

            if (spawnDelayTimer < updated) // deltat tiem icnreasing timer is < updated delay time
            {
                return;
            }

            if (TOP[RightSide - 1] == 2 || MIDDLE[RightSide - 1] == 2 || BOTTOM[RightSide - 1] == 2) // enemies are one to the left of the very right of the map
            {
                return;
            }

            if (TOP[RightSide] == 2 || MIDDLE[RightSide] == 2 || BOTTOM[RightSide] == 2)
            {
                return;
            }
            spawnDelayTimer = 0;
            RandomDelayMulti = rand.Next(0, 3);

            int randY = rand.Next(1, 4);
            (int x, int y) NewEnemy = (RightSide, randY);
            Enemies.Add(NewEnemy);
            UpdateMapWithValue(NewEnemy.x, NewEnemy.y, 2);
        }

        // -- RENDERIGN AND PLR -- //
        static void Render()
        {
            Console.BackgroundColor = ConsoleColor.DarkGray;
            for (int i = 0; i < TOP.Count; i++)
            {
                string Symbol = TranslateToSymbol(TOP[i]);
                //Color
                if (Symbol == "O")
                {
                    Console.ForegroundColor = ConsoleColor.Yellow;
                }
                if (Symbol == "%")
                {
                    Console.ForegroundColor = ConsoleColor.Magenta;
                }
                Console.Write($"{Symbol}");
                //Color
                Console.ForegroundColor = ConsoleColor.DarkGray;
            }
            Console.WriteLine();

            for (int i = 0; i < MIDDLE.Count; i++)
            {
                string Symbol = TranslateToSymbol(MIDDLE[i]);
                //Color
                if (Symbol == "O")
                {
                    Console.ForegroundColor = ConsoleColor.Yellow;
                }
                if (Symbol == "%")
                {
                    Console.ForegroundColor = ConsoleColor.Magenta;
                }
                Console.Write($"{Symbol}");
                //Color
                Console.ForegroundColor = ConsoleColor.DarkGray;
            }
            Console.WriteLine();

            for (int i = 0; i < BOTTOM.Count; i++)
            {
                string Symbol = TranslateToSymbol(BOTTOM[i]);
                // Color
                if (Symbol == "O")
                {
                    Console.ForegroundColor = ConsoleColor.Yellow;
                }
                if (Symbol == "%")
                {
                    Console.ForegroundColor = ConsoleColor.Magenta;
                }

                Console.Write($"{Symbol}");
                //Color
                Console.ForegroundColor = ConsoleColor.DarkGray;
            }
            Console.BackgroundColor = ConsoleColor.Black;
            Console.ForegroundColor = ConsoleColor.White;
        }
        static void UpdatePlayerPosition(int lastX, int lastY)
        {
            if (PlayerYpoz > 3)
            {
                PlayerYpoz = 3;
            }
            else if (PlayerYpoz < 1)
            {
                PlayerYpoz = 1;
            }
            // REMOVING LAST
            if (lastY == 1)
            {
                BOTTOM[lastX] = 0;
            }
            else if (lastY == 2)
            {
                MIDDLE[lastX] = 0;
            }
            else if (lastY == 3)
            {
                TOP[lastX] = 0;
            }

            // UPDATE
            if (PlayerYpoz == 1)
            {
                BOTTOM[PlayerXpoz] = 1;
            }
            else if (PlayerYpoz == 2)
            {
                MIDDLE[PlayerXpoz] = 1;
            }
            else if (PlayerYpoz == 3)
            {
                TOP[PlayerXpoz] = 1;
            }
        }
        static void processInput(ConsoleKey userKey)
        {
            if (userKey == ConsoleKey.W || userKey == ConsoleKey.UpArrow)
            {
                PlayerYpoz += 1;
            }
            else if (userKey == ConsoleKey.S || userKey == ConsoleKey.DownArrow)
            {
                PlayerYpoz -= 1;
            }
        }
        //  ///

        //  ///

        //  ///

        //  ///
        static void Main()
        {
            while (true)
            {
                
                // Cleanup
                Console.Clear();
                Enemies.Clear();
                clock.Reset();

                DeltaTime = 0.0f;
                TimeSurvived = 0.0f;
                
                MinEnemyUpdateDelay = 0.085f;
                EnemyUpdateDelay = 0.25f;
                enemyUpdateDelayTimer = 0.0f;

                RandomDelayMulti = 1;
                MinEnemySpawnDelay = 0.05f;
                SpawnDelay = 0.5f;
                spawnDelayTimer = 0.0f;

                Scaling = 0;

                PlayerYpoz = 2;

                for (int i = 0; i < TOP.Count; i++)
                {
                    TOP[i] = 0;
                }
                for (int i = 0; i < MIDDLE.Count; i++)
                {
                    MIDDLE[i] = 0;
                }
                for (int i = 0; i < BOTTOM.Count; i++)
                {
                    BOTTOM[i] = 0;
                }


                // Variables
                int UpdateDelay = 1;
                long LastTime = 0;

                // Runtime
                Console.CursorVisible = false;

                // plopping plr in le middle
                PlayerXpoz = TOP.Count / 2;
                MIDDLE[PlayerXpoz] = 1;

                lastPlayerYpoz = 2;
                lastPlayerXpoz = PlayerXpoz;

                clock.Start();

                while (true)
                {
                    // Clear
                    Console.SetCursorPosition(0, 0);

                    // delta time
                    DeltaTime = (double)(clock.ElapsedMilliseconds - LastTime) / 1000;
                    LastTime = clock.ElapsedMilliseconds;

                    // timers
                    TimeSurvived += DeltaTime;
                    enemyUpdateDelayTimer += DeltaTime;
                    spawnDelayTimer += DeltaTime;

                    Scaling = TimeSurvived / 200;

                    if (SpawnDelay < MinEnemySpawnDelay)
                    {
                        SpawnDelay = MinEnemySpawnDelay;
                    }
                    if (EnemyUpdateDelay < MinEnemyUpdateDelay)
                    {
                        EnemyUpdateDelay = MinEnemyUpdateDelay;
                    }

                    // input processing
                    if (Console.KeyAvailable)
                    {
                        ConsoleKeyInfo KINFO = Console.ReadKey(true);
                        processInput(KINFO.Key);
                    }

                    // player
                    UpdatePlayerPosition(lastPlayerXpoz, lastPlayerYpoz);
                    lastPlayerXpoz = PlayerXpoz;
                    lastPlayerYpoz = PlayerYpoz;

                    // enemy
                    UpdateEnemies();
                    SpawnEnemy();

                    if (IsPlayerDead())
                    {
                        break;
                    }

                    // rendering
                    Render();
                    Console.Write($"\nLaiks: {Math.Floor(TimeSurvived * 100) / 100}s");
                    Thread.Sleep(UpdateDelay);
                }
                Render();
                Console.Write($"\nJus izdivojat: {Math.Floor(TimeSurvived * 100) / 100}s\n");
                Console.Write($"Spiezat ENTER. . .");
                Console.ReadLine();
            }
        }
    }
}