using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.Serialization;

using BS.Shared.Extensions;

namespace BS.Shared.Core
{
    [DataContract]
    public class DateTimeSpan : INotifyPropertyChanged
    {
        public static string PropertyName_EndDate = "EndDate";

        public static string PropertyName_EndDateTime = "EndDateTime";

        public static string PropertyName_EndHours = "EndHours";

        public static string PropertyName_EndMinutes = "EndMinutes";

        public static string PropertyName_StartDate = "StartDate";

        public static string PropertyName_StartDateTime = "StartDateTime";

        public static string PropertyName_StartHours = "StartHours";

        public static string PropertyName_StartMinutes = "StartMinutes";

        private DateTime _EndDate;

        private int _EndHours;

        private int _EndMinutes;

        private DateTime _StartDate;

        private int _StartHours;

        private int _StartMinutes;

        private bool _SynchEndDate;

        public DateTime EndDate
        {
            get { return _EndDate; }

            set
            {
                _EndDate = value;

                if (IsCalculatingHoursAndMinutesByDates)
                {
                    _EndHours   = value.Hour;
                    _EndMinutes = value.Minute;
                }

                FirePropertyChanged(PropertyName_EndDate);
                FirePropertyChanged(PropertyName_EndDateTime);
            }
        }

        [DataMember]
        public DateTime EndDateTime
        {
            get { return _EndDate.Date.AddHours(_EndHours).AddMinutes(_EndMinutes); }

            set
            {
                EndDate = value.Date;
                EndHours = value.Hour;
                EndMinutes = value.Minute;
            }
        }

        public int EndHours
        {
            get { return _EndHours; }

            set
            {
                _EndHours = value;
                FirePropertyChanged(PropertyName_EndHours);
                FirePropertyChanged(PropertyName_EndDateTime);
            }
        }

        public int EndMinutes
        {
            get { return _EndMinutes; }

            set
            {
                _EndMinutes = value;
                FirePropertyChanged(PropertyName_EndMinutes);
                FirePropertyChanged(PropertyName_EndDateTime);
            }
        }

        public DateTime StartDate
        {
            get { return _StartDate; }

            set
            {
                _StartDate = value;
                if (IsCalculatingHoursAndMinutesByDates)
                {
                    _StartHours   = value.Hour;
                    _StartMinutes = value.Minute;
                }
                FirePropertyChanged(PropertyName_StartDate);
                FirePropertyChanged(PropertyName_StartDateTime);
                if (_SynchEndDate)
                {
                    SynchronizeEndDate();
                }
            }
        }

        [DataMember]
        public DateTime StartDateTime
        {
            get { return _StartDate.Date.AddHours(_StartHours).AddMinutes(_StartMinutes); }

            set
            {
                StartDate = value.Date;
                StartHours = value.Hour;
                StartMinutes = value.Minute;
            }
        }

        public int StartHours
        {
            get { return _StartHours; }

            set
            {
                _StartHours = value;
                FirePropertyChanged(PropertyName_StartHours);
                FirePropertyChanged(PropertyName_StartDateTime);
                if (_SynchEndDate)
                {
                    SynchronizeEndDate();
                }
            }
        }

        public int StartMinutes
        {
            get { return _StartMinutes; }

            set
            {
                _StartMinutes = value;
                FirePropertyChanged(PropertyName_StartMinutes);
                FirePropertyChanged(PropertyName_StartDateTime);
                if (_SynchEndDate)
                {
                    SynchronizeEndDate();
                }
            }
        }

        public bool SynchEndDate
        {
            get { return _SynchEndDate; }

            set { _SynchEndDate = value; }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public DateTimeSpan Clone()
        {
            return new DateTimeSpan
            {
                StartDateTime = StartDateTime,
                EndDateTime = EndDateTime
            };
        }

        public override bool Equals(object obj)
        {
            if (!(obj is DateTimeSpan))
            {
                return false;
            }

            return ((DateTimeSpan) obj).EndDateTime == EndDateTime &&
                   ((DateTimeSpan) obj).StartDateTime == StartDateTime;
        }

        public override int GetHashCode()
        {
            return StartDateTime.GetHashCode() ^ EndDateTime.GetHashCode();
        }

        public TimeSpan GetTimeSpan()
        {
            return EndDateTime - StartDateTime;
        }

        public IEnumerable<DateTimeSpan> GetWholeWeeks()
        {
            var lResult = new List<DateTimeSpan>();

            DateTime startDate = StartDate;
            DateTime endDate = StartDate.AddDays(6);

            DateTime totalEnddate = EndDate;
            if (totalEnddate == DateTime.MaxValue.Date)
                totalEnddate = StartDate.AddYears(1);

            while (endDate <= totalEnddate)
            {
                var lCurrentWorkWeek = new DateTimeSpan
                {
                    StartDateTime = startDate,
                    EndDateTime = endDate
                };

                lResult.Add(lCurrentWorkWeek);

                startDate = endDate.AddDays(1);
                endDate = startDate.AddDays(6);
            }

            return lResult;
        }

        public bool Overlaps(DateTimeSpan pDateSpan2)
        {
            DateTime lThisEnd = EndDateTime.AddMilliseconds(-1);
            DateTime lOtherEnd = pDateSpan2.EndDateTime.AddMilliseconds(-1);
			
            return StartDateTime.InBetween(pDateSpan2.StartDateTime, lOtherEnd, false) ||
                   lThisEnd.InBetween(pDateSpan2.StartDateTime, lOtherEnd, false) ||
                   (StartDateTime <= pDateSpan2.StartDateTime && lThisEnd >= lOtherEnd);


			
        }

        public string ToDateString()
        {
            return StartDateTime.ToMyDateTimeString() + " - " +
                   EndDateTime.ToMyDateTimeString();
        }

        public override string ToString()
        {
            return StartDateTime.ToMyDateTimeString() + " " + StartDateTime.ToShortTimeString() + " - " +
                   EndDateTime.ToMyDateTimeString() + " " + EndDateTime.ToShortTimeString();
        }

        private void FirePropertyChanged(string pPropertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(pPropertyName));
            }
        }

        private void SynchronizeEndDate()
        {
            if (EndDateTime < StartDateTime)
            {
                EndDateTime = StartDateTime;
            }
        }

        public bool ContainsDate(DateTime dt)
        {
            DateTime endDate = EndDate;
            if (endDate < DateTime.MaxValue.Date)
                endDate = endDate.AddDays(1);
            return StartDate <= dt && dt < endDate;
        }

        public TimeSpan StartDateAsTimeSpan
        {
            get { return StartDate.AddHours(StartHours).AddMinutes(StartMinutes).TimeOfDay; }

            set
            {
                StartHours = value.Hours;
                StartMinutes = value.Minutes;
            }
        }

        public TimeSpan EndDateAsTimeSpan
        {
            get { return EndDate.AddHours(EndHours).AddMinutes(EndMinutes).TimeOfDay; }

            set
            {
                EndHours = value.Hours;
                EndMinutes = value.Minutes;
            }
        }

        public bool IsCalculatingHoursAndMinutesByDates { get; set; }
    }
}