using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using DataClasses;

namespace DataRepositories
{
    public class SchoolDayRoomParameters
    {
        #region "Properties"

        public DayOfWeekEnum Day
        {
            get { return day; }
        }


        public DateTime Date
        {
            get { return date; }
        }


        public LessonHoursEnum HourFrom
        {
            get { return hourFrom; }
        }


        public LessonHoursEnum HourTo
        {
            get { return hourTo; }
        }


        public Lesson Lesson
        {
            get { return lesson; }
        }


        public Room Room
        {
            get { return room; }
        }


        public bool IsValid
        {
            get { return isValid; }
        }


        public DataContext Context
        {
            get { return context; }
        }


        public int ExistingId
        {
            get { return existingId; }
        }


        public List<string> ErrorMsg
        {
            get { return erroMsg; }
        }

        #endregion


        #region "Variables"

        private DayOfWeekEnum day;
        private DateTime date;
        private LessonHoursEnum hourFrom;
        private LessonHoursEnum hourTo;
        private Lesson lesson;
        private Room room;
        private DataContext context;
        private bool isValid = true;
        private int existingId = 0;
        private readonly List<string> erroMsg = new List<string>();

        #endregion


        #region "Constructor"

        public SchoolDayRoomParameters(dynamic parameters, DataContext cnxt)
        {
            if (!BaseValidation(parameters))
            {
                return;
            }

            GetSimpleParameters(parameters, cnxt);
            GetAdvancedParameters(parameters);
            CheckAlreadyExistingLesson();
            CheckIdenticalDays();
            CheckOverLappingLessons();
            ValidateFromToHour();
            TouchLesson();
        }

        #endregion


        #region "Private Methods"

        /// <summary>
        /// Checks if it was single lesson or recurring lesson.
        /// </summary>
        private void TouchLesson()
        {
            if (date > DateTime.MinValue)
            {
                lesson.SingleLessonDate = date;
            }
        }


        /// <summary>
        /// Checks errors from given parameters in constructor.
        /// </summary>
        /// <param name="parameters"></param>
        /// <returns></returns>
        private bool BaseValidation(dynamic parameters)
        {
            if (!string.IsNullOrEmpty(parameters.Error))
            {
                erroMsg.Add(parameters.Error);
                isValid = false;
                return false;
            }

            return true;
        }


        /// <summary>
        /// If lesson already exists in Timetable, dissociate it.
        /// </summary>
        private void CheckAlreadyExistingLesson()
        {
            if (lesson.SchoolDay != null)
            {
                var schooldayRepository = new Repository<SchoolDayRoom>(context);

                var allDays = (from s in schooldayRepository.GetAll()
                               select s);

                int maxEnumValue = (int)Enum.GetValues(typeof(LessonHoursEnum)).Cast<LessonHoursEnum>().Max();

                foreach (var schoolDayRoom in allDays)
                {
                    for (int i = 1; i <= maxEnumValue; i++)
                    {
                        PropertyInfo property = typeof(SchoolDayRoom).GetProperty(((LessonHoursEnum)i).ToString());
                        if ((property.GetValue(schoolDayRoom) != null) && property.GetValue(schoolDayRoom).Equals(lesson))
                        {
                            property.SetValue(schoolDayRoom, null);
                        }
                    }
                }
            }
        }


        /// <summary>
        /// Checks if current day already exists in database, otherwise sets existing id of the day.
        /// </summary>
        private void CheckIdenticalDays()
        {
            var schooldayRepository = new Repository<SchoolDayRoom>(context);

            var result = from d in schooldayRepository.GetAll()
                         where (d.Day == day) && (d.LessonRoom.Id == room.Id)
                         select d.Id;

            if (result.Any())
            {
                existingId = result.FirstOrDefault();
            }
        }


        /// <summary>
        /// Validates From and To selected hours.
        /// </summary>
        private void ValidateFromToHour()
        {
            if (((int)hourTo - (int)hourFrom) <= 0)
            {
                isValid = false;
                erroMsg.Add("Čas od-do sa nesmie prekrývať!");
            }
        }


        /// <summary>
        /// Gets simple parameters from dynamic properties.
        /// </summary>
        /// <param name="parameters">dynamic properties</param>
        /// <param name="cntx">DataContext</param>
        private void GetSimpleParameters(dynamic parameters, DataContext cntx)
        {
            day = parameters.Day;
            hourFrom = parameters.HourFrom;
            hourTo = hourTo = parameters.HourTo;
            context = cntx;
        }


        /// <summary>
        /// Gets advanced parameters from dynamic properties.
        /// </summary>
        /// <param name="parameters">dynamic properties</param>
        private void GetAdvancedParameters(dynamic parameters)
        {
            int lessonId = parameters.LessonId;
            var lessonRepository = new Repository<Lesson>(context);
            lesson = lessonRepository.GetById(lessonId);

            int roomId = parameters.RoomId;
            var roomRepository = new Repository<Room>(context);
            room = roomRepository.GetById(roomId);

            if (parameters.Date != null)
            {
                try
                {
                    date = DateTime.Parse(parameters.Date);
                    day = (DayOfWeekEnum)Enum.Parse(typeof(DayOfWeekEnum), date.DayOfWeek.ToString());
                }
                catch (Exception)
                {

                    erroMsg.Add("Zlý formát dátumu!");
                }
            }
        }


        /// <summary>
        /// Checks if day there is some entry in selected day.
        /// </summary>
        /// <param name="schoolDayRoom">Day</param>
        /// <returns>Returns false if there is no record</returns>
        private bool IsUnusedDay(SchoolDayRoom schoolDayRoom)
        {
            int maxEnumValue = (int)Enum.GetValues(typeof(LessonHoursEnum)).Cast<LessonHoursEnum>().Max();
            bool delete = true;

            for (int i = 1; i <= maxEnumValue; i++)
            {
                PropertyInfo property = typeof(SchoolDayRoom).GetProperty(((LessonHoursEnum)i).ToString());
                if (property.GetValue(schoolDayRoom) != null)
                {
                    delete = false;
                }
            }
            return delete;
        }


        /// <summary>
        /// Checks if user is not trying to define two different lessons at same time.
        /// </summary>
        private void CheckOverLappingLessons()
        {
            if (existingId > 0)
            {
                var schooldayRepository = new Repository<SchoolDayRoom>(context);
                var selectedDay = schooldayRepository.GetById(existingId);

                for (int i = (int)hourFrom; i < (int)hourTo; i++)
                {
                    PropertyInfo property = typeof(SchoolDayRoom).GetProperty(((LessonHoursEnum)i).ToString());
                    if (property.GetValue(selectedDay) != null)
                    {
                        isValid = false;
                    }
                }

                if (!isValid)
                {
                    erroMsg.Add("Zapísané hodiny v učebni sa nesmú prekrývať!");
                }
            }
        }

        #endregion


        #region "Public methods"


        /// <summary>
        /// Deletes empty days with no lessons.
        /// </summary>
        public void DeleteEmptyDays()
        {
            var schooldayRepository = new Repository<SchoolDayRoom>(context);

            var allDays = (from s in schooldayRepository.GetAll()
                           select s);

            foreach (var schoolDayRoom in allDays)
            {
                if (IsUnusedDay(schoolDayRoom))
                {
                    schooldayRepository.Delete(schoolDayRoom);
                }
            }

            context.SaveChanges();
        }

        #endregion
    }
}