//===============================================================================
// Microsoft patterns & practices Enterprise Library
// Core
//===============================================================================
// Copyright © Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//===============================================================================

//---------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation.  All rights reserved.
//
//---------------------------------------------------------------------------

using System;
using System.ComponentModel;

#pragma warning disable 1591

namespace Microsoft.Practices.EnterpriseLibrary.Configuration.Design.Controls.Toolkit.Windows.Controls
{
    /// <summary>
    /// Specifies a DateTime range class which has a start and end.
    /// </summary>
    public sealed class CalendarDateRange : INotifyPropertyChanged
    {
        #region Data
        private DateTime _end;
        private DateTime _start;
        #endregion Data

        /// <summary>
        /// Initializes a new instance of the CalendarDateRange class.
        /// </summary>
        public CalendarDateRange() :
            this(DateTime.MinValue, DateTime.MaxValue)
        {
        }

        /// <summary>
        /// Initializes a new instance of the CalendarDateRange class which creates a range from a single DateTime value.
        /// </summary>
        /// <param name="day"></param>
        public CalendarDateRange(DateTime day) :
            this(day, day)
        {
        }

        /// <summary>
        /// Initializes a new instance of the CalendarDateRange class which accepts range start and end dates.
        /// </summary>
        /// <param name="start"></param>
        /// <param name="end"></param>
        public CalendarDateRange(DateTime start, DateTime end)
        {
            _start = start;
            _end = end;
        }

        #region Public Events

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        #region Public Properties

        /// <summary>
        /// Specifies the End date of the CalendarDateRange.
        /// </summary>
        public DateTime End
        {
            get 
            { 
                return CoerceEnd(_start, _end);
            }

            set
            {
                DateTime newEnd = CoerceEnd(_start, value);
                if (newEnd != End)
                {
                    OnChanging(new CalendarDateRangeChangingEventArgs(_start, newEnd));
                    _end = value;
                    OnPropertyChanged(new PropertyChangedEventArgs("End"));
                }
            }
        }

        /// <summary>
        /// Specifies the Start date of the CalendarDateRange.
        /// </summary>
        public DateTime Start
        {
            get 
            { 
                return _start; 
            }

            set 
            { 
                if (_start != value)
                {
                    DateTime oldEnd = End;
                    DateTime newEnd = CoerceEnd(value, _end);

                    OnChanging(new CalendarDateRangeChangingEventArgs(value, newEnd));

                    _start = value;

                    OnPropertyChanged(new PropertyChangedEventArgs("Start"));

                    if (newEnd != oldEnd)
                    {
                        OnPropertyChanged(new PropertyChangedEventArgs("End"));
                    }
                }
            }
        }

        #endregion Public Properties

        #region Internal Events

        internal event EventHandler<CalendarDateRangeChangingEventArgs> Changing;

        #endregion Internal Events

        #region Internal Methods

        /// <summary>
        /// Returns true if any day in the given DateTime range is contained in the current CalendarDateRange.
        /// </summary>
        /// <param name="range"></param>
        /// <returns></returns>
        internal bool ContainsAny(CalendarDateRange range)
        {
            return (range.End >= this.Start) && (this.End >= range.Start);
        }

        #endregion Internal Methods

        #region Private Methods

        private void OnChanging(CalendarDateRangeChangingEventArgs e)
        {
            EventHandler<CalendarDateRangeChangingEventArgs> handler = this.Changing;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        private void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            PropertyChangedEventHandler handler = this.PropertyChanged;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        /// <summary>
        /// Coerced the end parameter to satisfy the start &lt;= end constraint
        /// </summary>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <returns>If start &lt;= end the end parameter otherwise the start parameter</returns>
        private static DateTime CoerceEnd(DateTime start, DateTime end)
        {
            return (DateTime.Compare(start, end) <= 0) ? end : start;
        }

        #endregion Private Methods
    }
}
#pragma warning restore 1591
