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

namespace MachUtilPlanner
{
    /// <summary>
    /// Machine Utilization Planning Problem (MUP)
    /// </summary>
    public class MachUtilProblem : IntDivisionProblem<VecSolution<IntVector, int>>
    {
        int[] ut, t0;
        public int[] UseTime { get { return ut; } }
        public int[] T0 { get { return t0; } }

        internal int[] cT;

        int mt;
        public int MaintenanceTime { get { return mt; } }

        double wt;
        public double Weight { get { return wt; } }

        public FailureRateFunc FailureRateFunction { get; set; }

        /// <summary>
        /// Create an MUP instance
        /// </summary>
        /// <param name="W">total workload</param>
        /// <param name="n">number of machines</param>
        /// <param name="ut">the array of used time of the machines</param>
        /// <param name="t0">the array of used time since the last maintenance of the machines</param>
        public MachUtilProblem(FailureRateFunc fr_func, int W, int n, int mt, double wt, int[] ut, int[] t0) : base(W, n)
        {
            this.FailureRateFunction = fr_func;
            this.ut = ut;
            this.t0 = t0;
            this.mt = mt;
            this.wt = wt;
        }

        public override string ToString()
        {
            return string.Format("W={0},n={1}", Total, Dimension);
        }

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

        public override double Evaluate(IntVector x)
        {
            if(cT == null)
            {
                cT = new int[dimension];
                for (int i = 0; i < dimension; i++)
                {
                    cT[i] = (int)Math.Round(1.0 / this.FailureRateFunction(ut[i])) - t0[i];
                    if (cT[i] < 0)
                        cT[i] = 0;
                }
            }
            int sumk = 0, maxT = 0, T;
            for (int i = 0, k; i < dimension; i++)
            {
                T = this.Calculate(i, x[i], out k);
                T += k * mt;
                maxT = Math.Max(maxT, T);
                sumk += k;
            }
            return maxT + wt * sumk;
        }

        public double Evaluate(IntVector x, out int K)
        {
            if (cT == null)
            {
                cT = new int[dimension];
                for (int i = 0; i < dimension; i++)
                {
                    cT[i] = (int)Math.Round(1.0 / this.FailureRateFunction(ut[i])) - t0[i];
                    if (cT[i] < 0)
                        cT[i] = 0;
                }
            }
            int maxT = 0, T;
            K = 0;
            for (int i = 0, k; i < dimension; i++)
            {
                T = this.Calculate(i, x[i], out k);
                T += k * mt;
                maxT = Math.Max(maxT, T);
                K += k;
            }
            return maxT + wt * K;
        }

        static Random Rand = new Random((int)DateTime.Now.Ticks);
        
        public static MachUtilProblem TestProblem(ThreePhaseFailureRate tpfr, int workload, int n, int mt, double wt, double percent1 = 0.5, double percent2 = 0.4, double percent3 = 0.1)
        {
            int[] ut = new int[n];
            int[] t0 = new int[n];
            for (int i=0; i< n; i++)
            {
                double r = Rand.NextDouble(), fr;
                if (r < percent1)
                {
                    ut[i] = (int)(Rand.NextDouble() * tpfr.T1);
                    fr = FailureRate.Weibull(ut[i], tpfr.m1, tpfr.eta1, tpfr.gamma1);
                }
                else if (r < percent2)
                {
                    ut[i] = (int)(tpfr.T1 + Rand.NextDouble() * (tpfr.T2 - tpfr.T1));
                    fr = FailureRate.Weibull(ut[i], tpfr.m2, tpfr.eta2, tpfr.gamma2);
                }
                else
                { 
                    ut[i] = (int)(tpfr.T2 + Rand.NextDouble() * (tpfr.T3 - tpfr.T2));
                    fr = FailureRate.Weibull(ut[i], tpfr.m3, tpfr.eta3, tpfr.gamma3);
                }
                t0[i] = (int)(Rand.NextDouble() * (1.0 / fr) );
            }
            return new MachUtilProblem(tpfr.FailureRateFunc, workload, n, mt, wt, ut, t0);
        }
    }    
}
