using System.Collections.Specialized;
using System.Linq;
#if Silverlight
using ItemsControl = Telerik.Windows.Controls.ItemsControl;
#else
using ItemsControl = System.Windows.Controls.ItemsControl;
#endif
using Microsoft.Practices.Composite.Presentation.Regions;
using Microsoft.Practices.Composite.Regions;
using Telerik.Windows.Controls;
using System.Windows.Data;
using Microsoft.Practices.Composite.Presentation.Regions.Behaviors;
using System.Windows;

using System;
using Telerik.Windows.Controls.Docking;
using System.Collections.ObjectModel;

namespace RadDockingAndPRISM
{
	public class RadPaneGroupRegionAdapter : RegionAdapterBase<RadPaneGroup>
	{
		public RadPaneGroupRegionAdapter(IRegionBehaviorFactory regionBehaviorFactory)
			: base(regionBehaviorFactory)
		{
		}

		protected override void Adapt(IRegion region, RadPaneGroup regionTarget)
		{
            var docking = regionTarget.ParentOfType<RadDocking>();
            if (docking.DockingPanesFactory == null)
            {
                docking.DockingPanesFactory = new CustomDockingPanesFactory();
            }

            var store = DocumentsStoreAttachment.GetPanesStore(docking);
            if (store == null)
            {
                store = new PanesStore();
                DocumentsStoreAttachment.SetPanesStore(docking, store);
                BindingOperations.SetBinding(
               docking,
               RadDocking.PanesSourceProperty,
               new Binding("PanesSource") { Source = store });
            }
        }

		protected override IRegion CreateRegion()
		{
			return new SingleActiveRegion();
		}

        protected override void AttachBehaviors(IRegion region, RadPaneGroup regionTarget)
        {
            if (region == null)
                throw new System.ArgumentNullException("region");

            var docking = regionTarget.ParentOfType<RadDocking>();
           
            // Add the behavior that syncs the items source items with the rest of the items
            region.Behaviors.Add(RadPaneGroupSyncBehavior.BehaviorKey, new RadPaneGroupSyncBehavior()
            {
                HostControl = regionTarget,
                Docking = docking
            });

            base.AttachBehaviors(region, regionTarget);
        }

        private class PanesStore
        {
            public readonly ObservableCollection<object> Panes = new ObservableCollection<object>();
            ReadOnlyObservableCollection<object> _readonlyDocumentsList;
            public ReadOnlyObservableCollection<object> PanesSource
            {
                get
                {
                    return _readonlyDocumentsList ??
                           (_readonlyDocumentsList = new ReadOnlyObservableCollection<object>(Panes));
                }
            }
        }

        private static class DocumentsStoreAttachment
        {
            public static readonly DependencyProperty PanesStoreProperty = DependencyProperty.RegisterAttached(
       "PanesStore",
       typeof(PanesStore),
       typeof(DocumentsStoreAttachment),
       new PropertyMetadata(null)
     );
            public static void SetPanesStore(UIElement element, PanesStore value)
            {
                element.SetValue(PanesStoreProperty, value);
            }
            public static PanesStore GetPanesStore(UIElement element)
            {
                return (PanesStore)element.GetValue(PanesStoreProperty);
            }
        }

        private class CustomDockingPanesFactory : DockingPanesFactory
        {
            public RadPaneGroup ActiveGroup { get; internal set; }

            protected override void AddPane(RadDocking radDocking, RadPane pane)
            {
                var group = ActiveGroup;

                pane.SetValue(RadPane.HeaderProperty, DependencyProperty.UnsetValue);

                var style = group.ItemContainerStyle;
                if (style != null)
                {
                    pane.SetValue(RadPane.StyleProperty, style);
                }
                group?.Items.Add(pane);
            }
        }

        private class RadPaneGroupSyncBehavior : RegionBehavior, IHostAwareRegionBehavior
        {
            public static readonly string BehaviorKey = "RadPaneGroupSyncBehavior";
            private bool _updatingActiveViewsInManagerActiveContentChanged;
            private RadPaneGroup _radPaneGroup;
            private PanesStore _documentStore;

            public DependencyObject HostControl
            {
                get
                {
                    return _radPaneGroup;
                }

                set
                {
                    _radPaneGroup = value as RadPaneGroup;
                }
            }



            public RadDocking Docking { get; internal set; }

            /// <summary>
            /// Starts to monitor the <see cref="IRegion"/> to keep it in synch with the items of the <see cref="HostControl"/>.
            /// </summary>
            protected override void OnAttach()
            {
                bool itemsSourceIsSet = _radPaneGroup.ItemsSource != null;


                if (itemsSourceIsSet)
                {
                    throw new InvalidOperationException();
                }

                SynchronizeItems();

                Docking.ActivePaneChanged += ManagerActiveContentChanged;
                Region.ActiveViews.CollectionChanged += ActiveViews_CollectionChanged;
                Region.Views.CollectionChanged += Views_CollectionChanged;
                Docking.Close += _dockingManager_DocumentClosed;

            }

            private void _dockingManager_DocumentClosed(object sender, StateChangeEventArgs e)
            {
                foreach (var pane in e.Panes)
                {
                    if (Region.Views.Contains(pane.Content))
                    {
                        Region.Remove(pane.Content);
                    }
                }

            }


            private void Views_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
            {
                if (e.Action == NotifyCollectionChangedAction.Add)
                {
                    int startIndex = e.NewStartingIndex;

                    foreach (var newItem in e.NewItems)
                    {
                        var panesFactory = ((CustomDockingPanesFactory)Docking.DockingPanesFactory);
                        panesFactory.ActiveGroup = _radPaneGroup;
                        _documentStore.Panes.Insert(0, newItem);
                    }

                }
                else if (e.Action == NotifyCollectionChangedAction.Remove)
                {
                    foreach (object oldItem in e.OldItems)
                    {
                        _documentStore.Panes.Remove(oldItem);
                    }
                }
            }

            private void SynchronizeItems()
            {
                var store = DocumentsStoreAttachment.GetPanesStore(Docking);



                _documentStore = store;
                foreach (object view in Region.Views)
                {
                    ((CustomDockingPanesFactory)Docking.DockingPanesFactory).ActiveGroup = _radPaneGroup;
                    _documentStore.Panes.Insert(0, view);

                }
            }

            private void ActiveViews_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
            {
                if (_updatingActiveViewsInManagerActiveContentChanged)
                {
                    return;
                }

                if (e.Action == NotifyCollectionChangedAction.Add)
                {
                    if (Docking.ActivePane != null
                        && Docking.ActivePane != e.NewItems[0]
                        && Region.ActiveViews.Contains(Docking.ActivePane.Content))
                    {
                        Region.Deactivate(Docking.ActivePane.Content);
                    }

                    var pane = Docking.Panes.FirstOrDefault(x => x.Content.Equals(e.NewItems[0]));
                    Docking.ActivePane = pane;
                }
                else if (e.Action == NotifyCollectionChangedAction.Remove &&
                         e.OldItems.Contains(Docking.ActivePane))
                {
                    Docking.ActivePane = null;
                }
            }

            private void ManagerActiveContentChanged(object sender, EventArgs e)
            {
                try
                {
                    _updatingActiveViewsInManagerActiveContentChanged = true;

                    if (Docking.Equals(sender))
                    {
                        var activePane = Docking.ActivePane;
                        if (activePane != null)
                        {
                            var activeViews = Region.ActiveViews.Where(it => it != activePane.Content).ToList();
                            foreach (var item in activeViews)
                            {
                                Region.Deactivate(item);
                            }

                            if (Region.Views.Contains(activePane.Content) && !Region.ActiveViews.Contains(activePane.Content))
                            {
                                Region.Activate(activePane.Content);
                            }
                        }



                    }
                }
                finally
                {
                    _updatingActiveViewsInManagerActiveContentChanged = false;
                }
            }

        }
    }
}
