private static int calcTotalPendees(CourseContainer course, HashSet <CourseContainer> counted)
        {
            // return value if value already exists
            if (course.TotalPendees != 0)
            {
                return(course.TotalPendees);
            }
            else if (counted.Contains(course))
            {
                return(-1);
            }
            // return 0 if both are empty
            else if (course.Dependees.Count == 0 && course.Copendees.Count == 0)
            {
                counted.Add(course);
                return(0);
            }
            // else, recurse
            else
            {
                int count = 0;
                foreach (CourseContainer dependee in course.Dependees)
                {
                    count += calcTotalPendees(dependee, counted) + 1;
                }
                foreach (CourseContainer copendee in course.Copendees)
                {
                    count += calcTotalPendees(copendee, counted) + 1;
                }

                //set and return value
                course.TotalPendees = count;
                return(count);
            }
        }
        private static int calcTotalPendees(CourseContainer course)
        {
            //variables
            HashSet <CourseContainer> counted = new HashSet <CourseContainer>();  //used to prevent duplicate counts

            return(calcTotalPendees(course, counted));
        }
        //helper methods

        private static int pendeeDepth(CourseContainer course, Semester startingSemester, List <Semester> semesters)
        {
            //variables
            List <int> depths = new List <int>();
            int?       returnInt;

            // if course already has value, return value
            if (course.PendeeDepth != null)
            {
                return((int)course.PendeeDepth);
            }

            // if course has no dependees/codepedees it has no depth
            if (course.Dependees.Count == 0 && course.Copendees.Count == 0)
            {
                course.PendeeDepth = 0;
                return(0);
            }

            //  check if starting semester is a valid semester for the course, if not, pass to next semester
            if (!course.Course.validTerms.Contains(startingSemester.Term))
            {
                returnInt          = pendeeDepth(course, nextSemester(startingSemester, semesters), semesters) + 1;
                course.PendeeDepth = returnInt;
                return((int)returnInt);
            }

            // if so, recurse each dependee/copendee and store their depths in list
            else
            {
                // recurse dependees
                foreach (CourseContainer dependee in course.Dependees)
                {
                    depths.Add(pendeeDepth(dependee, nextSemester(startingSemester, semesters), semesters));
                }
                // recurse copendees
                foreach (CourseContainer copendee in course.Copendees)
                {
                    depths.Add(pendeeDepth(copendee, startingSemester, semesters));
                }
            }

            returnInt          = depths.Max() + 1;
            course.PendeeDepth = returnInt;
            return((int)returnInt);
        }
        private static bool requirementsMet(CourseContainer course, IEnumerable <CourseContainer> scheduledCourses,
                                            IEnumerable <CourseContainer> currentCourses)
        {
            //check dependents
            foreach (CourseContainer dependent in course.Dependents)
            {
                if (!scheduledCourses.Contains(dependent))
                {
                    return(false);
                }
            }

            // check copendents
            foreach (CourseContainer copendent in course.Copendents)
            {
                if (!scheduledCourses.Contains(copendent) && !currentCourses.Contains(copendent))
                {
                    return(false);
                }
            }

            // check addtional details
            CourseDetails details = course.Course.courseDetails;

            // sum previously scheduled courses
            int total = 0;

            foreach (CourseContainer scheduledCourse in scheduledCourses)
            {
                total += scheduledCourse.Course.credits;
            }

            // check to ensure credit requirment or standing requirement
            if (details.CreditsRequired < total)
            {
                return(false);
            }

            //if requrirements met, return true
            return(true);
        }
        /// <summary>
        /// coursePriority takes in a course list and attempts to prioritise the courses based on dependencies.
        /// It returns a HashSet of prioritised CourseContainer objects that wrap around the course objects.
        /// </summary>
        /// <param name="coursesIn"></param>
        /// <returns></returns>
        public static HashSet <CourseContainer> genCourseContainers(List <Course> coursesIn)
        {
            //define variables
            HashSet <Course>                     courses             = new HashSet <Course>(coursesIn);
            HashSet <CourseContainer>            processedContainers = new HashSet <CourseContainer>();
            Dictionary <Course, CourseContainer> courseLookup        = new Dictionary <Course, CourseContainer>();

            // repeatedly look through remaining courses finding the dependencies that have been processed
            // each time adding the course as an dependee
            Boolean skip;

            while (courses.Count != 0)
            {
                foreach (Course course in courses.ToList())
                {
                    //verify each pendent has been processed
                    skip = false;
                    foreach (Course dependent in course.dependencies)
                    {
                        if (!courseLookup.ContainsKey(dependent))
                        {
                            skip = true;
                            continue;
                        }
                    }
                    // skip course if pendents have not been processed yet
                    if (skip)
                    {
                        continue;
                    }
                    foreach (Course copendent in course.copendencies)
                    {
                        if (!courseLookup.ContainsKey(copendent))
                        {
                            skip = true;
                            continue;
                        }
                    }
                    // skip course if pendents have not been processed yet
                    if (skip)
                    {
                        continue;
                    }


                    //skip after inintial code if course has no pendents
                    if (course.dependencies.Count == 0 && course.copendencies.Count == 0)
                    {
                        skip = true;
                    }
                    else
                    {
                        skip = false;
                    }

                    //create the course container
                    CourseContainer courseCon = new CourseContainer(course);
                    //move independent courses to processed
                    processedContainers.Add(courseCon);
                    courses.Remove(course);

                    //attempt to add the course to the dictionary
                    if (!courseLookup.ContainsKey(course))
                    {
                        courseLookup.Add(course, courseCon);
                    }

                    if (skip)
                    {
                        continue;
                    }

                    //add course as a de/co pendee to dependent coursecontainers and add de/co pendents to coursecontainer
                    foreach (Course dependent in course.dependencies)
                    {
                        //get Container of dependent
                        CourseContainer dependentCon;
                        courseLookup.TryGetValue(dependent, out dependentCon);
                        //add dependee
                        if (dependentCon != null)
                        {
                            dependentCon.Dependees.Add(courseCon);
                        }
                        else
                        {
                            throw new Exception("Course dependent has not been added yet!");
                        }
                        //add dependent to course
                        courseCon.Dependents.Add(dependentCon);
                    }

                    foreach (Course copendent in course.copendencies)
                    {
                        //Get Container of copendent
                        CourseContainer copendentCon;
                        courseLookup.TryGetValue(copendent, out copendentCon);
                        //add copendee
                        if (copendentCon != null)
                        {
                            copendentCon.Copendees.Add(courseCon);
                        }
                        else
                        {
                            throw new Exception("Course copendent has not been added yet!");
                        }
                        //add copendent to course
                        courseCon.Copendents.Add(copendentCon);
                    }
                }
            }


            //calculate course pendee TotalSize by recursion
            foreach (CourseContainer course in processedContainers)
            {
                calcTotalPendees(course);
            }

            //return the Course Container set
            return(processedContainers);
        }
        public static bool scheduleSemesters(List <CourseContainer> courseList, List <Semester> semesters,
                                             Dictionary <Semester, List <Course> > manualAddDictIn, CustumCoursePriority priority,
                                             List <Course> previouslyCompletedCourses)
        {
            try
            {
                // variables
                List <CourseContainer> futureCourses  = new List <CourseContainer>(courseList);
                List <CourseContainer> currentCourses = new List <CourseContainer>();
                SortedList <CourseContainer, CourseContainer> courseLineup =
                    new SortedList <CourseContainer, CourseContainer>(priority);
                HashSet <CourseContainer> scheduledCourses = new HashSet <CourseContainer>();
                HashSet <CourseContainer> dependeesToCheck = new HashSet <CourseContainer>();
                HashSet <CourseContainer> copendeesToCheck = new HashSet <CourseContainer>();
                Dictionary <Semester, List <CourseContainer> > manualAddDict =
                    new Dictionary <Semester, List <CourseContainer> >();


                //convert the dictionary, and remove all manual add courses from future courses
                foreach (Semester sem in manualAddDictIn.Keys)
                {
                    List <Course> semList = new List <Course>();
                    manualAddDictIn.TryGetValue(sem, out semList);
                    List <CourseContainer> output = new List <CourseContainer>();
                    foreach (Course course in semList)
                    {
                        CourseContainer courseCont = getCourseContainer(course, courseList);
                        output.Add(courseCont);
                        futureCourses.Remove(courseCont);
                    }
                    manualAddDict.Add(sem, output);
                }


                // remove all previously Completed Courses from future courses and move them to
                // scheduled courses list. Additionally check if the courses de/copendees can
                // be added.
                List <CourseContainer> temp = new List <CourseContainer>();
                foreach (CourseContainer course in futureCourses)
                {
                    if (previouslyCompletedCourses.Contains(course.Course))
                    {
                        temp.Add(course);
                        scheduledCourses.Add(course);

                        foreach (CourseContainer dependee in course.Dependees)
                        {
                            dependeesToCheck.Add(dependee);
                        }
                        foreach (CourseContainer copendee in course.Copendees)
                        {
                            copendeesToCheck.Add(copendee);
                        }
                    }
                }
                // remove courses from future courses (neccessary to avoid modifying collection)
                foreach (CourseContainer course in temp)
                {
                    futureCourses.Remove(course);
                }
                // set temp to null
                temp = null;



                // initialize by adding all courses that can be scheduled to current courses list
                // and ensuring the semester list is sorted.
                foreach (CourseContainer course in futureCourses.ToList())
                {
                    //debug: checking initial adding of courses
                    Console.WriteLine("Initial course add: " + course.Course.courseReference + ", " +
                                      (course.Dependents.Count == 0 && course.Copendents.Count == 0));
                    if (course.Copendents.Count != 0)
                    {
                        Console.WriteLine(Course.CourseListString("Copendents: ", course.Copendents));
                    }

                    if (course.Dependents.Count == 0 && course.Copendents.Count == 0)
                    {
                        // add the course to the schedulable currentCourses list/remove from unschedulable
                        currentCourses.Add(course);
                        futureCourses.Remove(course);

                        foreach (CourseContainer copendee in course.Copendees)
                        {
                            copendeesToCheck.Add(copendee);
                        }
                    }
                }

                // not implemented: ensure sort of semester!

                //debug: print future Courses
                Console.WriteLine(Course.CourseListString("Future Courses: ", futureCourses));
                //debug: pring initial current courses
                Console.WriteLine(Course.CourseListString("Initial Current Courses: ", currentCourses));
                Console.WriteLine("-------------------------");

                // Main loop to schedule courses for each semester
                foreach (Semester semester in semesters)
                {
                    // check and add potentially schedulable courses from dependeesToCheck/copendeesToCheck
                    foreach (CourseContainer dependee in dependeesToCheck)
                    {
                        if (!futureCourses.Contains(dependee))
                        {
                            continue;
                        }
                        if (requirementsMet(dependee, scheduledCourses, currentCourses))
                        {
                            currentCourses.Add(dependee);
                            futureCourses.Remove(dependee);
                        }
                    }
                    // copendees are done seperate to allow for the possibility of a copendee depending on a dependee above
                    foreach (CourseContainer copendee in copendeesToCheck)
                    {
                        if (!futureCourses.Contains(copendee))
                        {
                            continue;
                        }
                        if (requirementsMet(copendee, scheduledCourses, currentCourses))
                        {
                            currentCourses.Add(copendee);
                            futureCourses.Remove(copendee);
                        }
                    }



                    //ensure there are remaining courses to schedule
                    if (currentCourses.Count == 0 && futureCourses.Count == 0)
                    {
                        break;
                    }

                    //null all course current and future course dependee depths
                    foreach (CourseContainer course in currentCourses)
                    {
                        course.PendeeDepth = null;
                    }
                    foreach (CourseContainer course in futureCourses)
                    {
                        course.PendeeDepth = null;
                    }
                    //clear courseLineup
                    courseLineup.Clear();
                    // clear the check lists
                    dependeesToCheck.Clear();
                    copendeesToCheck.Clear();



                    //calculate each current course's pendee depth
                    foreach (CourseContainer course in currentCourses)
                    {
                        pendeeDepth(course, semester, semesters);
                    }

                    // Add valid current courses to courseLineup
                    // ie: Ensure the course coresponds to the correct term and year
                    foreach (CourseContainer course in currentCourses)
                    {
                        // Get course details
                        CourseDetails details = course.Course.courseDetails;
                        int           yearRemainder;
                        bool          valid = true;

                        // calculate year
                        if (details.YearBase.HasValue)
                        {
                            yearRemainder = semester.Year - details.YearBase.Value;
                            if (yearRemainder % details.YearMultiple != 0)
                            {
                                valid = false;
                            }
                        }

                        if (course.Course.validTerms.Contains(semester.Term) && valid)
                        {
                            courseLineup.Add(course, course);
                        }
                    }

                    //debug: print semester reference
                    Console.WriteLine("Adding courses for " + semester.SemesterReference);

                    //debug: print current current courses
                    Console.WriteLine(Course.CourseListString("Current Courses: ", currentCourses));

                    //debug: print course lineup
                    System.Console.WriteLine(Course.CourseListString("pre add list: ", courseLineup.Values));

                    //get the manual add list for the semester
                    List <CourseContainer> semesterManualAdd = new List <CourseContainer>();

                    //get manual add list for the semester
                    manualAddDict.TryGetValue(semester, out semesterManualAdd);


                    // add manualy scheduled courses
                    while (semesterManualAdd.Count != 0)
                    {
                        CourseContainer course = semesterManualAdd[0];

                        semester.addCourse(course);    //add the manual course
                        scheduledCourses.Add(course);  // add course to list of scheduled courses
                        semesterManualAdd.RemoveAt(0); //remove the course from the manual add list

                        // add the courses dependees and their copendees to HashSets respectivly. This is done to check if these courses
                        // requirements are satisfied and such be added to currentCourses list.
                        foreach (CourseContainer dependee in course.Dependees)
                        {
                            dependeesToCheck.Add(dependee);     //add the dependee
                            foreach (CourseContainer copendee in dependee.Copendees)
                            {
                                copendeesToCheck.Add(copendee);     //add the copendee
                            }
                        }
                    }



                    // Attempt to add courses to the current semester
                    while (semester.TotalCredits < semester.MaxCredits && courseLineup.Count != 0)
                    {
                        CourseContainer course;

                        //get next course from upcoming courses
                        course = courseLineup.First().Value;

                        //if there are free credits in the semester add the next priority course
                        if (semester.TotalCredits + course.Course.credits <= semester.MaxCredits)
                        {
                            semester.addCourse(course);     // schedule course in current semester
                            scheduledCourses.Add(course);   // add course to list of scheduled courses
                            currentCourses.Remove(course);  // remove course from current Courses to schedule

                            // add the courses dependees and their copendees to HashSets respectivly. This is done to check if these courses
                            // requirements are satisfied and such be added to currentCourses list.
                            foreach (CourseContainer dependee in course.Dependees)
                            {
                                dependeesToCheck.Add(dependee);     //add the dependee
                                foreach (CourseContainer copendee in dependee.Copendees)
                                {
                                    copendeesToCheck.Add(copendee);     //add the copendee
                                }
                            }
                        }

                        //regardless, remove the course from the lineup
                        courseLineup.RemoveAt(0);
                    }


                    //debug: print course Lineup after
                    System.Console.WriteLine(Course.CourseListString("post add list: ", courseLineup.Values));
                    //debug: print scheduled courses
                    System.Console.WriteLine(Course.CourseListString("Semester courses: ", semester.Courses));

                    Console.WriteLine("-----------------------------------");
                }

                //check if all courses have been scheduled
                if (!(futureCourses.Count == 0 && currentCourses.Count == 0))
                {
                    //debug
                    Console.WriteLine(Course.CourseListString("Courses unable to be scheduled: ", futureCourses, currentCourses));
                    throw new Exception("Not Enough Semesters to schedule all courses!");
                }

                return(true);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
                return(false);
            }
        }