using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EvoBoat.EvoAlg.WWO
{
    public abstract class COMB_WWOAlg<P, S, R, e> : VarWWOAlg<P, S, R, e>
        where P : IProblem<S, R>
        where S : VecSolution<R, e>, new()
        where R : IVector<e>, ICloneable<R>
    {
        /// <summary>
        /// whether the fitness of a moved solution can be evaluated by increment or from scratch
        /// </summary>
        public bool CanFitIncrement { get; set; }

        public int UpperIncSteps { get; set; }

        public COMB_WWOAlg() : base()
        {
            Name = "COMB_WWO";
        }

        public COMB_WWOAlg(P problem, int seed = 0) : base(problem, seed)
        {
            Name = "COMB_WWO";
        }

        public override void SetParameters()
        {
            base.SetParameters();
            UpperIncSteps = 2;
        }

        /// <summary>
        /// whether the fitness of a propagated solution will be evaluated by increment or from scratch
        /// </summary>
        public virtual bool NoNewEva(S sol, int index)
        {
            return (CanFitIncrement && wavelengths[index] * Problem.Dimension < UpperIncSteps);
        }

        public override S Propagate(S sol, int index)
        {
            S sol1 = new S() { Content = sol.Content.Clone(), Fit = sol.Fit };
            bool bChanged = false;
            double incr = 0;
            for (int d = 0; d < Problem.Dimension; d++)
                if (Rand.NextDouble() < wavelengths[index])
                {
                    incr += MoveP(sol1, index, d);
                    if (!bChanged)
                        bChanged = true;
                }
            if (bChanged)
            {
                if (NoNewEva(sol, index)) sol1.Fit += incr;
                else this.Evaluate(sol1);
            }
            return sol1;
        }

        public override S Break(S sol, int index, int times)
        {
            if (times >= Problem.Dimension / 2)
                times = Problem.Dimension / 2;
            S bnsol = sol, sol1;
            int[] sels = RandomSelectKIndicies(Problem.Dimension, times);
            Array.Sort(sels);
            double incr;
            foreach(int d in sels)
            {
                sol1 = new S() { Content = sol.Content.Clone(), Fit = sol.Fit };
                incr = this.MoveB(sol1, index, d);
                if (CanFitIncrement)
                    sol1.Fit += incr;
                else
                    this.Evaluate(sol1);
                if (sol1.Fit < bnsol.Fit)
                    bnsol = sol1;
            }
            return bnsol;
        }
    }
}
