context: #region License
  
  /*
   Copyright 2014 - 2015 Nikita Bernthaler
   Weights.cs is part of SFXKalista.
  
   SFXKalista is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.
  
   SFXKalista is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
   GNU General Public License for more details.
  
   You should have received a copy of the GNU General Public License
   along with SFXKalista. If not, see <http://www.gnu.org/licenses/>.
  */
  
  #endregion License
  
  #region
  
  using System;
  using System.Collections.Generic;
  using System.Collections.ObjectModel;
  using System.Linq;
  using LeagueSharp;
  using LeagueSharp.Common;
  using ItemData = LeagueSharp.Common.Data.ItemData;
  
  #endregion
  
  /*
   * Don't copy paste this without asking & giving credits fuckers :^) 
   */
  
  namespace SFXKalista.SFXTargetSelector
  {
      public static partial class TargetSelector
      {
          public static partial class Weights
          {
              public const int MinWeight = 0;
              public const int MaxWeight = 20;
              public const int MinCaching = 0;
              public const int MaxCaching = 3000;
              private static float _range;
              private static readonly List<Item> PItems;
              private static float _maxRange = 2000f;
              private static readonly SimpleCache<float> ItemCache;
              private static readonly SimpleCache<float> LongCache;
  
              static Weights()
              {
                  ItemCache = new SimpleCache<float> { MaxAge = MinCaching };
                  LongCache = new SimpleCache<float> { MaxAge = 3000 };
                  InvertedPrefix = "[i]";
                  CustomPrefix = "[c]";
                  PItems = new List<Item>();
                  PItems.AddRange(
                      new List<Item>
                      {
                          new Item(
                              "killable", "AA Killable", 20, false,
                              t => t.Health < ObjectManager.Player.GetAutoAttackDamage(t, true) ? 1 : 0,
                              "Checks if target is killable with one auto attack."),
                          new Item(
                              "attack-damage", "Attack Damage", 15, false, delegate(Obj_AI_Hero t)
                              {
                                  var ad = t.FlatPhysicalDamageMod;
                                  var infinity = LongCache.GetOrAdd(
                                      "infinity-edge-" + t.ChampionName,
                                      () => ItemData.Infinity_Edge.GetItem().IsOwned(t) ? 2.5f : 2);
                                  ad += ad / 100 * (t.Crit * 100) * (infinity ?? 1f);
                                  var averageArmor = LongCache.GetOrAdd(
                                      "average-armor",
                                      delegate
                                      {
                                          return HeroManager.Allies.Select(a => a.Armor).DefaultIfEmpty(0).Average() *
                                                 t.PercentArmorPenetrationMod - t.FlatArmorPenetrationMod;
                                      });
                                  return ad * (100 / (100 + (averageArmor ?? 0))) *
                                         (1f / ObjectManager.Player.AttackDelay);
                              }, "AD + Pen + Crit + Speed = Higher Weight"),
                          new Item(
                              "ability-power", "Ability Power", 15, false, delegate(Obj_AI_Hero t)
                              {
                                  var averageMr = LongCache.GetOrAdd(
                                      "average-magic-resist",
                                      delegate
                                      {
                                          return
                                              HeroManager.Allies.Select(a => a.SpellBlock).DefaultIfEmpty(0).Average() *
                                              t.PercentMagicPenetrationMod - t.FlatMagicPenetrationMod;
                                      });
                                  return t.FlatMagicDamageMod * (100 / (100 + (averageMr ?? 0)));
                              }, "AP + Pen = Higher Weight"),
                          new Item(
                              "low-resists", "Resists", 0, true,
                              t =>
                                  ObjectManager.Player.FlatPhysicalDamageMod >= ObjectManager.Player.FlatMagicDamageMod
                                      ? t.Armor
                                      : t.SpellBlock, "Low Armor / Magic-Resist = Higher Weight"),
                          new Item("low-health", "Health", 20, true, t => t.Health, "Low Health = Higher Weight"),
                          new Item(
                              "short-distance-player", "Distance to Player", 5, true,
                              t => t.Distance(ObjectManager.Player), "Close to Player = Higher Weight"),
                          new Item(
                              "short-distance-cursor", "Distance to Cursor", 0, true, t => t.Distance(Game.CursorPos),
                              "Close to Cursor = Higher Weight"),
                          new Item(
                              "crowd-control", "Crowd Control", 0, false, delegate(Obj_AI_Hero t)
                              {
                                  var buffs =
                                      t.Buffs.Where(
                                          x =>
                                              x.Type == BuffType.Charm || x.Type == BuffType.Knockback ||
                                              x.Type == BuffType.Suppression || x.Type == BuffType.Fear ||
                                              x.Type == BuffType.Taunt || x.Type == BuffType.Stun ||
                                              x.Type == BuffType.Slow || x.Type == BuffType.Silence ||
                                              x.Type == BuffType.Snare || x.Type == BuffType.Polymorph).ToList();
                                  return buffs.Any() ? buffs.Select(x => x.EndTime).DefaultIfEmpty(0).Max() : 0;
                              }, "Checks if the target suffers from any form of crowd control."),
                          new Item(
                              "gold", "Acquired Gold", 0, false,
                              t =>
                                  (t.MinionsKilled + t.NeutralMinionsKilled) * 27.35f + t.ChampionsKilled * 300f +
                                  t.Assists * 85f, "Calculates the approx. amount of Gold."),
                          new Item(
                              "team-focus", "Team Focus", 0, false,
                              t =>
                                  Aggro.Items.Where(a => a.Value.Target.Hero.NetworkId == t.NetworkId)
                                      .Select(a => a.Value.Value)
                                      .DefaultIfEmpty(0)
                                      .Sum(), "Checks who your allies are targeting."),
                          new Item(
                              "focus-me", "Focus Me", 0, false, delegate(Obj_AI_Hero t)
                              {
                                  var entry = Aggro.GetSenderTargetItem(t, ObjectManager.Player);
                                  return entry != null ? entry.Value : 0;
                              }, "Checks who is targeting you.")
                      });
              }
  
              public static ReadOnlyCollection<Item> Items
              {
                  get { return PItems.AsReadOnly(); }
              }
  
              public static string InvertedPrefix { get; set; }
              public static string CustomPrefix { get; set; }
              public static Menu Menu { get; private set; }
  
              public static float Range
              {
                  get { return _range; }
                  set { _range = Math.Min(MaxRange, value); }
              }
  
              public static float MaxRange
              {
                  get { return _maxRange; }
                  set { _maxRange = value; }
              }
  
              public static int Caching
              {
                  get { return ItemCache.MaxAge; }
                  set
                  {
                      ItemCache.MaxAge = Math.Min(MaxCaching, Math.Max(MinCaching, value));
                      Utils.UpdateMenuItem(Menu, ".caching", ItemCache.MaxAge);
                  }
              }
  
              internal static void AddToMainMenu()
              {
                  Menu = TargetSelector.Menu.AddSubMenu(new Menu("Weights", TargetSelector.Menu.Name + ".weights"));
  
                  Heroes.AddToWeightMenu();
  
                  Menu.AddItem(
                      new MenuItem(Menu.Name + ".caching", "Caching %").SetShared()
                          .SetValue(new Slider(ItemCache.MaxAge, MinCaching, MaxCaching)))
                      .SetTooltip("0 = Real Time Calculations | Milliseconds")
                      .ValueChanged += (sender, args) => ItemCache.MaxAge = args.GetNewValue<Slider>().Value;
  
                  foreach (var item in Items)
                  {
                      var localItem = item;
                      var weightItem =
                          new MenuItem(Menu.Name + "." + item.UniqueName, GetDisplayNamePrefix(item) + item.DisplayName)
                              .SetShared().SetValue(new Slider(localItem.Weight, MinWeight, MaxWeight));
                      if (!string.IsNullOrWhiteSpace(item.Tooltip))
                      {
                          weightItem.SetTooltip(item.Tooltip);
                      }
                      Menu.AddItem(weightItem);
                      Menu.Item(Menu.Name + "." + item.UniqueName).ValueChanged +=
                          delegate(object sender, OnValueChangeEventArgs args)
                          {
                              localItem.OnWeightChange(args.GetNewValue<Slider>().Value);
                          };
                      item.Weight = TargetSelector.Menu.Item(Menu.Name + "." + item.UniqueName).GetValue<Slider>().Value;
                  }
  
                  ItemCache.MaxAge = Utils.GetMenuItemValue<int>(Menu, ".caching");
  
                  Game.OnInput += OnGameInput;
              }
  
              private static string GetDisplayNamePrefix(Item item)
              {
                  var prefix = string.Empty;
                  if (item.Custom)
                  {
                      prefix += CustomPrefix;
                  }
                  if (item.Inverted)
                  {
                      prefix += InvertedPrefix;
                  }
                  if (!string.IsNullOrEmpty(prefix))
                  {
                      prefix += " ";
                  }
                  return prefix;
              }
  
              private static void OnGameInput(GameInputEventArgs args)
              {
                  if (args.Input == null)
                  {
                      return;
                  }
                  var input = args.Input.ToLower();
                  if (input.Equals("/weights reset", StringComparison.OrdinalIgnoreCase))
                  {
                      args.Process = false;
                      RestoreDefaultWeights();
                  }
              }
  
              public static void RestoreDefaultWeights()
              {
                  foreach (var item in Items)
                  {
                      if (Menu != null)
                      {
                          var menuItem = Menu.Item(Menu.Name + "." + item.UniqueName);
  
gt:                         if (menuItem != null)