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

namespace MachUtilPlanner
{
    public class MUP_WWOAlg : IntDiv_WWOAlg<MachUtilProblem, VecSolution<IntVector, int>>
    {
        int nom;

        public MUP_WWOAlg() : base()
        {
            Name = "WWO-E";
        }

        public MUP_WWOAlg(MachUtilProblem problem, int seed = 0) : base(problem, seed)
        {
            Name = "WWO-E";
        }

        public override void InitPop()
        {
            //op = Problem;
            //Problem = MUPExtension.Reduce(Problem, out eT);
            base.InitPop();
            pop[iWorst] = new VecSolution<IntVector, int>(new IntVector(MUPExtension.MinNom(Problem, out nom)));
            Evaluate(pop[iWorst]);
            UpdateBestWorst();
        }

        public int Calculate(int i, int x_i, out int nom, out int exceedTime, out int avaTime)
        {
            nom = exceedTime = avaTime = 0;
            int ct = Problem.cT[i];
            if (x_i <= ct)
            {
                exceedTime = x_i;
                avaTime = ct - x_i;
                return x_i;
            }
            x_i -= ct;
            int T = Problem.UseTime[i] + ct;
            while (x_i > 0)
            {
                ct = (int)Math.Round(1.0 / Problem.FailureRateFunction(T));
                if (ct <= 1) //MTBF=1
                {
                    nom += x_i;
                    T += nom * Problem.MaintenanceTime;
                    exceedTime = 1;
                    avaTime = 0;
                    break;
                }
                if (x_i <= ct)
                {
                    T += x_i;
                    exceedTime = x_i;
                    avaTime = ct - x_i;
                    break;
                }
                else
                {
                    T += ct;
                    x_i -= ct;
                }
                nom++;
            }
            return T - Problem.UseTime[i];
        }

        public override double Evaluate(VecSolution<IntVector, int> sol)
        {
            int d, d1, d2;
            if (sol.Content.Sum() != Problem.Total)
                d = 0;
            if (Problem.cT == null)
            {
                Problem.cT = new int[Problem.Dimension];
                for (d = 0; d < Problem.Dimension; d++)
                {
                    Problem.cT[d] = (int)Math.Round(1.0 / Problem.FailureRateFunction(Problem.UseTime[d])) - Problem.T0[d];
                    if (Problem.cT[d] < 0)
                        Problem.cT[d] = 0;
                }
            }
            int sumk = 0;
            int[] TS = new int[Problem.Dimension], excT = new int[Problem.Dimension], avaT = new int[Problem.Dimension], noms = new int[Problem.Dimension];
            for (d = 0; d < Problem.Dimension; d++)
            {
                TS[d] = Calculate(d, sol.Content[d], out noms[d], out excT[d], out avaT[d]);
                TS[d] += noms[d] * Problem.MaintenanceTime;
                sumk += noms[d];
            }

            while (true)
            {
                for (d = 0, d1 = 0; d < Problem.Dimension; d++)
                    if (TS[d] > TS[d1])
                        d1 = d;
                if (excT[d1] <= 0)
                    break;
                for (d = 0, d2 = 0; d < Problem.Dimension; d++)
                    if (d != d1 && avaT[d] >= 1 && TS[d] < TS[d2] && TS[d] + 1 <= TS[d1] - 1)
                        d2 = d;
                if (avaT[d2] >= 1 && TS[d2] + 1 <= TS[d1] - 1)
                {
                    sol.Content[d2]++;
                    excT[d2]++;
                    TS[d2]++;
                    avaT[d2]--;
                    sol.Content[d1]--;
                    excT[d1]--;
                    TS[d1]--;
                    avaT[d1]++;
                }
                else
                    break;
            }
            if (sol.Content.Sum() != Problem.Total)
                d = 0;
            nfe++;
            sol.Fit = TS.Max() + Problem.Weight * sumk;
            return sol.Fit;
        }
    }
}
