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

namespace EvoBoat.EvoAlg.WWO
{
    public class Num_DWWOAlg<S> : Num_SimWWOAlg<S>
        where S : VecSolution<Vector, double>, new()
    {
        protected IntVector[] directions;

        public Num_DWWOAlg() : base()
        {
            Name = "DWWO";
        }

        public Num_DWWOAlg(NumOptProblem<S> problem, int seed = 0) : base(problem, seed)
        {
            Name = "DWWO";
        }

        public override void SetFactory()
        {
            if (Problem == null)
                throw new ArgumentNullException("Please assign the problem instance before set the factory");
            Factory = new VectorSolutionFactory<NumOptProblem<S>,S>(Problem);
        }

        public override void InitPop()
        {
            base.InitPop();
            directions = new IntVector[pop.Length];
            for (int i = 0; i < pop.Length; i++)
            {
                directions[i] = new IntVector(Problem.Dimension);
                for (int d = 0; d < Problem.Dimension; d++)
                    directions[i][d] = (Rand.Next() % 2 == 0) ? 1 : -1;
            }
        }

        public override double MoveP(S sol, int index, int d)
        {
            sol.Content[d] += directions[index][d] * Rand.NextDouble() * WaveLengths[index] * Problem.Ranges[d];
            if (sol.Content[d] < Problem.Lowers[d] || sol.Content[d] > Problem.Uppers[d])
                sol.Content[d] = Problem.Lowers[d] + Rand.NextDouble() * Problem.Ranges[d];
            return 0;
        }

        public override S Propagate(S sol, int index)
        {
            int K = Rand.Next(1, Problem.Dimension);
            int[] sels = RandomSelectKIndicies(Problem.Dimension, K);
            S sol1 = new S() { Content = sol.Content.Clone() };
            foreach (int d in sels)
                MoveP(sol1, index, d);
            if(this.Evaluate(sol1)>sol.Fit)
                foreach (int d in sels)
                    directions[index][d] = -directions[index][d];
            return sol1;
        }

        public override void ReduceSize()
        {
            if (pop.Length > SizeMin)
            {
                if (iWorst != pop.Length - 1)
                {
                    pop[iWorst] = pop[pop.Length - 1];
                    wavelengths[iWorst] = wavelengths[pop.Length - 1];
                    directions[iWorst] = directions[pop.Length - 1];
                }
                pop = pop.Take(pop.Length - 1).ToArray();
                wavelengths = wavelengths.Take(pop.Length).ToArray();
                directions = directions.Take(pop.Length).ToArray();
                this.UpdateWorst();
            }
        }
    }
}
