#if FRAMEWORK20
using System;
using System.Collections.Generic;
using System.Text;
using System.Collections.ObjectModel;
using DevComponents.Schedule.Model.Primitives;
namespace DevComponents.Schedule.Model
{
    /// 
    /// Represents subset of appointments collection.
    /// 
    public class AppointmentSubsetCollection : CustomCollection
    {
        #region Private Variables
        private DateTime _StartDate = DateTime.MinValue;
        private DateTime _EndDate = DateTime.MinValue;
        #endregion
        #region Constructor
        /// 
        /// Initializes a new instance of the AppointmentSubsetCollection class with appointments between given start and end date.
        /// 
        /// 
        /// 
        /// 
        public AppointmentSubsetCollection(CalendarModel calendar, DateTime start, DateTime end)
        {
            _Calendar = calendar;
            _StartDate = start;
            _EndDate = end;
            PopulateCollection();
        }
        #endregion
        #region Internal Implementation
        private CalendarModel _Calendar = null;
        /// 
        /// Gets the calendar collection is associated with this collection.
        /// 
        public CalendarModel Calendar
        {
            get { return _Calendar; }
            internal set { _Calendar = value; }
        }
        protected override void InsertItem(int index, Appointment item)
        {
            OnBeforeInsert(index, item);
            base.InsertItem(index, item);
        }
        private void OnBeforeInsert(int index, Appointment item)
        {
            item.SubPropertyChanged += this.ChildPropertyChangedEventHandler;
            item.Calendar = _Calendar;
            if (item.IsRecurringInstance && _Calendar != null)
                _Calendar.InternalAppointmentAdded(item);
        }
        protected override void SetItem(int index, Appointment item)
        {
            OnBeforeSetItem(index, item);
            base.SetItem(index, item);
        }
        private void OnBeforeSetItem(int index, Appointment item)
        {
            Appointment app = this[index];
            app.SubPropertyChanged -= this.ChildPropertyChangedEventHandler;
            app.Calendar = null;
            item.SubPropertyChanged += this.ChildPropertyChangedEventHandler;
            app.Calendar = _Calendar;
        }
        protected override void RemoveItem(int index)
        {
            OnBeforeRemove(index);
            base.RemoveItem(index);
        }
        private void OnBeforeRemove(int index)
        {
            Appointment item = this[index];
            OnAppointmentRemoved(item);
        }
        private void OnAppointmentRemoved(Appointment item)
        {
            item.SubPropertyChanged -= this.ChildPropertyChangedEventHandler;
            if (item.IsRecurringInstance && _Calendar != null)
                _Calendar.InternalAppointmentRemoved(item, false);
            if (item.IsRecurringInstance)
                item.Calendar = null;
        }
        protected override void ClearItems()
        {
            foreach (Appointment item in this.GetItemsDirect())
            {
                OnAppointmentRemoved(item);
            }
            base.ClearItems();
        }
        private void PopulateCollection()
        {
            this.Clear();
            foreach (Appointment app in _Calendar.Appointments)
            {
                if (app.LocalStartTime >= _StartDate && app.LocalStartTime <= _EndDate || app.LocalEndTime > _StartDate && (app.LocalEndTime <= _EndDate || app.LocalStartTime < _StartDate))
                {
                    this.Add(app);
                }
                if (app.Recurrence != null && IsRecurrenceInRange(app, _StartDate, _EndDate))
                {
                    int count = this.GetItemsDirect().Count;
                    app.Recurrence.GenerateSubset(this, _StartDate, _EndDate);
                    if (count == this.GetItemsDirect().Count && TotalDaysDuration(app.StartTime, app.EndTime) >= 1 &&
                        app.LocalEndTime < _StartDate)
                    {
                        // Nothing generated lets wind back and look to see is there an recurrence that needs to be captured in this view
                        int daysLookBack = TotalDaysDuration(app.StartTime, app.EndTime);
                        DateTime start = _StartDate;
                        for (int i = 0; i < daysLookBack; i++)
                        {
                            start = start.AddDays(-1);
                            AppointmentSubsetCollection dayCollection = _Calendar.GetDay(start).Appointments;
                            foreach (Appointment subAppointment in dayCollection)
                            {
                                if (subAppointment.IsRecurringInstance && subAppointment.RootAppointment == app &&
                                    (subAppointment.LocalStartTime >= _StartDate && subAppointment.LocalStartTime <= _EndDate ||
                                     subAppointment.LocalEndTime > _StartDate && (subAppointment.LocalEndTime <= _EndDate ||
                                                                             subAppointment.LocalStartTime < _StartDate)))
                                {
                                    this.Add(subAppointment);
                                    daysLookBack = -1;
                                    break;
                                }
                            }
                        }
                    }
                }
            }
            _IsCollectionUpToDate = true;
        }
        private int TotalDaysDuration(DateTime startTime, DateTime endTime)
        {
            if (endTime.Hour == 0 && endTime.Minute == 0 && endTime.Second == 0)
                endTime = endTime.AddMinutes(-1);
            return (int)Math.Max(0, Math.Ceiling(endTime.Date.Subtract(startTime.Date).TotalDays));
        }
        private bool IsRecurrenceInRange(Appointment app, DateTime startDate, DateTime endDate)
        {
            if (app.Recurrence.RecurrenceType == eRecurrencePatternType.Yearly)
            {
                // Simple range check on number of occurrences
                if (app.Recurrence.RangeLimitType == eRecurrenceRangeLimitType.RangeNumberOfOccurrences && app.LocalEndTime.Subtract(endDate).TotalDays / 365 > app.Recurrence.RangeNumberOfOccurrences)
                    return false;
                // Date check based on next expected recurrence date
                if (app.Recurrence.Yearly.RepeatInterval > 1)
                {
                    DateTime nextRecurrence = DateTimeHelper.MaxDate(app.Recurrence.RecurrenceStartDate, app.EndTime.AddDays(1).Date);
                    while (nextRecurrence < startDate)
                        nextRecurrence = RecurrenceGenerator.GetNextYearlyRecurrence(app.Recurrence, nextRecurrence);
                    if (nextRecurrence > endDate || nextRecurrence.Add(endDate.Subtract(startDate)) < startDate)
                        return false;
                }
                else
                {
                    DateTime nextRecurrence = RecurrenceGenerator.GetNextYearlyRecurrence(app.Recurrence, startDate.Date.AddDays(-startDate.DayOfYear));
                    if (nextRecurrence > endDate || nextRecurrence.Add(endDate.Subtract(startDate)) < startDate)
                        return false;
                }
            }
            if (app.Recurrence.RangeEndDate == DateTime.MinValue || app.Recurrence.RangeEndDate >= _EndDate || app.Recurrence.RangeEndDate < _EndDate && app.Recurrence.RangeEndDate > _StartDate)
                return true;
            return false;
        }
        private SubPropertyChangedEventHandler _ChildPropertyChangedEventHandler = null;
        private SubPropertyChangedEventHandler ChildPropertyChangedEventHandler
        {
            get
            {
                if (_ChildPropertyChangedEventHandler == null) _ChildPropertyChangedEventHandler = new SubPropertyChangedEventHandler(ChildPropertyChanged);
                return _ChildPropertyChangedEventHandler;
            }
        }
        private void ChildPropertyChanged(object sender, SubPropertyChangedEventArgs e)
        {
            InvalidateCollection();
        }
        /// 
        /// Invalidates collection content due to the change to appointments or some other condition. Invalidating collection
        /// content causes the collection elements to be re-generated on next collection read access.
        /// 
        public virtual void InvalidateCollection()
        {
            _IsCollectionUpToDate = false;
        }
        protected override void OnCollectionReadAccess()
        {
            if (!_IsCollectionUpToDate) PopulateCollection();
            base.OnCollectionReadAccess();
        }
        private bool _IsCollectionUpToDate = false;
        internal bool IsCollectionUpToDate
        {
            get { return _IsCollectionUpToDate; }
            set
            {
                _IsCollectionUpToDate = value;
            }
        }
        
        #endregion
    }
}
#endif