using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SharpGLTF.Memory
{
    /// <summary>
    /// Represents a readable and sometimes writeable(*) array of elements
    /// </summary>
    /// <typeparam name="T">
    /// For Index Buffers: <see cref="UInt32"/><br/>
    /// For Vertex Buffers: <see cref="System.Numerics.Vector2"/>, <see cref="System.Numerics.Vector3"/>, <see cref="System.Numerics.Vector4"/> and so on.<br/>
    /// For animations and other plain data: <see cref="float"/>, <see cref="System.Numerics.Quaternion"/>, <see cref="System.Numerics.Quaternion"/> and so on.<br/>
    /// </typeparam>
    /// <remarks>
    /// <para>
    /// This interface is exposed by <see cref="Schema2.Accessor"/> to easily encode/decode packed and strided elements
    /// </para>
    /// <para>
    /// Implemented by:<br/>
    /// <see cref="SparseArray{T}"/> (Read Only)<br/>
    /// <see cref="IntegerArray"/><br/>
    /// <see cref="ScalarArray"/><br/>
    /// <see cref="Vector2Array"/><br/>
    /// <see cref="Vector3Array"/><br/>
    /// <see cref="Vector4Array"/><br/>
    /// <see cref="QuaternionArray"/><br/>
    /// <see cref="Matrix2x2Array"/><br/>
    /// <see cref="Matrix3x2Array"/><br/>
    /// <see cref="Matrix3x3Array"/><br/>
    /// <see cref="Matrix4x3Array"/><br/>
    /// <see cref="Matrix4x3Array"/><br/>
    /// </para>
    /// </remarks>
    public interface IAccessorArray<T> : IReadOnlyList<T>  , IList<T>
    {
        // Because this.Count and this[index] exist at both at IList<T> and IReadOnlyList<T>,
        // attempting to use them may cause an ambiguity error. This will be fixed in Net10.        
        // see:
        // https://github.com/dotnet/runtime/issues/31001#issuecomment-2942678308
        // https://github.com/dotnet/runtime/pull/115802

        new T this[int index] { get; set; }
        new int Count { get; }                
    }

    public readonly struct ZeroAccessorArray<T> : IAccessorArray<T>
    {
        static ZeroAccessorArray()
        {
            _Default = default(T);
            if (typeof(T) == typeof(float[])) _Default = (T)(Object)Array.Empty<T>();
        }

        public ZeroAccessorArray(int count)
        {
            Count = count;            
        }

        private static readonly T _Default;

        public bool IsReadOnly => true;

        public T this[int index] { get => _Default; set => throw new NotSupportedException(); }

        public int Count { get; }

        public int IndexOf(T item)
        {
            return Count > 0 && item.Equals(_Default) ? 0 : -1;
        }

        public bool Contains(T item)
        {
            return Count > 0 && item.Equals(_Default);
        }

        public void CopyTo(T[] array, int arrayIndex)
        {
            for(int i=0; i < Count; ++i)
            {
                array[i + arrayIndex] = default;
            }
        }        

        public IEnumerator<T> GetEnumerator()
        {
            return Enumerable.Repeat(_Default, Count).GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return Enumerable.Repeat(_Default, Count).GetEnumerator();
        }

        void IList<T>.Insert(int index, T item) { throw new NotSupportedException(); }

        void IList<T>.RemoveAt(int index) { throw new NotSupportedException(); }

        void ICollection<T>.Add(T item) { throw new NotSupportedException(); }

        void ICollection<T>.Clear() { throw new NotSupportedException(); }

        bool ICollection<T>.Remove(T item) { throw new NotSupportedException(); }
    }

    readonly struct BooleanArrayOverIntegerArray : IAccessorArray<Boolean>
    {
        public BooleanArrayOverIntegerArray(IAccessorArray<uint> source) : this()
        {
            _Source = source;
        }

        private readonly IAccessorArray<uint> _Source;        

        public bool this[int index]
        {
            get => _Source[index] != 0;
            set => _Source[index] = value ? (uint)1 : 0;
        }

        public bool IsReadOnly => true;

        public int Count => _Source.Count;

        public bool Contains(Boolean item) { return IndexOf(item) >= 0; }

        public int IndexOf(Boolean item)
        {
            var value = item ? (uint)1 : 0;
            return _Source.IndexOf(value);
        }        

        public void CopyTo(Boolean[] array, int arrayIndex)
        {
            for (int i = 0; i < Count; ++i)
            {
                array[i + arrayIndex] = this[i];
            }
        }

        public IEnumerator<Boolean> GetEnumerator()
        {
            var c = _Source.Count;
            for (int i = 0; i < c; ++i) yield return this[i];
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            var c = _Source.Count;
            for (int i = 0; i < c; ++i) yield return this[i];
        }

        void IList<Boolean>.Insert(int index, Boolean item) { throw new NotSupportedException(); }

        void IList<Boolean>.RemoveAt(int index) { throw new NotSupportedException(); }

        void ICollection<Boolean>.Add(Boolean item) { throw new NotSupportedException(); }

        void ICollection<Boolean>.Clear() { throw new NotSupportedException(); }

        bool ICollection<Boolean>.Remove(Boolean item) { throw new NotSupportedException(); }
    }
}
