using HeuristicLab.Common;
using HeuristicLab.Core;
using HeuristicLab.Data;
using HeuristicLab.Encodings.PermutationEncoding;
using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace HeuristicLab.Problems.PFSP
{ /// <summary>
  /// An operator which evaluates TSP solutions given in path representation using the rounded Euclidean distance metric.
  /// </summary>
    [Item("AwesomeMakeSpanEvaluator", "An operator which evaluates TSP solutions given in path representation using the rounded Euclidean distance metric.")]
    [StorableClass]
    public sealed class AwesomeMakeSpanEvaluator : MakeSpanEvaluator
    {
        [StorableConstructor]
        private AwesomeMakeSpanEvaluator(bool deserializing) : base(deserializing) { }
        private AwesomeMakeSpanEvaluator(AwesomeMakeSpanEvaluator original, Cloner cloner) : base(original, cloner) { }
        public AwesomeMakeSpanEvaluator() : base() { }

        public override IDeepCloneable Clone(Cloner cloner)
        {
            return new AwesomeMakeSpanEvaluator(this, cloner);
        }

        /// <summary>
        /// Calculates the distance between two points using the rounded Euclidean distance metric.
        /// </summary>
        /// <param name="x1">The x-coordinate of point 1.</param>
        /// <param name="y1">The y-coordinate of point 1.</param>
        /// <param name="x2">The x-coordinate of point 2.</param>
        /// <param name="y2">The y-coordinate of point 2.</param>
        /// <returns>The calculated distance.</returns>
        protected override double CalculateMakespan(DoubleMatrix m, Permutation p)
        {
            DoubleMatrix calculatedTime = new DoubleMatrix(m.Rows, m.Columns);

            for (var machineIdx = 0; machineIdx < m.Rows; machineIdx++)
            {
                for (var jobIdx = 0; jobIdx < m.Columns; jobIdx++)
                {
                    double runtimeCur = m[machineIdx, p[jobIdx]];
                    double runtimePrev = 0;

                    //Simply add to calculatedTimeMatrix
                    if (jobIdx == 0 && machineIdx == 0)
                    {
                        //Take default (runtimePrev = 0)
                    }
                    //Add the time + the previous time
                    else if (machineIdx == 0)
                    {
                        runtimePrev = calculatedTime[machineIdx, jobIdx - 1];
                    }
                    //Check the job-time from the previous machine and previos job and take the greater
                    else if (jobIdx == 0)
                    {
                        runtimePrev = calculatedTime[machineIdx - 1, jobIdx];
                    }
                    //X X X X X
                    //X O O O O
                    //X O O O O
                    //X O O O O
                    else
                    {
                        double runtimePrevMachine = calculatedTime[machineIdx - 1, jobIdx];
                        double runtimePrevJobOnThisMachine = calculatedTime[machineIdx, jobIdx - 1];
                        runtimePrev = runtimePrevMachine > runtimePrevJobOnThisMachine ? runtimePrevMachine : runtimePrevJobOnThisMachine;
                    }

                    calculatedTime[machineIdx, jobIdx] = runtimeCur + runtimePrev;
                }
            }

            //return the makespan(the total runtime from start till the last job is finished)
            return calculatedTime[calculatedTime.Rows - 1, calculatedTime.Columns - 1];
        }
    }
}
