using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Windows;

namespace HandyControl.Media.Animation
{
    public class GeometryKeyFrameCollection : Freezable, IList
    {
        private List<GeometryKeyFrame> _keyFrames;
        
        private static GeometryKeyFrameCollection s_emptyCollection;

        public GeometryKeyFrameCollection()
        {
            _keyFrames = new List<GeometryKeyFrame>(2);
        }

        public static GeometryKeyFrameCollection Empty
        {
            get
            {
                if (s_emptyCollection == null)
                {
                    var emptyCollection = new GeometryKeyFrameCollection
                    {
                        _keyFrames = new List<GeometryKeyFrame>(0)
                    };

                    emptyCollection.Freeze();

                    s_emptyCollection = emptyCollection;
                }

                return s_emptyCollection;
            } 
        }

        public new GeometryKeyFrameCollection Clone() => (GeometryKeyFrameCollection)base.Clone();

        protected override Freezable CreateInstanceCore() => new GeometryKeyFrameCollection();

        protected override void CloneCore(Freezable sourceFreezable)
        {
            var sourceCollection = (GeometryKeyFrameCollection)sourceFreezable;
            base.CloneCore(sourceFreezable);

            var count = sourceCollection._keyFrames.Count;

            _keyFrames = new List<GeometryKeyFrame>(count);

            for (var i = 0; i < count; i++)
            {
                var keyFrame = (GeometryKeyFrame)sourceCollection._keyFrames[i].Clone();
                _keyFrames.Add(keyFrame);
                OnFreezablePropertyChanged(null, keyFrame);
            }
        }

        protected override void CloneCurrentValueCore(Freezable sourceFreezable)
        {
            var sourceCollection = (GeometryKeyFrameCollection)sourceFreezable;
            base.CloneCurrentValueCore(sourceFreezable);

            var count = sourceCollection._keyFrames.Count;

            _keyFrames = new List<GeometryKeyFrame>(count);

            for (var i = 0; i < count; i++)
            {
                var keyFrame = (GeometryKeyFrame)sourceCollection._keyFrames[i].CloneCurrentValue();
                _keyFrames.Add(keyFrame);
                OnFreezablePropertyChanged(null, keyFrame);
            }
        }

        protected override void GetAsFrozenCore(Freezable sourceFreezable)
        {
            var sourceCollection = (GeometryKeyFrameCollection)sourceFreezable;
            base.GetAsFrozenCore(sourceFreezable);

            var count = sourceCollection._keyFrames.Count;

            _keyFrames = new List<GeometryKeyFrame>(count);

            for (var i = 0; i < count; i++)
            {
                var keyFrame = (GeometryKeyFrame)sourceCollection._keyFrames[i].GetAsFrozen();
                _keyFrames.Add(keyFrame);
                OnFreezablePropertyChanged(null, keyFrame);
            }
        }

        protected override void GetCurrentValueAsFrozenCore(Freezable sourceFreezable)
        {
            var sourceCollection = (GeometryKeyFrameCollection)sourceFreezable;
            base.GetCurrentValueAsFrozenCore(sourceFreezable);

            var count = sourceCollection._keyFrames.Count;

            _keyFrames = new List<GeometryKeyFrame>(count);

            for (var i = 0; i < count; i++)
            {
                var keyFrame = (GeometryKeyFrame)sourceCollection._keyFrames[i].GetCurrentValueAsFrozen();
                _keyFrames.Add(keyFrame);
                OnFreezablePropertyChanged(null, keyFrame);
            }
        }

        protected override bool FreezeCore(bool isChecking)
        {
            var canFreeze = base.FreezeCore(isChecking);

            for (var i = 0; i < _keyFrames.Count && canFreeze; i++)
            {
                canFreeze &= Freeze(_keyFrames[i], isChecking);
            }

            return canFreeze;
        }

        public IEnumerator GetEnumerator()
        {
            ReadPreamble();

            return _keyFrames.GetEnumerator();
        }

        void ICollection.CopyTo(Array array, int index)
        {
            ReadPreamble();

            ((ICollection)_keyFrames).CopyTo(array, index);
        }

        public void CopyTo(GeometryKeyFrame[] array, int index)
        {
            ReadPreamble();

            _keyFrames.CopyTo(array, index);
        }

        public int Count
        {
            get
            {
                ReadPreamble();

                return _keyFrames.Count;
            }
        }
        
        public object SyncRoot
        {
            get
            {
                ReadPreamble();

                return ((ICollection)_keyFrames).SyncRoot;
            }
        }

        public bool IsSynchronized
        {
            get
            {
                ReadPreamble();

                return IsFrozen || Dispatcher != null;
            }
        }

        int IList.Add(object keyFrame) => Add((GeometryKeyFrame)keyFrame);

        public int Add(GeometryKeyFrame keyFrame)
        {
            if (keyFrame == null)
            {
                throw new ArgumentNullException(nameof(keyFrame));
            }

            WritePreamble();

            OnFreezablePropertyChanged(null, keyFrame);
            _keyFrames.Add(keyFrame);

            WritePostscript();

            return _keyFrames.Count - 1;
        }

        public void Clear()
        {
            WritePreamble();

            if (_keyFrames.Count <= 0) return;
            
            foreach (var frame in _keyFrames)
            {
                OnFreezablePropertyChanged(frame, null);
            }

            _keyFrames.Clear();

            WritePostscript();
        }

        bool IList.Contains(object keyFrame) => Contains((GeometryKeyFrame)keyFrame);

        public bool Contains(GeometryKeyFrame keyFrame)
        {
            ReadPreamble();

            return _keyFrames.Contains(keyFrame);
        }

        int IList.IndexOf(object keyFrame) => IndexOf((GeometryKeyFrame)keyFrame);

        public int IndexOf(GeometryKeyFrame keyFrame)
        {
            ReadPreamble();

            return _keyFrames.IndexOf(keyFrame);
        }

        void IList.Insert(int index, object keyFrame) => Insert(index, (GeometryKeyFrame)keyFrame);

        public void Insert(int index, GeometryKeyFrame keyFrame)
        {
            if (keyFrame == null)
            {
                throw new ArgumentNullException(nameof(keyFrame));
            }

            WritePreamble();

            OnFreezablePropertyChanged(null, keyFrame);
            _keyFrames.Insert(index, keyFrame);

            WritePostscript();
        }

        void IList.Remove(object keyFrame) => Remove((GeometryKeyFrame)keyFrame);

        public void Remove(GeometryKeyFrame keyFrame)
        {
            WritePreamble();

            if (_keyFrames.Contains(keyFrame))
            {
                OnFreezablePropertyChanged(keyFrame, null);
                _keyFrames.Remove(keyFrame);

                WritePostscript();
            }
        }

        public void RemoveAt(int index)
        {
            WritePreamble();

            OnFreezablePropertyChanged(_keyFrames[index], null);
            _keyFrames.RemoveAt(index);

            WritePostscript();
        }

        object IList.this[int index]
        {
            get => this[index];
            set => this[index] = (GeometryKeyFrame)value;
        }

        public GeometryKeyFrame this[int index]
        {
            get
            {
                ReadPreamble();

                return _keyFrames[index];
            }
            set
            {
                if (value == null)
                {
                    throw new ArgumentNullException(string.Format(CultureInfo.InvariantCulture, "DoubleKeyFrameCollection[{0}]", index));
                }

                WritePreamble();

                if (value != _keyFrames[index])
                {
                    OnFreezablePropertyChanged(_keyFrames[index], value);
                    _keyFrames[index] = value;

                    WritePostscript();
                }
            }
        }

        public bool IsReadOnly
        {
            get
            {
                ReadPreamble();

                return IsFrozen;
            }
        }

        public bool IsFixedSize
        {
            get
            {
                ReadPreamble();

                return IsFrozen;
            }
        }
    }
}
