using System;
using System.Globalization;
using System.Windows;
using System.Windows.Markup;
using System.Windows.Media.Animation;

namespace HandyControl.Interactivity
{
    [ContentProperty("Actions")]
    public abstract class TriggerBase : Animatable, IAttachedObject
    {
        private static readonly DependencyPropertyKey ActionsPropertyKey =
            DependencyProperty.RegisterReadOnly("Actions", typeof(TriggerActionCollection), typeof(TriggerBase),
                new FrameworkPropertyMetadata());

        public static readonly DependencyProperty ActionsProperty = ActionsPropertyKey.DependencyProperty;

        private DependencyObject _associatedObject;
        private readonly Type _associatedObjectTypeConstraint;

        internal TriggerBase(Type associatedObjectTypeConstraint)
        {
            _associatedObjectTypeConstraint = associatedObjectTypeConstraint;
            var actions = new TriggerActionCollection();
            SetValue(ActionsPropertyKey, actions);
        }

        public TriggerActionCollection Actions =>
            (TriggerActionCollection) GetValue(ActionsProperty);

        protected DependencyObject AssociatedObject
        {
            get
            {
                ReadPreamble();
                return _associatedObject;
            }
        }

        protected virtual Type AssociatedObjectTypeConstraint
        {
            get
            {
                ReadPreamble();
                return _associatedObjectTypeConstraint;
            }
        }

        public void Attach(DependencyObject dependencyObject)
        {
            if (!Equals(dependencyObject, AssociatedObject))
            {
                if (AssociatedObject != null)
                    throw new InvalidOperationException(ExceptionStringTable
                        .CannotHostTriggerMultipleTimesExceptionMessage);
                if (dependencyObject != null &&
                    !AssociatedObjectTypeConstraint.IsInstanceOfType(dependencyObject))
                    throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
                        ExceptionStringTable.TypeConstraintViolatedExceptionMessage,
                        new object[]
                            {GetType().Name, dependencyObject.GetType().Name, AssociatedObjectTypeConstraint.Name}));
                WritePreamble();
                _associatedObject = dependencyObject;
                WritePostscript();
                Actions.Attach(dependencyObject);
                OnAttached();
            }
        }

        public void Detach()
        {
            OnDetaching();
            WritePreamble();
            _associatedObject = null;
            WritePostscript();
            Actions.Detach();
        }

        DependencyObject IAttachedObject.AssociatedObject =>
            AssociatedObject;

        public event EventHandler<PreviewInvokeEventArgs> PreviewInvoke;

        protected override Freezable CreateInstanceCore()
        {
            return (Freezable) Activator.CreateInstance(GetType());
        }

        protected void InvokeActions(object parameter)
        {
            if (PreviewInvoke != null)
            {
                var e = new PreviewInvokeEventArgs();
                PreviewInvoke(this, e);
                if (e.Cancelling)
                    return;
            }
            foreach (var action in Actions)
                action.CallInvoke(parameter);
        }

        protected virtual void OnAttached()
        {
        }

        protected virtual void OnDetaching()
        {
        }
    }
}