using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EvoBoat.EvoAlg.WWO;

namespace EvoBoat.EvoAlg.IntProg
{
    public class IP_WWOAlg<P,S> : WWOAlg<P, S, IntVector, int>
        where P : IntProgProblem<S>
        where S : VecSolution<IntVector, int>, new()
    {
        public static Func<WWOAlg<P, S, IntVector, int>, S, int, int, double> MovePFunc = (alg, sol, index, d) =>
        {
            sol.Content[d] += (int)Math.Round((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.Rand.Next(alg.Problem.Lowers[d], alg.Problem.Uppers[d]);
            return 0;
        };

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

        public static Func<WWOAlg<P, S, IntVector, int>, S, int, S, S> ReflectFunc = (alg, sol, index, exSol) =>
        {
            int[] vec = new int[alg.Problem.Dimension];
            for (int d = 0; d < alg.Problem.Dimension; d++)
            {
                vec[d] = (int)Math.Round((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.Rand.Next(alg.Problem.Lowers[d], alg.Problem.Uppers[d]);
            }
            S sol1 = new S() { Content = vec };
            alg.Evaluate(sol1);
            return sol1;
        };

        protected IntVector sranges;

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

        public IP_WWOAlg() : base()
        { }

        public IP_WWOAlg(P problem, int seed = 0) : base(problem, seed)
        {
            this.Factory = new IntVectorSolutionFactory<P, S>(problem);
        }

        public override void SetFactory()
        {
            if (Problem == null)
                throw new ArgumentNullException("Please assign the problem instance before set the factory");
            this.Factory = new IntVectorSolutionFactory<P, 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);
        }
    }
}
