using Algorithms.Nw.ShortestPath;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using U.Graph;
using U.StringOperations;

namespace Algorithms.Nw.PathGeneration
{
    public class LinkPenalty : PathGenerationAlgorithm
    {
        private ShortestPathAlgorithm spa;
        private double penalty; // if 0.05 means that fftt of 100 will be 105
        private long iterLim;
        private double[] originalCosts;
        public LinkPenalty(ShortestPathAlgorithm shortestPathAlgorithm, double penalty, long iterationLimit)
        {
            this.spa = shortestPathAlgorithm;
            this.penalty = penalty;
            this.iterLim = iterationLimit;
        }


        public override void run(Graph graph, int oriIndex, int desIndex, int nbExistingPaths, int maxNbPaths, bool keepTime)
        {
            if (keepTime) { base.startTimer(); }
            base.lstNodeIndices = new List<List<int>>();
            if (nbExistingPaths >= maxNbPaths) { return; }
            int nbPathsToGenerate = maxNbPaths - nbExistingPaths;

            string str;
            initializeOriginalCosts(graph);

            spa.run(graph, null, oriIndex, desIndex, false);
            List<int> sp = spa.getShortestPathAsNodeIndices(oriIndex, desIndex);
            int pathCount = 0;
            bool cs = (sp != null);
            if (cs)
            {
                str = Str.combine(sp, Str.Delimiter.Dash);
                penalizeArcs(graph, sp);
                if (!existingPathLabels.Contains(str))
                {
                    pathCount += 1;
                    base.lstNodeIndices.Add(sp);
                    addToExisting(str);
                }
            }
            int iterCount = 0;
            while (cs && iterCount < iterLim && pathCount < nbPathsToGenerate)
            {
                iterCount += 1;
                spa.run(graph, null, oriIndex, desIndex, false);
                sp = spa.getShortestPathAsNodeIndices(oriIndex, desIndex);
                cs = (sp != null);
                if (cs)
                {
                    penalizeArcs(graph, sp);
                    str = Str.combine(sp, Str.Delimiter.Dash);
                    if (!existingPathLabels.Contains(str))
                    {
                        pathCount += 1;
                        base.lstNodeIndices.Add(sp);
                        addToExisting(str);
                    }
                }
            }
            returnToOriginalCosts(graph);
            if (keepTime) { base.stopTimer(); }
        }


        #region PRIVATE HELPERS
        private void initializeOriginalCosts(Graph graph)
        {
            this.originalCosts = new double[graph.getNbArcs()];
            for (int a = 0; a < graph.getNbArcs(); a++) { originalCosts[a] = graph.getArc(a).getCost(); }
        }
        private void penalizeArcs(Graph graph, List<int> path)
        {
            for (int i = 0; i < path.Count - 1; i++)
            {
                int from = path[i];
                int to = path[i + 1];
                int a = graph.getIncidenceMatrix()[from, to];
                Arc arc = graph.getArc(a);
                arc.setCost(arc.getCost() * (1.0 + penalty));
            }
        }
        private void returnToOriginalCosts(Graph graph)
        {
            for (int a = 0; a < graph.getNbArcs(); a++) { Arc arc = graph.getArc(a); arc.setCost(originalCosts[a]); }
        } 
        #endregion

    }
}
