/*public bool IsProperTarget(PlayedFighter caster, Fighter target, Spell spell)
         * {
         *  // SpellTargetType
         *  if (target == null) return spell.LevelTemplate.needFreeCell && spell.IsAvailable(null, null);
         *
         *  foreach (var spellEffect in spell.GetEffects())
         *      if (EffectBase.canAffectTarget(spellEffect, spell, caster, target)) return true;
         *  return false;
         * }*/
        #endregion Availability management

        #region Spell selection

        /*public IEnumerable<Spell> GetOrderListOfSimpleBoostSpells(PlayedFighter caster, Fighter target, Spell.SpellCategory category)
         * {
         *  return m_spells.Where(spell => (caster.Stats.CurrentAP >= spell.LevelTemplate.apCost) && spell.IsAvailable(caster.Id, category) && IsProperTarget(caster, target, spell)).OrderByDescending(spell => spell.Level).ThenByDescending(spell => spell.LevelTemplate.minPlayerLevel);
         * }
         *
         * public IEnumerable<Spell> GetOrderedAttackSpells(PlayedFighter caster, Fighter target, Spell.SpellCategory category = Spell.SpellCategory.Damages)
         * {
         *  Debug.Assert(((category & Spell.SpellCategory.Damages) > 0), "category do not include Damage effects");
         *  return m_spells.Where(spell =>
         *      (caster.Stats.CurrentAP >= spell.LevelTemplate.apCost) &&
         *      spell.IsAvailable(target.Id, category) &&
         *      IsProperTarget(caster, target, spell))
         *      .OrderByDescending(spell => (int)(spell.GetSpellDamages(caster, target, Spell.SpellCategory.Damages).Damage) * (caster.Stats.CurrentAP / (int)spell.LevelTemplate.apCost));
         * }*/


        public SpellTarget FindBestUsage(PlayedFighter pc, List <int> spellIds, IEnumerable <Cell> possiblePlacement = null)
        {
            Stopwatch timer = new Stopwatch();

            timer.Start();
            PathFinder         pathFinder  = new PathFinder(pc.Fight, true);
            IEnumerable <Cell> sourceCells = possiblePlacement == null?pc.GetPossibleMoves(true, true, pathFinder) : possiblePlacement;

            IEnumerable <Fighter> actorsWithoutPC = pc.Fight.AliveActors.Where(actor => actor != pc);
            SpellTarget           spellTarget     = new SpellTarget();

            foreach (int spellId in spellIds)
            {
                Spell spell = m_spells.FirstOrDefault(sp => sp.Template.id == spellId);
                if (spell == null && spellId == 0)
                {
                    spell = WeaponSpellLike();
                }
                if (spell != null)
                {
                    if (spell.IsAvailable(null) && ((spellId != 0 && pc.CanCastSpells) || (spellId == 0 && pc.CanFight)) && spell.LevelTemplate.apCost <= pc.Stats.CurrentAP && pc.CanCastSpell(spellId))
                    {
                        if (spell != null && spell.IsAvailable(null) && spell.LevelTemplate.apCost <= pc.Stats.CurrentAP)
                        {
                            SpellTarget lastSpellTarget = spell.FindBestTarget(pc, sourceCells, actorsWithoutPC, Spell.SpellCategory.All);
                            if (lastSpellTarget != null && lastSpellTarget.Efficiency > spellTarget.Efficiency)
                            {
                                spellTarget = lastSpellTarget;
                                break; // Stop on the first spell with positive efficiency
                            }
                        }
                    }
                }
            }
            timer.Stop();
            spellTarget.TimeSpan = timer.Elapsed;
            return(spellTarget);
        }
        public SpellTarget FindBestUsage(PlayedFighter pc, Spell.SpellCategory category, bool withWeapon, IEnumerable <Cell> possiblePlacement = null)
        {
            SpellTarget spellTarget = new SpellTarget();

            if (pc.PCStats.CurrentAP <= 0)
            {
                return(spellTarget);
            }
            Stopwatch timer = new Stopwatch();

            timer.Start();
            PathFinder         pathFinder  = new PathFinder(pc.Fight, true);
            IEnumerable <Cell> sourceCells = possiblePlacement == null?pc.GetPossibleMoves(true, true, pathFinder) : possiblePlacement;

            IEnumerable <Fighter> actorsWithoutPC = pc.Fight.AliveActors.Where(actor => actor != pc);
            List <Spell>          spells          = m_spells.ToList();

            if (withWeapon && ((category & (Spell.SpellCategory.Damages | Spell.SpellCategory.Healing)) != 0))
            {
                spells.Add(WeaponSpellLike());
            }
            //logger.Debug("***FindBestUsage {0}, {1} spells in book. {2} PA. {3}/{4} HP ***", category, spells.Count, pc.PCStats.CurrentAP, pc.PCStats.Health, pc.PCStats.MaxHealth);
            Object thisLock = new Object();
            //foreach(Spell spell in spells)
            int NbSpellsChecked = 0;

            Parallel.ForEach(spells, spell =>
            {
                NbSpellsChecked++;
                if (spell != null && !IgnoredSpells.Contains(spell.Template.id))
                {
                    int spellId = spell.Template.id;
                    if (spell.IsAvailable(null) && ((spellId != 0 && pc.CanCastSpells) || (spellId == 0 && pc.CanFight)) && spell.LevelTemplate.apCost <= pc.Stats.CurrentAP && pc.CanCastSpell(spellId))
                    {
                        if ((spell.Categories & category) > 0)
                        {
                            SpellTarget lastSpellTarget = spell.FindBestTarget(pc, sourceCells, actorsWithoutPC, category);
                            if (lastSpellTarget != null && lastSpellTarget.Efficiency > spellTarget.Efficiency)
                            {
                                //lock (thisLock)
                                spellTarget = lastSpellTarget;
                            }
                            //if (lastSpellTarget != null)
                            //    logger.Debug("efficiency {0} = {1} ({2})", lastSpellTarget.Spell, lastSpellTarget.Efficiency, lastSpellTarget.Comment);
                            //lock (thisLock)
//                                    logger.Debug("efficiency {0} = ???", spell);
//                            else
                            //lock (thisLock)
                            //                                  logger.Debug("efficiency {0} = {1} ({2})", lastSpellTarget.Spell, lastSpellTarget.Efficiency, lastSpellTarget.Comment);
                        }
                    }
                    else
                    {
                        //                      lock (thisLock)
                        //                          logger.Info("{0} skipped : available={1} ({6}), canUse={2}, ApCost={3}, CanCast({4})={5}", spell, spell.IsAvailable(null), ((spellId != 0 && pc.CanCastSpells) || (spellId == 0 && pc.CanFight)), spell.LevelTemplate.apCost <= pc.Stats.CurrentAP, spellId, pc.CanCastSpell(spellId), spell.AvailabilityExplainString(null));
                    }
                }
            }
                             );
            //Debug.Assert(NbSpellsChecked == spells.Count);
            timer.Stop();
            spellTarget.TimeSpan = timer.Elapsed;
            //pc.Character.SendInformation("Spell {0} selected - efficientcy : {1} - comment = {2}", spellTarget.Spell, spellTarget.Efficiency, spellTarget.Comment);
            return(spellTarget);
        }