/// <summary>
 /// Checks if the description of the skill exists
 /// </summary>
 protected SkillEffectName checkIfSkillNameExists(string name, Skill skill, string skillDescription)
 {
     SkillEffectName skillEffectName = new SkillEffectName();
     using (Dota2Entities ctx = new Dota2Entities())
     {
         try
         {
             skillEffectName = ctx.SkillEffectName.Where(x =>
                                                         x.Name == name &&
                                                         x.Skill.Hero.Name == skill.Hero.Name &&
                                                         skillDescription == skill.Description).First();
             if(skillEffectName.ID != null)
             {
                 Console.WriteLine("Skill " + name + " Already exists...");
                 return skillEffectName;
             }
             else
             {
                 return null;   
             }
         }
         catch (Exception e)
         {
             //TODO implementar log de erro
             throw e;
         }                
     }
 }
        public SkillEffectName InsertSkillEffectName(string name, string heroName, Skill skill, List<string> skillEffectValues)
        {
            SkillEffectName skillEffectName;            

            Skill completeSkill = SkillCreator.SelectByName(skill.Name);            

            bool exists = checkIfSkillNameExists(name, completeSkill, completeSkill.Description, out skillEffectName);

            if (!exists)
            {
                skillEffectName.Name = name.Trim();
                skillEffectName.SkillId = completeSkill.ID;
                skillEffectName.ValueLv1 = int.Parse(skillEffectValues.First());
                skillEffectName.ValueLv2 = int.Parse(skillEffectValues.ElementAt(1));
                skillEffectName.ValueLv3 = int.Parse(skillEffectValues.ElementAt(2));
                skillEffectName.ValueLv4 = int.Parse(skillEffectValues.ElementAt(3));
                skillEffectName.ValueScepter = int.Parse(skillEffectValues.Last());            

                using (Dota2Entities ctx = new Dota2Entities())
                {
                    try
                    {
                        skillEffectName = ctx.SkillEffectName.Add(skillEffectName);
                        ctx.SaveChanges();
                        Console.WriteLine("Skill " + name + " Created");
                    }
                    catch (Exception e)
                    {
                        throw e;
                    }
                }
            }
            return skillEffectName;
        }
        public SkillEffectName InsertSkillEffectName(string name, string heroName, Skill skill, List<string> skillEffectValues)
        {
            SkillEffectName skillEffectName = new SkillEffectName();

            Skill completeSkill = SkillCreator.SelectByName(skill.Name);

            skillEffectName.Name = name.Trim();



            skillEffectName = checkIfSkillNameExists(name, completeSkill, completeSkill.Description);

            if (skillEffectName == null)
            {
                using (Dota2Entities ctx = new Dota2Entities())
                {
                    try
                    {
                        skillEffectName = ctx.SkillEffectName.Add(skillEffectName);
                        ctx.SaveChanges();
                        Console.WriteLine("Skill " + name + " Created");
                    }
                    catch (Exception e)
                    {
                        throw e;
                    }
                }
            }
            return skillEffectName;
        }
        public Skill createSkill(int heroId, string name, string description, List<string> manaCostList, List<string> coolDownList,
            string abilityCastType, string targetAffectedType, string damageType, string videoUrl)
        {
            this.skill = new Skill();

            this.skill.HeroId = heroId;
            this.skill.Name = name;
            this.skill.Description = description;
            this.skill.VideoUrl = videoUrl;
            //Set Up ManaCost
            foreach (var manaCost in manaCostList)
            {
                setManaCost(manaCost);
            }

            //Set up CoolDowns
            foreach (var coolDown in coolDownList)
            {
                setCoolDowns(coolDown);
            }

            if(!string.IsNullOrEmpty(abilityCastType))
                setAbilityCastType(abilityCastType);

            if (!string.IsNullOrEmpty(targetAffectedType))
                setTargetAffectedType(targetAffectedType);

            if (!string.IsNullOrEmpty(damageType))
                setDamageType(damageType);

            return this.skill;

        }
        public SkillCreator(string name, string description, List<string> manaCostList, List<string> coolDownList,
            string abilityCastType, string targetAffectedType, string damageType, string videoUrl)
        {
            this.skill = new Skill();

            this.skill.Name = name;
            this.skill.Description = description;

            //Set Up ManaCost
            foreach (var manaCost in manaCostList)
            {
                setManaCost(manaCost);
            }

            //Set up CoolDowns
            foreach (var coolDown in coolDownList)
            {
                setCoolDowns(coolDown);
            }

            if(!string.IsNullOrEmpty(abilityCastType))
                setAbilityCastType(abilityCastType);

            if(string.IsNullOrEmpty(targetAffectedType))
            

        }
        /// <summary>
        /// Checks if the description of the skill exists
        /// </summary>
        protected SkillEffectName checkIfSkillNameExists(string name, Skill skill, string skillDescription)
        {
            SkillEffectName skillEffectName = new SkillEffectName();
            using (Dota2Entities ctx = new Dota2Entities())
            {
                try
                {
                    ctx.Configuration.LazyLoadingEnabled = true;

                    if(ctx.SkillEffectName.Any(s => s.Name == name))
                    {
                    skillEffectName = ctx.SkillEffectName.Where(x =>
                                                                x.Name == name).First();


                    skillEffectName = ctx.SkillEffectName.Where(x =>
                                                                x.Name == name &&
                                                                x.Skill.Hero.Name == skill.Hero.Name &&
                                                                skillDescription == skill.Description).First();
                    
                        Console.WriteLine("Skill " + name + " Already exists...");
                        return skillEffectName;
                    }
                    else
                    {
                        return null;   
                    }
                }
                catch (Exception e)
                {
                    //TODO implementar log de erro
                    throw e;
                }                
            }
        }
        public SkillEffectName InsertSkillEffectName(string name, string heroName, Skill skill, List<string> skillEffectValues)
        {
            HeroCreator heroCreator = new HeroCreator();
            SkillEffectName skillEffectName;
            Hero hero = heroCreator.getHeroByName(heroName);

            Skill completeSkill = SkillCreator.SelectSkill(skill.Name, hero.ID);            

            bool exists = checkIfSkillNameExists(name, completeSkill, completeSkill.Description, out skillEffectName);

            if (!exists)
            {
                skillEffectName.Name = name.Trim();
                skillEffectName.SkillId = completeSkill.ID;
                if (skillEffectValues.FirstOrDefault() != null)
                    skillEffectName.ValueLv1 = skillEffectValues.First();
                if (skillEffectValues.ElementAtOrDefault(1) != null)
                    skillEffectName.ValueLv2 = skillEffectValues.ElementAt(1);
                if (skillEffectValues.ElementAtOrDefault(2) != null)
                    skillEffectName.ValueLv3 = skillEffectValues.ElementAt(2);
                if (skillEffectValues.ElementAtOrDefault(3) != null)
                    skillEffectName.ValueLv4 = skillEffectValues. ElementAt(3);
                if (skillEffectValues.LastOrDefault() != null)
                    skillEffectName.ValueScepter = skillEffectValues.Last();            

                using (Dota2Entities ctx = new Dota2Entities())
                {
                    try
                    {
                        skillEffectName = ctx.SkillEffectName.Add(skillEffectName);
                        ctx.SaveChanges();
                        Console.WriteLine("Skill " + name + " Created");
                    }
                    catch (Exception e)
                    {
                        throw e;
                    }
                }
            }
            return skillEffectName;
        }
        /// <summary>
        /// Checks if the description of the skill exists
        /// </summary>
        protected bool checkIfSkillNameExists(string name, Skill skill, string skillDescription, out SkillEffectName skillEffectName)
        {
            using (Dota2Entities ctx = new Dota2Entities())
            {
                skillEffectName = new SkillEffectName();

                try
                {
                    skill = ctx.Skill.Include("Hero").Where(x => x.ID == skill.ID).First();

                    var result = from s in ctx.SkillEffectName
                                 where s.Name == name
                                    && s.SkillId == skill.ID
                                    && s.Skill.Hero.Name == skill.Hero.Name
                                    && s.Skill.Description == skill.Description
                                 select s;

                    if (result.Count() == 1)
                    {
                        Console.WriteLine(result.First().ID +  " Skill " + name + " Already exists..." );
                        skillEffectName = result.First();
                        return true;
                    }
                    else if (result.Count() > 1)
                    {
                        //TODO Create log
                        throw new Exception("More than 1 skillEffectname returned. Check this");
                    }
                    else
                    {
                        return false;
                    }
                }
                catch (Exception e)
                {
                    //TODO implementar log de erro
                    throw e;
                }                
            }
        }
        /// <summary>
        /// Checks if the description of the skill exists
        /// </summary>
        protected SkillEffectName checkIfSkillNameExists(string name, Skill skill, string skillDescription)
        {
            SkillEffectName skillEffectName = new SkillEffectName();
            

            using (Dota2Entities ctx = new Dota2Entities())
            {
                try
                {
                    ctx.Configuration.LazyLoadingEnabled = false;

                    var result = from s in ctx.SkillEffectName
                                 where s.SkillId == skill.ID
                                    && s.Skill.Hero.Name == skill.Hero.Name
                                    && s.Skill.Description == skill.Description
                                 select s;
                    
                    if(result.Count() == 0)
                    {
                        Console.WriteLine("Skill " + name + " Already exists...");
                        return result.First();
                    }
                    else if(result.Count() > 1)
                    {
                        //TODO Create log
                        throw new Exception("More than 1 skilleffectname returned. Check this");
                    }
                    else
                    {
                        return null;   
                    }
                }
                catch (Exception e)
                {
                    //TODO implementar log de erro
                    throw e;
                }                
            }
        }
		protected Skill workAroundToSkillsOfSameName(string skillName)
		{
			Skill skill = new Skill() { Name = skillName + " " };
			return skill;
		}
		private Dictionary<Skill, Dictionary<string, string>> GetSkillRemainingValues(HtmlDocument doc)
		{
			var dicRemainingValues = new Dictionary<Skill, Dictionary<string, string>>();

			var skillList = new List<Skill>();//Where the skills are

			//We need to take this nodelist to get the skillName of the remaining values
			var abilityHeaderRowDescription = doc.DocumentNode.SelectNodes("//*[@class = 'abilityHeaderRowDescription']");

			//The node that the remaining values are
			var abilityFooterBoxRight = doc.DocumentNode.SelectNodes("//*[@class = 'abilityFooterBoxRight']");

			for (int i = 0; i < abilityFooterBoxRight.Count; i++)
			{
				var skillEffectNameList = new List<string>();//Where the descriptions of the skills are
				var skillEffectValuesList = new List<string>(); //Where the values of the skills are

				var divInnerHtml = abilityFooterBoxRight[i].SelectNodes(".//*[contains(@span, '')]");

				//Íf the divInnerHtml is empty, it means that the abilityFooterBoxRight don't have info about the skill
				//but the abilityHeaderRowDescription may have info, so we must check it
				if (divInnerHtml != null)
				{
					//? Get the list of br tags that contains the description
					var descriptionList = divInnerHtml.Where(x => x.Name == "br").ToList();

					//? Get the list of span tags that contains the values
					var valuesList = divInnerHtml.Where(x => x.Name == "span")
												 .Where(x => x.Attributes["class"].Value != "scepterVal");

					//? Get the name of the skills that the current remanining values are
					string skillName = getSkillNameInRemainingSkill(abilityHeaderRowDescription[i]);
					var skillComparer = new Skill() { Name = skillName };//Create skillobject to do the comparison

					//? Get the description of the skills that the current remanining values are
					skillComparer.Description = getSkillDescriptionInRemainingSkill(abilityHeaderRowDescription[i]);

					//Now a workaround, Because dictionaries doesn't accept keys with same value, we need to add a " "
					//to skills that have the same name.
					if (skillList.Contains(skillComparer))
						skillList.Add(workAroundToSkillsOfSameName(skillName));
					else
						skillList.Add(skillComparer);
					//In case that no tag "br" exists, it means that the skill has only one description
					//So we need to get the value inside the div tag
					if (descriptionList.Count <= 0)
					{
						HtmlNode htmlNode = abilityFooterBoxRight[i].ChildNodes.Where(x => x.Name == "#text").First();
						skillEffectNameList.Add(htmlNode.InnerText.Trim());
					}

					else
					{
						for (int it = 0; it < descriptionList.Count; it++)
						{
							//Como pegando somente o nextSibling perdemos o primeiro texto, quando o I for 0, pegamos o 1° texto
							if (it == 0)
								skillEffectNameList.Add(descriptionList[it].PreviousSibling.PreviousSibling.InnerText.Trim());

							if (descriptionList.Count > 1)
							{
								//When the value is "" the skill must be a scepter upgrade, so we need to check the scepterVal class
								if (descriptionList[it].NextSibling.InnerText.Trim() == "")
								{
									skillEffectNameList.Add(descriptionList[it].ParentNode.SelectNodes(".//*[@class = 'scepterVal']").First().InnerText.Trim());
									divInnerHtml.Remove(descriptionList[it].ParentNode.SelectNodes(".//*[@class = 'scepterVal']").First());
								}
								else
									skillEffectNameList.Add(descriptionList[it].NextSibling.InnerText.Trim());
							}
						}
					}

					foreach (var span in valuesList)
					{
						skillEffectValuesList.Add(span.InnerText.Trim());
					}
					Dictionary<string, string> dicDescValue = new Dictionary<string, string>();

					for (int ix = 0; ix < skillEffectNameList.Count; ix++)
					{
						//Now a workaround, Because dictionaries doesn't accept keys with same value, we need to add a " "
						//to skillDescriptionList that have the same name.
						//if (dicDescValue[skillName].Contains(skillDescriptionList[ix]))
						//{
						//    skillDescriptionList[ix] = workAroundToSkillsSameDescription(skillDescriptionList[ix]);
						//}
						//else

						if (skillEffectNameList.Count != skillEffectNameList.Distinct().Count())
						{
							var duplicates = skillEffectNameList.GroupBy(s => s).SelectMany(grp => grp.Skip(1));

							//TODO Comentar isto
							foreach (var duplicate in duplicates)
							{
								for (int it = 0; it < skillEffectNameList.Count; it++)
								{
									if (skillEffectNameList[it].Contains(duplicate))
									{
										skillEffectNameList[it] = skillEffectNameList[it] + " ";
									}
								}
							}
						}

						dicDescValue.Add(skillEffectNameList[ix], skillEffectValuesList[ix]);
					}

					dicRemainingValues.Add(skillList[i], dicDescValue);
				}
				//In this case, the skill don't have values or descriptions, so we only need to get their name
				else if (abilityHeaderRowDescription != null)
				{
					Skill skill = new Skill() { Name = getSkillNameInRemainingSkill(abilityHeaderRowDescription[i]) };
					skillList.Add(skill);
				}
			}
			return dicRemainingValues;
		}
        /// <summary>
        /// Workaround 
        /// Get the first skill description because there're skills that have the same name and the same heroName, but they are equals, so get the first description
        /// </summary>
        /// <param name="name"></param>
        /// <param name="description"></param>
        /// <param name="heroId"></param>
        /// <returns></returns>
        public static Skill SelectSkill(string name, int heroId)
        {
            Skill skill = new Skill();
            using (Dota2Entities ctx = new Dota2Entities())
            {
                try
                {
                    skill = ctx.Skill.Where(s => s.Name == name &&
                                                 s.HeroId == heroId).FirstOrDefault();
                }
                catch (Exception e)
                {
                    //TODO adicionar log
                    throw e;
                }
            }

            return skill;
        }
		public Skill createSkill(int heroId, string name, string description, List<KeyValuePair<string, string>> manaCostList, List<KeyValuePair<string, string>> coolDownList,
			List<KeyValuePair<string, string>> abilityCastTypeList, List<KeyValuePair<string, string>> targetAffectedTypeList, List<KeyValuePair<string, string>> damageTypeList, string videoUrl)
		{

            if (SkillExists(name, description, heroId))
            {
                return SelectSkill(name, description, heroId);
            }
            else
            {
                this.skill = new Skill();
                this.abilityTypeList = new List<AbilityType>();
                this.skill.HeroId = heroId;
                this.skill.Name = name;
                this.skill.Description = description;
                this.skill.VideoUrl = videoUrl;

                //Set Up ManaCost
                foreach (var manaCost in manaCostList)
                {
                    //Check if the mana cost is for the current skill
                    //We need to check this because certain skills don't need mana
                    if (manaCost.Key == this.skill.Name)
                    {
                        setManaCost(manaCost.Value);
                    }
                }

                //Set up CoolDowns
                foreach (var coolDown in coolDownList)
                {
                    //Check if the coolDown is for the current skill
                    //We need to check this because certain skills don't have coolDown
                    if (coolDown.Key == this.skill.Name)
                        setCoolDowns(coolDown.Value);
                }

                foreach (var abilityCastType in abilityCastTypeList)
                {
                    //Check if the AbilityCastType is for the current skill
                    //We need to check this because certain skills don't have AbilityCastType
                    if (abilityCastType.Key == this.skill.Name)
                    {
                        if (!string.IsNullOrEmpty(abilityCastType.Value))
                            setAbilityCastType(abilityCastType.Value);
                    }
                }

                foreach (var targetAffectedType in targetAffectedTypeList)
                {
                    //Check if the targetAffectedType is for the current skill
                    //We need to check this because certain skills don't have targetAffectedType
                    if (!string.IsNullOrEmpty(targetAffectedType.Value))
                        setTargetAffectedType(targetAffectedType.Value);
                }

                foreach (var damageType in damageTypeList)
                {
                    //Check if the damageType is for the current skill
                    //We need to check this because certain skills don't have damageType
                    if (!string.IsNullOrEmpty(damageType.Value))
                        setDamageType(damageType.Value);
                }

                InsertSkill();

            }
			return this.skill;
		}
        public static Skill SelectByName(string name)
        {
            Skill skill = new Skill();
            using (Dota2Entities ctx = new Dota2Entities())
            {
                try
                {
                    skill = ctx.Skill.Where(s => s.Name == name).First();
                    if (skill.ID == null)
                        return null;
                }
                catch (Exception e)
                {
                    //TODO adicionar log
                    throw e;
                }
            }

            return skill;
        }