using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace CustomControls
{
    public class ClippingBorder : ContentControl
    {
        private Border _border;
        private RectangleGeometry _bottomLeftClip;
        private ContentControl _bottomLeftContentControl;
        private RectangleGeometry _bottomRightClip;
        private ContentControl _bottomRightContentControl;

        private RectangleGeometry _topLeftClip;
        private ContentControl _topLeftContentControl;
        private RectangleGeometry _topRightClip;
        private ContentControl _topRightContentControl;

        // Fields
        public static readonly DependencyProperty ClipContentProperty = DependencyProperty.Register("ClipContent",
            typeof (bool), typeof (ClippingBorder), new PropertyMetadata (new PropertyChangedCallback(ClipContent_Changed)));

        public static readonly DependencyProperty CornerRadiusProperty = DependencyProperty.Register("CornerRadius",                                                                                                     
            typeof (CornerRadius), typeof (ClippingBorder), new PropertyMetadata (new PropertyChangedCallback(CornerRadius_Changed)));

        // Methods
        public ClippingBorder()
        {
            DefaultStyleKey = typeof (ClippingBorder);
            SizeChanged += ClippingBorder_SizeChanged;
        }

        [Category("Appearance"), Description("Sets whether the content is clipped or not.")]
        public bool ClipContent
        {
            get { return (bool) GetValue(ClipContentProperty); }
            set { SetValue(ClipContentProperty, value); }
        }

        [Description("Sets the corner radius on the border."), Category("Appearance")]
        public CornerRadius CornerRadius
        {
            get { return (CornerRadius) GetValue(CornerRadiusProperty); }
            set { SetValue(CornerRadiusProperty, value); }
        }

        private static void ClipContent_Changed(DependencyObject dependencyObject,
                                                DependencyPropertyChangedEventArgs eventArgs)
        {
            ((ClippingBorder) dependencyObject).UpdateClipContent((bool) eventArgs.NewValue);
        }

        private void ClippingBorder_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (ClipContent)
            {
                UpdateClipSize(e.NewSize);
            }
        }

        private void ContentControl_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (ClipContent)
            {
                UpdateClipSize(new Size(ActualWidth, ActualHeight));
            }
        }

        private static void CornerRadius_Changed(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
        {
            ((ClippingBorder) dependencyObject).UpdateCornerRadius((CornerRadius) eventArgs.NewValue);
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            _border = GetTemplateChild("PART_Border") as Border;
            _topLeftContentControl = GetTemplateChild("PART_TopLeftContentControl") as ContentControl;
            _topRightContentControl = GetTemplateChild("PART_TopRightContentControl") as ContentControl;
            _bottomRightContentControl = GetTemplateChild("PART_BottomRightContentControl") as ContentControl;
            _bottomLeftContentControl = GetTemplateChild("PART_BottomLeftContentControl") as ContentControl;
            if (_topLeftContentControl != null)
            {
                _topLeftContentControl.SizeChanged += ContentControl_SizeChanged;
            }
            _topLeftClip = GetTemplateChild("PART_TopLeftClip") as RectangleGeometry;
            _topRightClip = GetTemplateChild("PART_TopRightClip") as RectangleGeometry;
            _bottomRightClip = GetTemplateChild("PART_BottomRightClip") as RectangleGeometry;
            _bottomLeftClip = GetTemplateChild("PART_BottomLeftClip") as RectangleGeometry;
            UpdateClipContent(ClipContent);
            UpdateCornerRadius(CornerRadius);
        }

        internal void UpdateClipContent(bool clipContent)
        {
            if (clipContent)
            {
                if (_topLeftContentControl != null)
                {
                    _topLeftContentControl.Clip = _topLeftClip;
                }
                if (_topRightContentControl != null)
                {
                    _topRightContentControl.Clip = _topRightClip;
                }
                if (_bottomRightContentControl != null)
                {
                    _bottomRightContentControl.Clip = _bottomRightClip;
                }
                if (_bottomLeftContentControl != null)
                {
                    _bottomLeftContentControl.Clip = _bottomLeftClip;
                }
                UpdateClipSize(new Size(ActualWidth, ActualHeight));
            }
            else
            {
                if (_topLeftContentControl != null)
                {
                    _topLeftContentControl.Clip = null;
                }
                if (_topRightContentControl != null)
                {
                    _topRightContentControl.Clip = null;
                }
                if (_bottomRightContentControl != null)
                {
                    _bottomRightContentControl.Clip = null;
                }
                if (_bottomLeftContentControl != null)
                {
                    _bottomLeftContentControl.Clip = null;
                }
            }
        }

        private void UpdateClipSize(Size size)
        {
            if ((size.Width <= 0.0) && (size.Height <= 0.0))
            {
                return;
            }

            var contentWidth = Math.Max( 0.0, ((size.Width - BorderThickness.Left) - BorderThickness.Right));
            var contentHeight = Math.Max( 0.0, ((size.Height - BorderThickness.Top) - BorderThickness.Bottom));
            if (_topLeftClip != null)
            {
                _topLeftClip.Rect = new Rect(0.0, 0.0, contentWidth + (CornerRadius.TopLeft*2.0), contentHeight + (CornerRadius.TopLeft*2.0));
            }
            if (_topRightClip != null)
            {
                _topRightClip.Rect = new Rect(0.0 - CornerRadius.TopRight, 0.0, contentWidth + CornerRadius.TopRight,
                                              contentHeight + CornerRadius.TopRight);
            }
            if (_bottomRightClip != null)
            {
                _bottomRightClip.Rect = new Rect(0.0 - CornerRadius.BottomRight, 0.0 - CornerRadius.BottomRight, 
                                                 contentWidth + CornerRadius.BottomRight, contentHeight + CornerRadius.BottomRight);
            }
            if (_bottomLeftClip != null)
            {
                _bottomLeftClip.Rect = new Rect(0.0, 0.0 - CornerRadius.BottomLeft, contentWidth + CornerRadius.BottomLeft, contentHeight + CornerRadius.BottomLeft);
            }
        }

        internal void UpdateCornerRadius(CornerRadius newCornerRadius)
        {
            if (_border != null)
            {
                _border.CornerRadius = newCornerRadius;
            }
            if (_topLeftClip != null)
            {
                _topLeftClip.RadiusX = _topLeftClip.RadiusY = newCornerRadius.TopLeft - (Math.Min(BorderThickness.Left, BorderThickness.Top)/2.0);
            }
            if (_topRightClip != null)
            {
                _topRightClip.RadiusX = _topRightClip.RadiusY = newCornerRadius.TopRight - (Math.Min(BorderThickness.Top, BorderThickness.Right)/2.0);
            }
            if (_bottomRightClip != null)
            {
                _bottomRightClip.RadiusX = _bottomRightClip.RadiusY = newCornerRadius.BottomRight - (Math.Min(BorderThickness.Right, BorderThickness.Bottom)/2.0);
            }
            if (_bottomLeftClip != null)
            {
                _bottomLeftClip.RadiusX = _bottomLeftClip.RadiusY = newCornerRadius.BottomLeft - (Math.Min(BorderThickness.Bottom, BorderThickness.Left)/2.0);
            }

            UpdateClipSize(new Size(ActualWidth, ActualHeight));
        }
    }
}