/*
         * private List<Job> BuildSuccessorList(Job j)
         * {
         *  throw new Exception("What uses this?");
         *  Console.WriteLine("WARNING: BuildSuccessorList is slow. Are you sure you need it?");
         *  List<Job> Succs = new List<Job>(j.Successors.Count + 1);
         *  Job MSucc = GetMachineSuccessor(j);
         *  Succs.Add(MSucc);
         *  foreach (Job v in j.Successors)
         *  {
         *      if (v != MSucc) { Succs.Add(v); }
         *  }
         *  return Succs;
         * }*/

        /// <summary>
        /// Performs the Action (Job ==> Void) on each Job in Precedence order (topological order). Considers Machine Predecessors.
        /// </summary>
        /// <param name="PerFormAction"></param>
        public void ForeachJobInPrecOrderDo(Action <Job> PerFormAction)
        {
            int[]       nParentsProcessed = new int[PrecedenceDAG.N + 1]; // Position i contains the number of parents of Job with ID i that have been fully updated.
            Stack <Job> AllPredDone       = new Stack <Job>();            // The jobs that will no longer change Rj are those for which all Parents have been considered.

            bool[] IsPushed  = new bool[PrecedenceDAG.N + 1];
            bool[] IsVisited = new bool[PrecedenceDAG.N + 1];
            for (int i = 0; i < PrecedenceDAG.N + 1; i++)
            {
                IsPushed[i]  = false;
                IsVisited[i] = false;
            }

            foreach (Job j in PrecedenceDAG.Jobs)  //All jobs without predecessors can know their final Rj (it is equal to their own rj).
            {
                if (j.Predecessors.Count == 0 &&
                    GetMachinePredecessor(j) == null)
                {
                    AllPredDone.Push(j);
                    IsPushed[j.ID] = true;
                    // ESS[j.ID] = j.EarliestReleaseDate; Do this as part of the action!
                }
            }
            Job CurrentJob = null;
            //algo
            int DebugJobsPopped = 0;

            while (AllPredDone.Count > 0)
            {
                CurrentJob = AllPredDone.Pop();
                if (IsVisited[CurrentJob.ID])
                {
                    // Oops, found it twice.
                    continue;
                }
                DebugJobsPopped++;

                PerFormAction(CurrentJob);

                IsVisited[CurrentJob.ID] = true;

                Job MachineSucc = GetMachineSuccessor(CurrentJob);



                foreach (Job Child in CurrentJob.Successors)
                {
                    nParentsProcessed[Child.ID]++;
                    Job MachinePred = GetMachinePredecessor(Child);
                    if (nParentsProcessed[Child.ID] == Child.Predecessors.Count && (MachinePred == null || IsVisited[MachinePred.ID]))
                    {
                        if (MachinePred == null)
                        {
                            //Console.WriteLine("Debug note: No machine pred for job {0}", Child.ID);
                        }
                        if (!IsPushed[Child.ID])
                        {
                            AllPredDone.Push(Child);
                            IsPushed[Child.ID] = true;
                        }
                    }
                }
                if (MachineSucc != null && nParentsProcessed[MachineSucc.ID] == MachineSucc.Predecessors.Count)
                {
                    if (!IsPushed[MachineSucc.ID])
                    {
                        AllPredDone.Push(MachineSucc);
                        IsPushed[MachineSucc.ID] = true;
                    }
                }
                // missing machine arcs? NO! Machine arcs are dealt with.
            }
            for (int i = 0; i < PrecedenceDAG.N; i++)
            {
                if (!IsVisited[i])
                {
                    Console.WriteLine("ERROR! Printing Schedule for debugging then aborting.");
                    Console.WriteLine("Processed {0}/{1} jobs in Prec Order", DebugJobsPopped, PrecedenceDAG.N);
                    Console.WriteLine("Checking for cycles...");
                    bool Cyclesfound = false;
                    foreach (Machine M in this.Machines)
                    {
                        for (int higherindex = 1; higherindex < M.AssignedJobs.Count; higherindex++)
                        {
                            for (int lowerindex = 0; lowerindex < higherindex; lowerindex++)
                            {
                                if (PrecedenceDAG.PrecPathExists(M.AssignedJobs[higherindex], M.AssignedJobs[lowerindex]))
                                {
                                    Console.WriteLine("Cycle found on M{2}: Job {0} --Marcs--> Job {1} --Precs--> Job {0}", M.AssignedJobs[lowerindex].ID, M.AssignedJobs[higherindex].ID, M.MachineID);
                                    Cyclesfound = true;
                                }
                            }
                        }
                    }
                    if (!Cyclesfound)
                    {
                        Console.WriteLine("No cycles found");
                    }

                    this.Print();
                    this.PrintJobInfo();

                    Console.Write("Missed jobs: ");
                    for (int j = 0; j < PrecedenceDAG.N; j++)
                    {
                        if (!IsVisited[j])
                        {
                            Console.Write("{0}, ", j);
                        }
                    }
                    Console.Write(Environment.NewLine);
                    throw new Exception("Not all jobs visited!");
                }
            }
        }