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

namespace EvoBoat.EvoAlg.WWO
{
    public abstract class COMB_NWWOAlg<P, S, R, e> : SimWWOAlg<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_NWWOAlg() : base()
        {
            Name = "COMB_NWWO";
        }

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

        public override void InitPop()
        {
            pop = Factory.CreatePop(size);
            this.Evaluate(pop[0]);
            best = worst = pop[0];
            for (int i = 1; i < size; i++)
            {
                this.Evaluate(pop[i]);
                if (pop[i].Fit < best.Fit)
                {
                    best = pop[i];
                    iBest = i;
                }
                else if (pop[i].Fit > worst.Fit)
                {
                    worst = pop[i];
                    iWorst = i;
                }
            }
            wavelengths = new double[size];
            int len = Problem.Dimension / 2;
            for (int i = 0; i < size; i++)
                wavelengths[i] = len;
        }

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

        public override void Calculate()
        {
            double den = (worst.Fit - best.Fit + epsilon);
            for (int i = 0; i < pop.Length; i++)
                wavelengths[i] = Math.Pow(Problem.Dimension, (pop[i].Fit - best.Fit + epsilon) / den);
        }

        /// <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] < UpperIncSteps);
        }

        public override S Propagate(S sol, int index)
        {
            S sol1 = new S() { Content = sol.Content.Clone(), Fit = sol.Fit };
            int steps = Rand.Next(1, (int)Math.Round(wavelengths[index]) + 1);
            int[] sels = RandomSelectKIndicies(Problem.Dimension, steps);
            Array.Sort(sels);
            double incr = 0;
            foreach (int d in sels)
                incr += MoveP(sol1, index, d);
            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;
        }
    }
}
