context: using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Text;
  
  using LeagueSharp;
  using LeagueSharp.Common;
  using SharpDX;
  
  using EloBuddy; 
  using LeagueSharp.Common; 
   namespace ezEvade
  {
      public class Spell
      {
          public float startTime;
          public float endTime;
          public Vector2 startPos;
          public Vector2 endPos;
          public Vector2 direction;
          public float height;
          public int heroID;
          public int projectileID;
          public SpellData info;
          public int spellID;
          public GameObject spellObject = null;
          public SpellType spellType;
  
          public Vector2 cnLeft;
          public Vector2 cnRight;
          public Vector2 cnStart;
          public Vector2 currentSpellPosition = Vector2.Zero;
          public Vector2 currentNegativePosition = Vector2.Zero;
          public Vector2 predictedEndPos = Vector2.Zero;
  
          public float radius = 0;
          public int dangerlevel = 1;
  
          public float evadeTime = float.MinValue;
          public float spellHitTime = float.MinValue;
  
          public Spell()
          {
  
          }
      }
  
      public static class SpellExtensions
      {
          public static float GetSpellRadius(this Spell spell)
          {
              var radius = ObjectCache.menuCache.cache[spell.info.spellName + "SpellRadius"].GetValue<Slider>().Value;
              var extraRadius = ObjectCache.menuCache.cache["ExtraSpellRadius"].GetValue<Slider>().Value;
  
              if (spell.info.hasEndExplosion && spell.spellType == SpellType.Circular)
              {
                  return spell.info.secondaryRadius + extraRadius;
              }
  
              if (spell.spellType == SpellType.Arc)
              {
                  var spellRange = spell.startPos.Distance(spell.endPos);
                  var arcRadius = spell.info.radius * (1 + spellRange/100) + extraRadius;
                                  
                  return arcRadius;
              }
  
              return (float)(radius + extraRadius);
          }
  
          public static int GetSpellDangerLevel(this Spell spell)
          {
              var dangerStr = ObjectCache.menuCache.cache[spell.info.spellName + "DangerLevel"].GetValue<StringList>().SelectedValue;
  
              var dangerlevel = 1;
  
              switch (dangerStr)
              {
                  case "Low":
                      dangerlevel = 1;
                      break;
                  case "High":
                      dangerlevel = 3;
                      break;
                  case "Extreme":
                      dangerlevel = 4;
                      break;
                  default:
                      dangerlevel = 2;
                      break;
              }
  
              return dangerlevel;
          }
  
          public static string GetSpellDangerString(this Spell spell)
          {
              switch (spell.GetSpellDangerLevel())
              {
                  case 1:
                      return "Low";
                  case 3:
                      return "High";
                  case 4:
                      return "Extreme";
                  default:
                      return "Normal";
              }
          }
  
          public static bool hasProjectile(this Spell spell)
          {
              return spell.info.projectileSpeed > 0 && spell.info.projectileSpeed != float.MaxValue;
          }
  
          public static Vector2 GetSpellProjection(this Spell spell, Vector2 pos, bool predictPos = false)
          {
              if (spell.spellType == SpellType.Line)
              {
                  if (predictPos)
                  {
                      var spellPos = spell.currentSpellPosition;
                      var spellEndPos = spell.GetSpellEndPosition();
  
                      return pos.ProjectOn(spellPos, spellEndPos).SegmentPoint;
                  }
              
                   return pos.ProjectOn(spell.startPos, spell.endPos).SegmentPoint;
              }
  
              if (spell.spellType == SpellType.Arc)
              {
                  if (predictPos)
                  {
                      var spellPos = spell.currentSpellPosition;
                      var spellEndPos = spell.GetSpellEndPosition();
  
                      return pos.ProjectOn(spellPos, spellEndPos).SegmentPoint;
                  }
  
                 return pos.ProjectOn(spell.startPos, spell.endPos).SegmentPoint;
              }
  
              if (spell.spellType == SpellType.Circular)
              {
                  return spell.endPos;
              }
  
              if (spell.spellType == SpellType.Cone)
              {
                  
              }
  
              return Vector2.Zero;
          }
  
          public static Obj_AI_Base CheckSpellCollision(this Spell spell, bool ignoreSelf = true)
          {
              if (spell.info.collisionObjects.Count() < 1)
              {
                  return null;
              }
  
              List<Obj_AI_Base> collisionCandidates = new List<Obj_AI_Base>();
              var spellPos = spell.currentSpellPosition;
              var distanceToHero = spellPos.Distance(ObjectCache.myHeroCache.serverPos2D);
  
              if (spell.info.collisionObjects.Contains(CollisionObjectType.EnemyChampions))
              {
                  foreach (var hero in HeroManager.Allies
                      .Where(h => h.IsValidTarget(distanceToHero, false, spellPos.To3D())))
                  {
                      if (ignoreSelf && hero.IsMe)
                      {
                          continue;
                      }
  
                      collisionCandidates.Add(hero);
                  }
              }
  
              if (spell.info.collisionObjects.Contains(CollisionObjectType.EnemyMinions))
              {
                  foreach (var minion in ObjectManager.Get<Obj_AI_Minion>()
                      .Where(h => h.Team == Evade.myHero.Team && h.IsValidTarget(distanceToHero, false, spellPos.To3D())))
                  {
                      if (minion.BaseSkinName.ToLower() == "teemomushroom"
                          || minion.BaseSkinName.ToLower() == "shacobox")
                      {
                          continue;
                      }
  
                      collisionCandidates.Add(minion);
                  }
              }
  
              var sortedCandidates = collisionCandidates.OrderBy(h => h.Distance(spellPos));
  
              foreach (var candidate in sortedCandidates)
              {
                  if (candidate.ServerPosition.To2D().InSkillShot(spell, candidate.BoundingRadius, false))
                  {
                      return candidate;
                  }
              }
  
              return null;
          }
  
          public static float GetSpellHitTime(this Spell spell, Vector2 pos)
          {
              switch (spell.spellType)
              {
                  case SpellType.Line:
                      if (spell.info.projectileSpeed == float.MaxValue)
                          return Math.Max(0, spell.endTime - EvadeUtils.TickCount - ObjectCache.gamePing);
  
                      var spellPos = spell.GetCurrentSpellPosition(true, ObjectCache.gamePing);
                      return 1000 * spellPos.Distance(pos) / spell.info.projectileSpeed;
                  case SpellType.Cone:
                  case SpellType.Circular:
                      return Math.Max(0, spell.endTime - EvadeUtils.TickCount - ObjectCache.gamePing);
              }
  
              return float.MaxValue;
          }
  
          public static bool CanHeroEvade(this Spell spell, Obj_AI_Base hero, out float rEvadeTime, out float rSpellHitTime)
          {
              var heroPos = hero.ServerPosition.To2D();
              float evadeTime = 0;
              float spellHitTime = 0;
              float speed = hero.MoveSpeed;
              float delay = 0;
  
              var moveBuff = EvadeSpell.evadeSpells.OrderBy(s => s.dangerlevel).FirstOrDefault(s => s.evadeType == EvadeType.MovementSpeedBuff);
  
gt:             if (moveBuff != null && EvadeSpell.ShouldUseMovementBuff(spell))