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

namespace InABox.Core
{
    public class FluentList<T> : IEnumerable<T>
    {
        public delegate void FluentListChangedEvent(object sender, EventArgs args);

        private int _enabled = 0;
        private bool _changed = false;

        private readonly List<T> _values = new List<T>();

        public bool Enabled
        {
            get => _enabled == 0;
            set
            {
                if (value)
                    EndUpdate();
                else
                    BeginUpdate();
            }
        }

        public T this[int i]
        {
            get => _values[i];
            set
            {
                _values[i] = value;
                Changed();
            }
        }

        public int Count => _values.Count;

        public IEnumerator<T> GetEnumerator()
        {
            return ((IEnumerable<T>)_values).GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return ((IEnumerable<T>)_values).GetEnumerator();
        }

        public FluentList<T> Add(T value)
        {
            if (!_values.Contains(value))
            {
                _changed = true;
                _values.Add(value);
            }
            return Changed();
        }

        public FluentList<T> AddRange(params T[] values)
        {
            foreach (var value in values)
                if (!_values.Contains(value))
                {
                    _changed = true;
                    _values.Add(value);
                }
            return Changed();
        }

        public FluentList<T> AddRange(IEnumerable<T> values)
        {
            foreach (var value in values)
                if (!_values.Contains(value))
                {
                    _changed = true;
                    _values.Add(value);
                }
            return Changed();
        }

        public FluentList<T> Remove(T value)
        {
            _changed = _values.Remove(value) || _changed;
            return Changed();
        }

        public FluentList<T> RemoveAt(int index)
        {
            if (index > 0 && index < _values.Count)
            {
                _values.RemoveAt(index);
                _changed = true;
            }
            return Changed();
        }

        public FluentList<T> RemoveRange(params T[] values)
        {
            foreach (var value in values)
                _changed = _values.Remove(value) || _changed;
            return Changed();
        }

        public FluentList<T> RemoveRange(IEnumerable<T> values)
        {
            foreach (var value in values)
                _changed = _values.Remove(value) || _changed;
            return Changed();
        }

        public FluentList<T> Clear()
        {
            if(_values.Count > 0)
            {
                _values.Clear();
                _changed = true;
            }
            return Changed();
        }

        public event FluentListChangedEvent OnChanged;

        public FluentList<T> BeginUpdate()
        {
            ++_enabled;
            return this;
        }

        private FluentList<T> Changed()
        {
            if (_enabled == 0 && _changed)
            {
                _changed = false;
                OnChanged?.Invoke(this, new EventArgs());
            }
            return this;
        }

        public FluentList<T> EndUpdate()
        {
            --_enabled;
            return Changed();
        }
    }
}