using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Globalization;
using System.Text;
using Sched.CustomClasses;

namespace Sched.ScheduleAlgorithm
{
    public class LessonWithArray
    {
        public Lesson Lssn { get; set; }
        public bool[,,] OccupiedArray { get; }   //(lessons from DB, assigned Lessons by GA)
        public int EmptySpotsScore { get; set; }

        public LessonWithArray(Lesson lssn, int nDays, int nPeriods, int nWeeks)
        {
            Lssn = new Lesson(lssn);
            if (Lssn.Periodicity == 1)
            {
                OccupiedArray = new bool[nDays, nPeriods, 1];
            }
            else if (Lssn.Periodicity == 2)
            {
                OccupiedArray = new bool[nDays, nPeriods, 2];
            }
            else
            {
                OccupiedArray = new bool[nDays, nPeriods, nWeeks];
            }

            EmptySpotsScore = 0;
        }

        public void MarkOccupiedAllLessons(ObservableCollection<object> lessonColl, Semester sem)
        {
            foreach (Lesson lssn in lessonColl)
            {
                MarkOccupiedSingleLesson(lssn, sem,true,false);
            }
        }
        public void MarkOccupiedSingleLesson(Lesson lssn, Semester sem, bool isOccupied, bool onlyInternal = true)
        {
            int nWeeks = OccupiedArray.GetLength(2);
            int day = AutoscheduleData.GetWeekDayIndex(lssn.StartDateDT.DayOfWeek.ToString());
            int periodicity = lssn.Periodicity;
            int weekStart = sem.GetWeek(lssn.StartDateDT) % nWeeks; //different number of layers according to lesson, e.g. 7 % 1 ->0, 7%2 -> 1, 7 % 13 - >7

            int weekStep = 1;
            if (periodicity == 0)
            {
                weekStep = nWeeks;
            }
            else if (periodicity == 2)
            {
                weekStep = 2;
            }

            for (int w = weekStart; w < nWeeks; w += weekStep)
            {
                for (int p = lssn.PeriodStart; p < (lssn.PeriodStart + lssn.Hours); p++)
                {
                    OccupiedArray[day, p, w] = isOccupied;
                }
            }
            //Debug.WriteLine("MarkOccupied: Day,perStart,periodicity,weekStart,step: " + $"{day},{lssn.PeriodStart},{periodicity},{weekStart},{weekStep}");
        }
        public int RecalculateEmptySpotsScore(int dayStart, int dayEnd, int periodStart, int periodEnd)
        {
            int emptySpots = 0;
            int nWeeks = OccupiedArray.GetLength(2);

            //Debug.WriteLine(Lssn.Id.ToString());
            for (int w = 0; w < nWeeks; w++)
            {
                //Debug.WriteLine("Week: " + w.ToString());
                for (int d = dayStart; d <= dayEnd; d++)
                {
                    //string days = "";
                    for (int p = periodStart; p <= periodEnd; p++)
                    {
                        //days += OccupiedArray[d, p, w].ToString();
                        if (OccupiedArray[d, p, w] == false) //no external collisions (already declared lessons) or internal collision (from already assigned lessons)->the spot is empty
                        {
                            int emptyBlocksCont = 0;
                            for (int p_sub = p; p_sub <= periodEnd; p_sub++)    //is it long enough?
                            {
                                if (OccupiedArray[d, p_sub, w] == false)
                                { emptyBlocksCont++; }
                                else
                                { break; }
                            }

                            if (Lssn.Hours <= emptyBlocksCont)
                            {
                                emptySpots++;
                            }
                        }
                    }
                    //Debug.WriteLine(days);
                }
            }

            EmptySpotsScore = emptySpots;
            //Debug.WriteLine("EmptySpotsScore: " + emptySpotsScore.ToString());
            return EmptySpotsScore;
        }
        
        public List<(int day, int period, int week)> GetPossiblePositions(int dayStart, int dayEnd, int periodStart, int periodEnd)
        {
            List<(int day, int period, int week)> possiblePos = new List<(int, int, int)>();

            int nWeeks = OccupiedArray.GetLength(2);
            for (int w = 0; w < nWeeks; w++)
            {
                for (int d = dayStart; d <= dayEnd; d++)
                {
                    for (int p = periodStart; p <= periodEnd; p++)
                    {
                        if (OccupiedArray[d, p, w] == false) //the spot is empty
                        {
                            int emptyBlocksCont = 0;
                            for (int p_sub = p; p_sub <= periodEnd; p_sub++)
                            {
                                if (OccupiedArray[d, p_sub, w]== false)
                                { emptyBlocksCont++; }
                                else
                                { break; }
                            }

                            if (Lssn.Hours <= emptyBlocksCont)
                            {
                                possiblePos.Add((d, p, w));
                                if (possiblePos.Count > 19)
                                {    //return 10 first found
                                    return possiblePos;
                                }
                            }
                        }
                    }
                }
            }

            return possiblePos;
        }   
        public bool IsPossiblePosition(int day, int period, int week)
        {
            if(((period + Lssn.Hours) >= OccupiedArray.GetLength(1))||(week >= OccupiedArray.GetLength(2))) //to ensure we don't reach past the allowed timeframe
            {
                return false;
            }

            int emptyBlocksCont = 0;
            for (int p_sub = period; p_sub < (period + Lssn.Hours); p_sub++)
            {
                if (OccupiedArray[day, p_sub, week] == false)
                { emptyBlocksCont++; }
                else
                { break; }
            }

            if (Lssn.Hours == emptyBlocksCont)
            {
                return true;
            }

            return false;
        }
    }
}
