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

namespace EvoBoat.EvoAlg.IntDivision
{
    public class IntDiv_NumWWOAlg<P, S> : VarWWOAlg<P, S, IntVector, int>
        where P : IntDivisionProblem<S>
        where S : VecSolution<IntVector, int>, new()
    {
        protected int beta;
        public int BetaMax { get; set; }
        public int BetaMin { get; set; }

        public IntDiv_NumWWOAlg() : base()
        { }

        public IntDiv_NumWWOAlg(P problem, int seed = 0) : base(problem, seed)
        {
            this.Factory = new IntDivisionSolutionFactory<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 IntDivisionSolutionFactory<P, S>(Problem);
        }

        public override void SetParameters()
        {
            base.SetParameters();
            BetaMax = (int)Math.Round(Math.Floor(Problem.Avg / 4));
            if (BetaMax < 1) BetaMax = 1;
            beta = BetaMax;
            BetaMin = 1;
        }

        public override void Calculate()
        {
            base.Calculate();
            beta = (int)Math.Round(BetaMax - (double)(BetaMax - BetaMin) * nfe / nfes);
            if (beta < 1)
                beta = 1;
        }

        public override double MoveP(S sol, int index, int d)
        {
            int olde = sol.Content[d];
            int dep = (int)Math.Round(Rand.NextDouble() * 4 * wavelengths[index] * Problem.Avg);
            if (Rand.Next() % 2 == 0)
                dep = -dep;
            sol.Content[d] += dep;
            if (sol.Content[d] < 0 || sol.Content[d] > Problem.Total)
                sol.Content[d] = Rand.Next(0, (int)Math.Round(Problem.Avg + 1));
            Problem.RepairAt(this, sol, d, sol.Content[d] - olde);
            return 0;
        }

        public override double MoveB(S sol, int index, int d)
        {
            if (Rand.Next() % 2 == 0 && sol.Content[d] > beta)
            {
                int d1 = RandomSelectIndex(Problem.Dimension, d);
                sol.Content[d] -= beta;
                sol.Content[d1] += beta;
                if (Evaluate(sol) > sol.Fit)
                {
                    sol.Content[d] += beta;
                    sol.Content[d1] -= beta;
                }
            }
            else
            {
                int maxchange = Problem.Total - sol.Content[d];
                int change = (beta <= maxchange) ? beta : maxchange;
                sol.Content[d] += change;
                int[] changes = Problem.RepairAt(this, sol, d, change);
                if (Evaluate(sol) > sol.Fit)
                {
                    sol.Content[d] -= change;
                    for(int d1=0; d1<Problem.Dimension; d1++)
                        sol.Content[d1] -= changes[d1];
                }
            }
            return 0;
        }

        public override S Reflect(S sol, int index)
        {
            return IntDiv_WWOAlg<P, S>.ReflectFunc(this, sol, index, best);
        }
    }
}
