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

namespace EvoBoat.EvoAlg.WWO
{
    public class Num_WWOAlg<S> : WWOAlg<NumOptProblem<S>, S, Vector, double>
        where S : VecSolution<Vector, double>, new()
    {
        public static Func<WWOAlg<NumOptProblem<S>, S, Vector, double>, S, int, int, double> MovePFunc = (alg, sol, index, d) =>
        {
            sol.Content[d] += (alg.Rand.NextDouble() * 2 - 1) * alg.WaveLengths[index] * alg.Problem.Ranges[d];
            if (sol.Content[d] < alg.Problem.Lowers[d] || sol.Content[d] > alg.Problem.Uppers[d])
                sol.Content[d] = alg.Problem.Lowers[d] + alg.Rand.NextDouble() * alg.Problem.Ranges[d];
            return 0;          
        };

        public static Func<WWOAlg<NumOptProblem<S>, S, Vector, double>, S, int, int, double, double> MoveBFunc = (alg, sol, index, d, srange) =>
        {
            double olde = sol.Content[d], oldf = sol.Fit;
            sol.Content[d] += alg.gRand.NextDouble() * srange;
            if (sol.Content[d] < alg.Problem.Lowers[d] || sol.Content[d] > alg.Problem.Uppers[d])
                sol.Content[d] = alg.Problem.Lowers[d] + alg.Rand.NextDouble() * alg.Problem.Ranges[d];
            if (alg.Evaluate(sol) > oldf)
            {
                sol.Content[d] = olde;
                sol.Fit = oldf;
            }
            return 0;
        };

        public static Func<WWOAlg<NumOptProblem<S>, S, Vector, double>, S, int, S, S> ReflectFunc = (alg, sol, index, exSol) =>
        {
            double[] vec = new double[alg.Problem.Dimension];
            for (int d = 0; d < alg.Problem.Dimension; d++)
            {
                vec[d] = (sol.Content[d] + exSol.Content[d]) / 2 + alg.gRand.NextDouble() * (exSol.Content[d] - sol.Content[d]) / 2;
                if (vec[d] < alg.Problem.Lowers[d] || vec[d] > alg.Problem.Uppers[d])
                    vec[d] = alg.Problem.Lowers[d] + alg.Rand.NextDouble() * alg.Problem.Ranges[d];
            }
            S sol1 = new S() { Content = vec };
            alg.Evaluate(sol1);
            return sol1;
        };

        protected Vector sranges;

        protected double beta;
        public double BetaMax { get; set; }
        public double BetaMin { get; set; }

        public Num_WWOAlg() : base()
        { }

        public Num_WWOAlg(NumOptProblem<S> problem, int seed = 0) : base(problem, seed)
        {
            this.Factory = new VectorSolutionFactory<NumOptProblem<S>, S>(problem);
        }

        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 SetParameters()
        {
            base.SetParameters();
            BetaMax = 0.25;
            BetaMin = 0.001;
        }

        public override void InitPop()
        {
            base.InitPop();
            beta = BetaMax;
            sranges = beta * Problem.Ranges;
        }

        public override void Calculate()
        {
            base.Calculate();
            if (iters > 0)
                beta = BetaMax - (BetaMax - BetaMin) * nfe / nfes;
            else if (iters > 0)
                beta = BetaMax - (BetaMax - BetaMin) * iter / iters;
            sranges = beta * Problem.Ranges;
        }

        public override double MoveP(S sol, int index, int d)
        {
            return MovePFunc(this, sol, index, d);
        }

        public override double MoveB(S sol, int index, int d)
        {
            return MoveBFunc(this, sol, index, d, sranges[d]);
        }

        public override S Reflect(S sol, int index)
        {
            return ReflectFunc(this, sol, index, best);
        }
    }
}
