using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;

using BeWo.Controls.Calendar;

using BS.Shared;
using BS.Shared.Core;
using BS.Shared.DataContracts;
using BS.Shared.DataContracts.Compact;
using BS.Shared.Extensions;
using BeWo.ViewModel;

namespace BeWo.Scheduler.ViewModel
{
    public class ResourceBookingVM : AbstractDCMapperVM<BookingSequenceDC>, IBeWoAppointment

    {
        public static string PropertyName_ChangedElements = "ChangedElements";

        public static string PropertyName_Employee = "Employee";

        public static string PropertyName_Frequency = "Frequency";

        public static string PropertyName_Notice = "Notice";

        public static string PropertyName_RepeatInterval = "RepeatInterval";

        public static string PropertyName_Resource = "Resource";

        public static string PropertyName_RootAppointment = "RootAppointment";

        public static string PropertyName_SequenceEnd = "SequenceEnd";

        private readonly Dictionary<int, Appointment> _ChangedElements = new Dictionary<int, Appointment>();

        private readonly List<FrequencyTranslation> _PossibleFrequencies = new List<FrequencyTranslation>
            {
                new FrequencyTranslation
                    {
                        Frequency = ResourceBookingFrequencyType.Once, 
                        Translation = "Einmalig"
                    }, 
                new FrequencyTranslation
                    {
                        Frequency = ResourceBookingFrequencyType.Daily, 
                        Translation = "Täglich"
                    }, 
                new FrequencyTranslation
                    {
                        Frequency = ResourceBookingFrequencyType.Weekly,
                        Translation = "Wöchentlich"
                    }, 
                new FrequencyTranslation
                    {
                        Frequency = ResourceBookingFrequencyType.Monthly,
                        Translation = "Monatlich"
                    }, 
                new FrequencyTranslation
                    {
                        Frequency = ResourceBookingFrequencyType.Yearly,
                        Translation = "Jährlich"
                    }
            };

        //private Dictionary<int, Appointment> _AppointmentsInSpan = new Dictionary<int, Appointment>();

        private CompactEmployeeDC _Employee;

        private ResourceBookingFrequencyType _Frequency;

        private string _Notice;

        private int _RepeatInterval = 1;

        private ResourceDC _Resource;

        
        private Appointment _RootAppointment;

        private DateTime _SequenceEnd;

        private DateTimeSpan _SpanToConsider;

    
        public ResourceBookingVM(BookingSequenceDC pDC)
            : base(pDC, pDC.SequenceOid == null)
        {
        }

        public Dictionary<int, Appointment> ChangedElements
        {
            get { return this._ChangedElements; }
        }

        public CompactEmployeeDC Employee
        {
            get { return this._Employee; }

            set
            {
                if (this.AreDifferent(this._Employee, value))
                {
                    this._Employee = value;

                    // StoreDirtyInformation(AreDifferent(DataContract.Employee, value), PropertyName_Employee);
                    this.FirePropertyChanged(PropertyName_Employee);
                }
            }
        }

        public ResourceBookingFrequencyType Frequency
        {
            get { return this._Frequency; }

            set
            {
                if (this.AreDifferent(this._Frequency, value))
                {
                    this._Frequency = value;
                    this.StoreDirtyInformation(this.AreDifferent(this.DataContract.Frequency, value), PropertyName_Frequency);
                    this.FirePropertyChanged(PropertyName_Frequency);
                }
            }
        }

        public string Notice
        {
            get { return this._Notice; }

            set
            {
                if (this.AreDifferent(this._Notice, value))
                {
                    this._Notice = value;
                    this.StoreDirtyInformation(this.AreDifferent(this.DataContract.Notice, value), PropertyName_Notice);
                    this.FirePropertyChanged(PropertyName_Notice);
                }
            }
        }

        public List<FrequencyTranslation> PossibleFrequencies
        {
            get { return this._PossibleFrequencies; }
        }

        public int RepeatInterval
        {
            get { return this._RepeatInterval; }

            set
            {
                if (this.AreDifferent(this._RepeatInterval, value))
                {
                    this._RepeatInterval = value;
                    this.StoreDirtyInformation(this.AreDifferent(this.DataContract.RepeatInterval, value), PropertyName_RepeatInterval);
                    this.FirePropertyChanged(PropertyName_RepeatInterval);
                }
            }
        }

        public ResourceDC Resource
        {
            get { return this._Resource; }

            set
            {
                this._Resource = value;
                this.RootAppointment.Index = (int)value.ResourceOid.Value;
                this.RootAppointment.Brush = value.GetBrush();
                foreach (var iChanged in this._ChangedElements)
                {
                    iChanged.Value.Index = (int)value.ResourceOid.Value;
                    iChanged.Value.Brush = value.GetBrush();
                }

                this.FirePropertyChanged(PropertyName_Resource);
            }
        }

        public Appointment RootAppointment
        {
            get { return this._RootAppointment; }

            set
            {
                if (this.AreDifferent(this._RootAppointment, value))
                {
                    this._RootAppointment = value;
                    this.StoreDirtyInformation(!this.AreEqual(this.DataContract.RootBooking, this._RootAppointment), PropertyName_RootAppointment);
                    this.FirePropertyChanged(PropertyName_RootAppointment);
                    this._RootAppointment.PropertyChanged += this._RootAppointment_PropertyChanged;
                    this._RootAppointment.Duration.SynchEndDate = true;
                }
            }
        }

        public DateTime SequenceEnd
        {
            get { return this._SequenceEnd; }

            set
            {
                if (this.AreDifferent(this._SequenceEnd, value))
                {
                    value = value.Date.AddHours(23).AddMinutes(59);
                    this._SequenceEnd = value;
                    this.StoreDirtyInformation(this.AreDifferent(this.DataContract.SequenceEnd, value), PropertyName_SequenceEnd);
                    this.FirePropertyChanged(PropertyName_SequenceEnd);
                }
            }
        }

        public DateTimeSpan SpanToConsider
        {
            get { return this._SpanToConsider; }

            set
            {
                if (this.AreDifferent(this._SpanToConsider, value))
                {
                    this._SpanToConsider = value;
                }
            }
        }

        private bool IsChangedElementsDirty
        {
            get
            {
                if (this.DataContract.ChangedElements.Count != this._ChangedElements.Count)
                {
                    return true;
                }

                if (this.DataContract.ChangedElements.Count(dc => !this._ChangedElements.ContainsKey(dc.SequencePosition) || !this.AreEqual(dc, this._ChangedElements[dc.SequencePosition])) > 0)
                {
                    return true;
                }

                return false;
            }
        }

        public Dictionary<int, Appointment> CalculateAppointmentsFromSequence(DateTimeSpan pSpanToConsider)
        {
            var lResult = new Dictionary<int, Appointment>();

            DateTime lCurrentStart = this.RootAppointment.Duration.StartDateTime;
            DateTime lCurrentEnd = this.RootAppointment.Duration.EndDateTime;
            int lCurrentPostion = 0;
            int lCurrentSequencePosition = 0;

            if (this.RootAppointment.Duration.Overlaps(pSpanToConsider))
            {
                lResult.Add(0, this._RootAppointment);
            }

            lCurrentStart = this.Next(lCurrentStart, this._Frequency);
            lCurrentEnd = this.Next(lCurrentEnd, this._Frequency);

            if (this._Frequency != ResourceBookingFrequencyType.Once)
            {
                while (lCurrentStart <= pSpanToConsider.EndDateTime && lCurrentStart <= this._SequenceEnd)
                {
                    lCurrentPostion++;

                    if (lCurrentPostion % this._RepeatInterval == 0)
                    {
                        lCurrentSequencePosition++;
                        if (this._ChangedElements.ContainsKey(lCurrentSequencePosition))
                        {
                            if (this._ChangedElements[lCurrentSequencePosition] != null && this._ChangedElements[lCurrentSequencePosition].Duration.Overlaps(pSpanToConsider))
                            {
                                lResult.Add(lCurrentSequencePosition, this._ChangedElements[lCurrentSequencePosition]);
                            }
                        }
                        else if (new DateTimeSpan
                            {
                                StartDateTime = lCurrentStart, 
                                EndDateTime = lCurrentEnd
                            }.Overlaps(pSpanToConsider))
                        {
                            Appointment app = new Appointment();
                            DateTimeSpan dts = new DateTimeSpan();
                            dts.StartDateTime = lCurrentStart;
                            dts.EndDateTime = lCurrentEnd;

                            app.Duration = dts;
                            app.Notice = this.RootAppointment.Notice;

                            app.Index = this.RootAppointment.Index;
                            app.Brush = this._Resource.GetBrush();
                            app.Tag = this.RootAppointment.Tag;

                            string text = this.RootAppointment.Text;
                            if (text.LastIndexOf("\n") > 0)
                            {
                                text = text.Substring(0, text.LastIndexOf("\n") + 1);
                                text += dts.ToString();
                            }

                            app.Text = text;

                            lResult.Add(lCurrentSequencePosition, app);
                        }
                    }

                    lCurrentStart = this.Next(lCurrentStart, this._Frequency);
                    lCurrentEnd = this.Next(lCurrentEnd, this._Frequency);
                }
            }

            return lResult;
        }

        public void ChangeElement(int pSequencePosition, Appointment pAppointment)
        {
            this._ChangedElements[pSequencePosition] = pAppointment;

            this.StoreDirtyInformation(this.IsChangedElementsDirty, PropertyName_ChangedElements);
        }

        public void ClearChanged()
        {
            this._ChangedElements.Clear();

            this.StoreDirtyInformation(this.IsChangedElementsDirty, PropertyName_ChangedElements);
        }

       

        public DateTimeSpan GetSequenceSpan()
        {
            return new DateTimeSpan
                {
                    StartDateTime = this.RootAppointment.Duration.StartDateTime, 
                    EndDateTime = this.SequenceEnd
                };
        }
        
        protected override void InitByDataContract(BookingSequenceDC pDataContract)
        {
            string text = string.Empty;

            if (pDataContract.RootBooking.Employee != null)
            {
                text = pDataContract.RootBooking.Employee.FirstName + " " + pDataContract.RootBooking.Employee.LastName + ":\n";
            }

            if (pDataContract.Resource != null)
            {
                text += pDataContract.Resource.Name + "\n";
            }

            if (!String.IsNullOrEmpty(pDataContract.RootBooking.Notice))
            {
                text += "\n";
                text += pDataContract.RootBooking.Notice;
                text += "\n";
            }

            text += "\n";

            if (pDataContract.RootBooking.Duration.StartDate.Date == pDataContract.RootBooking.Duration.EndDate.Date)
            {
                // return StartDateTime.ToMyDateTimeString() + " " + StartDateTime.ToShortTimeString() + " - " + EndDateTime.ToMyDateTimeString() + " " + EndDateTime.ToShortTimeString();
                text += String.Format("{0:ddd, dd.MM.yy HH:mm} - {1:HH:mm}", pDataContract.RootBooking.Duration.StartDateTime, pDataContract.RootBooking.Duration.EndDateTime);

                // text += DataContract.RootBooking.Duration.ToString();
            }
            else
            {
                text += pDataContract.RootBooking.Duration.ToString();
            }

            this.RootAppointment = new Appointment
                {
                    Duration = pDataContract.RootBooking.Duration, 
                    Notice = pDataContract.RootBooking.Notice, 
                    Text = text, 
                    Tag = pDataContract.RootBooking.Employee, 
                };

            if (pDataContract.Resource != null)
            {
                this.RootAppointment.Index = (int)pDataContract.Resource.ResourceOid.Value;
                this.RootAppointment.Brush = pDataContract.Resource.GetBrush();
            }

            if (pDataContract.ChangedElements == null)
            {
                pDataContract.ChangedElements = new List<ResourceBookingDC>();
            }

            foreach (var iBooking in pDataContract.ChangedElements)
            {
                this._ChangedElements.Add(
                    iBooking.SequencePosition, 
                    iBooking.IsDeleted
                        ? null
                        : new Appointment
                            {
                                Duration = iBooking.Duration.Clone(), 
                                Notice = iBooking.Notice, 
                                Index = this.RootAppointment.Index, 
                                Brush = this.RootAppointment.Brush, 
                                Tag = iBooking.Employee
                            });
            }

            this._Frequency = pDataContract.Frequency;
            this._RepeatInterval = pDataContract.RepeatInterval;
            this._SequenceEnd = pDataContract.SequenceEnd;
            this._Resource = pDataContract.Resource;
            this._Notice = pDataContract.Notice;
            this._Employee = pDataContract.Employee;

        }

        protected override BookingSequenceDC MapToDataContract(BookingSequenceDC pDataContract, bool doCommit)
        {
            

            pDataContract.Frequency = this._Frequency;
            pDataContract.RepeatInterval = this._RepeatInterval;
            pDataContract.Notice = this._Notice;
            pDataContract.Resource = this._Resource;
            pDataContract.SequenceEnd = this._SequenceEnd;
            pDataContract.Employee = this.RootAppointment.Tag as CompactEmployeeDC;

            pDataContract.RootBooking.Duration = this.RootAppointment.Duration.Clone();
            pDataContract.RootBooking.Notice = this.RootAppointment.Notice;
            pDataContract.RootBooking.Employee = this.RootAppointment.Tag as CompactEmployeeDC;

            if (this._Frequency == ResourceBookingFrequencyType.Once)
            {
                pDataContract.SequenceEnd = pDataContract.RootBooking.Duration.EndDateTime;
            }

            pDataContract.ChangedElements.RemoveRange(dc => !this._ChangedElements.ContainsKey(dc.SequencePosition));

            foreach (var iDC in pDataContract.ChangedElements)
            {
                if (this._ChangedElements[iDC.SequencePosition] != null)
                {
                    iDC.Duration = this._ChangedElements[iDC.SequencePosition].Duration.Clone();
                    iDC.Notice = this._ChangedElements[iDC.SequencePosition].Notice;
                    iDC.Employee = this._ChangedElements[iDC.SequencePosition].Tag as CompactEmployeeDC;
                }
                else
                {
                    iDC.IsDeleted = true;
                }
            }

            foreach (var iEntry in this._ChangedElements.Where(entry => pDataContract.ChangedElements.SingleOrDefault(dc => dc.SequencePosition == entry.Key) == null))
            {
                if (iEntry.Value == null)
                {
                    pDataContract.ChangedElements.Add(
                        new ResourceBookingDC
                            {
                                SequencePosition = iEntry.Key, 
                                IsDeleted = true, 
                                Duration = null
                            });
                }
                else
                {
                    pDataContract.ChangedElements.Add(
                        new ResourceBookingDC
                            {
                                SequencePosition = iEntry.Key, 
                                IsDeleted = false, 
                                Duration = iEntry.Value.Duration.Clone(), 
                                Notice = iEntry.Value.Notice, 
                                Employee = iEntry.Value.Tag as CompactEmployeeDC
                            });
                }
            }

            return pDataContract;
        }

        private bool AreEqual(ResourceBookingDC x, Appointment y)
        {
            NullCompareResult lResult = Utils.NullCompare(x, y);

            return lResult.BothNotNull ? x.Duration.StartDateTime == y.Duration.StartDateTime && x.Duration.EndDateTime == y.Duration.EndDateTime && x.Notice == y.Notice : lResult.Different;
        }


        private DateTime Next(DateTime pDate, ResourceBookingFrequencyType pFrequency)
        {
            switch (pFrequency)
            {
                case ResourceBookingFrequencyType.Daily:
                    return pDate.AddDays(1);
                case ResourceBookingFrequencyType.Weekly:
                    return pDate.AddDays(7);
                case ResourceBookingFrequencyType.Monthly:
                    return pDate.AddMonths(1);
                case ResourceBookingFrequencyType.Yearly:
                    return pDate.AddYears(1);
                default:
                    return pDate;
            }
        }

        private void _RootAppointment_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            this.StoreDirtyInformation(!this.AreEqual(this.DataContract.RootBooking, this._RootAppointment), PropertyName_RootAppointment);
        }

        public BeWoAppointmentCategory AppointmentCategory { get; set; }
        public object Id { get; set; }
        public DateTime Start 
        {
            get
            {
                return DateTime.Now;
            }
            set
            {
            }
        }
        public DateTime End { get; set; }
        public int LabelId { get; set; }
        public string Subject { get; set; }
        public string Description { get; set; }
        public int Status { get; set; }
        public string Location { get; set; }
        public bool AllDay { get; set; }
        public int EventType { get; set; }
        public string RecurrenceInfo { get; set; }
        public string ReminderInfo { get; set; }
        public object ResourceId { get; set; }

        private System.Collections.Generic.Dictionary<string, object> mCustomFields;
        public System.Collections.Generic.Dictionary<string, object> CustomFields
        {
            get
            {
                if (mCustomFields == null)
                    mCustomFields = new System.Collections.Generic.Dictionary<String, object>();
                return mCustomFields;
            }
        }
    }
}