using System.Diagnostics;
using System.Security.Cryptography;

namespace MyConsoleApp.Work
{
    public class LinqWork
    {
        private static List<Player> _players = new List<Player>();

        private static void InitInstance()
        {
            Random rand = new Random();
            for (int i = 0; i < 100; i++)
            {
                ClassType type = ClassType.Knight;
                switch (rand.Next(0, 3))
                {
                    case 1:
                        type = ClassType.Archer;
                        break;
                    case 2:
                        type = ClassType.Mage;
                        break;
                }

                Player player = new Player()
                {
                    ClassType = type,
                    Level = rand.Next(1, 100),
                    Hp = rand.Next(100, 1000),
                    Attack = rand.Next(5, 50)
                };

                for (int j = 0; j < 5; j++)
                {
                    player.Items.Add(rand.Next(1, 100));
                }

                _players.Add(player);
            }
        }

        public static void DoTest()
        {
            InitInstance();

            // Q1) 레벨 50 이상인 Knight를 레벨 오름차순으로 정렬

            Stopwatch sw = new Stopwatch();
            sw.Start();
            // Without Linq
            {
                List<Player> players = GetHighLevelKnight();
                foreach (Player player in players)
                {
                    System.Console.WriteLine($"[{player.Level}] {player.Hp}");
                }
            }
            System.Console.WriteLine($"{sw.ElapsedMilliseconds}ms");

            System.Console.WriteLine("---------------------");

            sw.Restart();
            // With Linq
            {
                var players = 
                    from p in _players
                    where p.ClassType == ClassType.Knight && p.Level >= 50
                    orderby p.Level
                    select p;

                foreach (Player player in players)
                {
                    System.Console.WriteLine($"[{player.Level}] {player.Hp}");
                }
            }
            System.Console.WriteLine($"{sw.ElapsedMilliseconds}ms");

            // from 중첩
            // Q2) id가 30 미만인 모든 아이템 목록 추출
            {
                var playerItems =
                    from p in _players
                    from i in p.Items
                    where i < 30
                    select new { p, i };
                
                var li = playerItems.ToList();
                System.Console.WriteLine($"Count: {li.Count}");
            }

            // grouping
            {
                var playerByLevel =
                    from p in _players
                    group p by p.Level into g
                    orderby g.Key
                    select new { g.Key, Players = g };
            }

            // join
            {
                List<int> levels = new List<int>() { 1, 5, 10 };
                var players = 
                    from p in _players
                    join l in levels
                    on p.Level equals l
                    select p;
            }

            // Linq 표준 연산자
            {
                var players = 
                    from p in _players
                    where p.ClassType == ClassType.Knight && p.Level >= 50
                    orderby p.Level
                    select p;

                var players2 = _players
                    .Where(p => p.ClassType == ClassType.Knight && p.Level >= 50)
                    .OrderBy(p => p.Level)
                    .Select(p => p);
            }
        }

        private static List<Player> GetHighLevelKnight()
        {
            List<Player> players = new List<Player>();
            foreach (Player player in _players)
            {
                if (player.ClassType != ClassType.Knight)
                    continue;
                if (player.Level < 50)
                    continue;

                players.Add(player);
            }

            players.Sort((lhs, rhs) => { return lhs.Level - rhs.Level; });

            return players;
        }
    }

    public enum ClassType
    {
        Knight,
        Archer,
        Mage,
    }

    public class Player
    {
        public ClassType ClassType { get; set; }
        public int Level { get; set; }
        public int Hp { get; set; }
        public int Attack { get; set; }
        public List<int> Items { get; set; } = new List<int>();
    }
}