context: using EloBuddy;
  namespace ElUtilitySuite.Trackers
  {
      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Reflection;
      using System.Security.Permissions;
  
      using LeagueSharp;
      using LeagueSharp.Common;
      using LeagueSharp.Data;
      using LeagueSharp.Data.DataTypes;
  
      using SharpDX;
      using SharpDX.Direct3D9;
  
      using Color = System.Drawing.Color;
      using EloBuddy.SDK.Events;
  
      internal class SpellCountdownTracker : IPlugin
      {
          #region Constants
  
          /// <summary>
          ///     The box height
          /// </summary>
          private const int BoxHeight = 105;
  
          /// <summary>
          ///     The box spacing
          /// </summary>
          private const int BoxSpacing = 25;
  
          /// <summary>
          ///     The box width
          /// </summary>
          private const int BoxWidth = 235;
  
          /// <summary>
          ///     The color indicator width
          /// </summary>
          private const int ColorIndicatorWidth = 10;
  
          /// <summary>
          ///     The countdown
          /// </summary>
          private const int Countdown = 10;
  
          /// <summary>
          ///     The move right speed
          /// </summary>
          private const int MoveRightSpeed = 1000;
  
          #endregion
  
          #region Properties
  
          /// <summary>
          ///     Gets the cards.
          /// </summary>
          /// <value>
          ///     The cards.
          /// </value>
          private List<Card> Cards { get; } = new List<Card>();
  
          /// <summary>
          ///     Gets the countdown font.
          /// </summary>
          /// <value>
          ///     The countdown font.
          /// </value>
          private Font CountdownFont { get; } = new Font(Drawing.Direct3DDevice, new System.Drawing.Font("Arial", 25));
  
          /// <summary>
          ///     Gets the icons.
          /// </summary>
          /// <value>
          ///     The icons.
          /// </value>
          private Dictionary<string, Texture> Icons { get; } = new Dictionary<string, Texture>();
          private Dictionary<string, List<SpellSlot>> ChampionSpells { get; } = new Dictionary<string, List<SpellSlot>>();
  
          /// <summary>
          ///     Gets or sets the menu.
          /// </summary>
          /// <value>
          ///     The menu.
          /// </value>
          private Menu Menu { get; set; }
  
          /// <summary>
          ///     Gets the padding.
          /// </summary>
          /// <value>
          ///     The padding.
          /// </value>
          private Vector2 Padding { get; } = new Vector2(10, 5);
  
          /// <summary>
          ///     Gets the spell name font.
          /// </summary>
          /// <value>
          ///     The spell name font.
          /// </value>
          private Font SpellNameFont { get; } = new Font(Drawing.Direct3DDevice, new System.Drawing.Font("Arial", 15));
  
          /// <summary>
          ///     Gets or sets the spells.
          /// </summary>
          /// <value>
          ///     The spells.
          /// </value>
          private List<SpellDatabaseEntry> Spells { get; } = new List<SpellDatabaseEntry>();
  
          /// <summary>
          ///     Gets the sprite.
          /// </summary>
          /// <value>
          ///     The sprite.
          /// </value>
          private Sprite Sprite { get; } = new Sprite(Drawing.Direct3DDevice);
  
          /// <summary>
          ///     Gets or sets the start x.
          /// </summary>
          /// <value>
          ///     The start x.
          /// </value>
          private int StartX => this.Menu.Item("XPos").GetValue<Slider>().Value;
  
          /// <summary>
          ///     Gets or sets the start y.
          /// </summary>
          /// <value>
          ///     The start y.
          /// </value>
          private int StartY => this.Menu.Item("YPos").GetValue<Slider>().Value;
  
          #endregion
  
          #region Public Methods and Operators
  
          /// <summary>
          ///     Creates the menu.
          /// </summary>
          /// <param name="rootMenu">The root menu.</param>
          /// <returns></returns>
          public void CreateMenu(Menu rootMenu)
          {
              var predicate = new Func<Menu, bool>(x => x.Name == "Trackers");
              var menu =
                  (rootMenu.Children.Any(predicate)
                       ? rootMenu.Children.First(predicate)
                       : rootMenu.AddSubMenu(new Menu("Trackers", "Trackers"))).AddSubMenu(
                           new Menu("Cooldown Notification", "CDNotif2"));
  
              menu.AddItem(new MenuItem("XPos", "X Position").SetValue(new Slider(Drawing.Width - BoxWidth, 0, Drawing.Width)));
              menu.AddItem(new MenuItem("YPos", "Y Position").SetValue(new Slider(Drawing.Height - BoxHeight * 4, 0, Drawing.Height)));
              menu.AddItem(new MenuItem("DrawCards", "Draw Cards").SetValue(true));
              menu.AddItem(new MenuItem("DrawTeleport", "Draw Teleports").SetValue(true));
              menu.AddItem(new MenuItem("AddTestCard", "Draw Test Card").SetValue(false).DontSave());
              menu.AddItem(new MenuItem("empty-line-3000", string.Empty));
  
              foreach (var enemy in HeroManager.Enemies)
              {
                  menu.AddItem(new MenuItem($"Track.{enemy.CharData.BaseSkinName}", "Track " + enemy.ChampionName))
                      .SetValue(true);
              }
  
              menu.Item("AddTestCard").ValueChanged += (sender, args) =>
              {
                  args.Process = false;
  
                  if (this.Cards.Any(x => x.Name.Equals("Test")) || args.GetOldValue<bool>())
                  {
                      return;
                  }
  
                  this.Cards.Add(
                      new Card
                      {
                          EndMessage = "Ready",
                          EndTime = Game.Time + 10,
                          FriendlyName = "Test",
                          Name = "Test",
                          StartTime = Game.Time
                      });
              };
  
              this.Menu = menu;
          }
  
          /// <summary>
          ///     Loads this instance.
          /// </summary>
          //[PermissionSet(SecurityAction.Assert, Unrestricted = true)]
          public void Load()
          {
              if (Game.MapId == GameMapId.HowlingAbyss)
              {
                  return;
              }
  
              Game.OnUpdate += this.GameOnUpdate;
              Drawing.OnDraw += this.Drawing_OnDraw;
  
              JungleTracker.CampDied += this.JungleTrackerCampDied;
              Teleport.OnTeleport += this.OnTeleport;
              Obj_AI_Base.OnBuffLose += this.OnBuffLose;
  
              Drawing.OnPreReset += args =>
              {
                  this.SpellNameFont.OnLostDevice();
                  this.CountdownFont.OnLostDevice();
                  this.Sprite.OnLostDevice();
              };
  
              Drawing.OnPostReset += args =>
              {
                  this.SpellNameFont.OnResetDevice();
                  this.CountdownFont.OnResetDevice();
                  this.Sprite.OnResetDevice();
              };
  
              var names = Assembly.GetExecutingAssembly().GetManifestResourceNames().Skip(1).ToList();
              var neededSpells =
                   Data.Get<SpellDatabase>().Spells.Where(
                          x =>
                          HeroManager.Enemies.Any(
                              y => x.ChampionName.Equals(y.ChampionName, StringComparison.InvariantCultureIgnoreCase))).Select(x => x.SpellName).ToList();
  
              foreach (var name in names)
              {
                  try
                  {
                      var spellName = name.Split('.')[3];
                      if (spellName != "Dragon" && spellName != "Baron" && spellName != "Teleport" && spellName != "Rebirthready" && spellName != "Zacrebirthready")
                      {
                          this.Spells.Add(Data.Get<SpellDatabase>().Spells.First(x => x.SpellName.Equals(spellName)));
  
                          if (!neededSpells.Contains(spellName))
                          {
                              continue;
                          }
                      }
  
                      this.Icons.Add(
                          spellName,
                          Texture.FromStream(
                              Drawing.Direct3DDevice,
                              Assembly.GetExecutingAssembly().GetManifestResourceStream(name)));
                  }
                  catch (Exception)
                  {
                  }
              }
  
              foreach (var spell in this.Spells)
              {
                  if (!this.ChampionSpells.ContainsKey(spell.ChampionName))
                  {
                      this.ChampionSpells[spell.ChampionName] = new List<SpellSlot>();
                  }
  
                  this.ChampionSpells[spell.ChampionName].Add(spell.Slot);
              }
          }
  
          #endregion
  
          #region Methods
  
          /// <summary>
          ///     
          /// </summary>
          /// <param name="sender"></param>
          /// <param name="args"></param>
          private void OnBuffLose(Obj_AI_Base sender, Obj_AI_BaseBuffLoseEventArgs args)
          {
              if (sender.IsEnemy)
              {
                  if (args.Buff.Name.Equals("rebirthready", StringComparison.InvariantCultureIgnoreCase))
                  {
                      var card = new Card
                      {
                          EndTime = Game.Time + 240,
                          EndMessage = "Ready",
                          FriendlyName = "Rebirth",
                          StartTime = Game.Time
                      };
                      card.Name = "Rebirthready";
                      this.Cards.Add(card);
                  }
  
                  if (args.Buff.Name.Equals("zacrebirthready", StringComparison.InvariantCultureIgnoreCase))
                  {
                      var card = new Card
                      {
                          EndTime = Game.Time + 300,
                          EndMessage = "Ready",
                          FriendlyName = "Cell Division",
                          StartTime = Game.Time
                      };
  
                      card.Name = "Zacrebirthready";
                      this.Cards.Add(card);
                  }
              }
          }
  
          /// <summary>
          /// 
          /// </summary>
          /// <param name="sender"></param>
          /// <param name="args"></param>
          private void OnTeleport(Obj_AI_Base sender, Teleport.TeleportEventArgs packet)
          {
              try
              {
                  if (sender.IsAlly || !this.Menu.Item("DrawTeleport").IsActive())
                  {
                      return;
                  }
  
                  var hero = sender as AIHeroClient;
                  if (hero != null)
                  {
                      if (packet.Type == EloBuddy.SDK.Enumerations.TeleportType.Teleport &&
                          (packet.Status == EloBuddy.SDK.Enumerations.TeleportStatus.Finish || packet.Status == EloBuddy.SDK.Enumerations.TeleportStatus.Abort))
                      {
                          var time = Game.Time;
                          LeagueSharp.Common.Utility.DelayAction.Add(
                              250,
                              delegate
                              {
                                  var cd = packet.Status == EloBuddy.SDK.Enumerations.TeleportStatus.Finish ? 300 : 200;
                                  var card = new Card
                                  {
                                      EndTime = time + cd,
                                      EndMessage = "Ready",
                                      FriendlyName = $"{hero.ChampionName} Teleport",
                                      StartTime = Game.Time
                                  };
  
                                  card.Name = "Teleport";
                                  this.Cards.Add(card);
                              });
                      }
                  }
              }
              catch (Exception e)
              {
                  Console.WriteLine(@"An error occurred: '{0}'", e);
              }
          }
  
          /// <summary>
          ///     Draws a box.
          /// </summary>
          /// <param name="position">The position.</param>
          /// <param name="width">The width.</param>
          /// <param name="height">The height.</param>
          /// <param name="color">The color.</param>
          /// <param name="borderwidth">The borderwidth.</param>
          /// <param name="borderColor">Color of the border.</param>
          private static void DrawBox(
              Vector2 position,
              int width,
              int height,
              Color color,
              int borderwidth,
              Color borderColor)
          {
              Drawing.DrawLine(position.X, position.Y, position.X + width, position.Y, height, color);
  
              if (borderwidth <= 0)
              {
                  return;
              }
  
              Drawing.DrawLine(position.X, position.Y, position.X + width, position.Y, borderwidth, borderColor);
              Drawing.DrawLine(
                  position.X,
                  position.Y + height,
                  position.X + width,
                  position.Y + height,
                  borderwidth,
                  borderColor);
  
              Drawing.DrawLine(position.X, position.Y + 1, position.X, position.Y + height + 1, borderwidth, borderColor);
              Drawing.DrawLine(
                  position.X + width,
                  position.Y + 1,
                  position.X + width,
                  position.Y + height + 1,
                  borderwidth,
                  borderColor);
          }
  
          /// <summary>
          ///     Drawing_s the on draw.
          /// </summary>
          /// <param name="args">The <see cref="EventArgs" /> instance containing the event data.</param>
          private void Drawing_OnDraw(EventArgs args)
          {
              if (Drawing.Direct3DDevice == null || Drawing.Direct3DDevice.IsDisposed
                  || !this.Menu.Item("DrawCards").IsActive())
              {
                  return;
              }
  
              var i = 0;
  
              // TODO clean this shit up LMAO
              foreach (var enemy in HeroManager.Enemies)
              {
                  List<SpellSlot> slots;
                  if (!this.ChampionSpells.TryGetValue(enemy.ChampionName, out slots))
                  {
                      continue;
                  }
  
                  if (!this.Menu.Item($"Track.{enemy.ChampionName}").IsActive())
                  {
                      continue;
                  }
  
                  foreach (var spell in slots.Select(x => enemy.GetSpell(x)).Where(x => x.Level > 0 && x.CooldownExpires > 0
                  && x.CooldownExpires - Game.Time <= Countdown))
                  {
  
gt:                     if (spell.CooldownExpires - Game.Time <= -5