Editor/HumanoidRetargeter/Embedded/Datamodel/Arrays.cs
#nullable enable
#pragma warning disable CS3021 // Upstream CLS attributes; host assembly does not claim CLS compliance.
using System;
using System.Collections.Generic;
using System.Linq;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Numerics;

namespace HumanoidRetargeterDmx
{
    [DebuggerTypeProxy(typeof(Array<>.DebugView))]
    [DebuggerDisplay("Count = {Inner.Count}")]
    public abstract class Array<T> : IList<T>, IList
    {
        internal class DebugView(Array<T> arr)
        {
            readonly Array<T> Arr = arr;

            [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
            public T[] Items { get { return [.. Arr.Inner]; } }
        }

        protected List<T> Inner;

        public virtual AttributeList? Owner
        {
            get => _Owner;
            internal set
            {
                _Owner = value;
            }
        }
        AttributeList? _Owner;

        protected HumanoidRetargeterDmx? OwnerHumanoidRetargeterDmx => Owner?.Owner;

        internal Array()
        {
            Inner = [];
        }

        internal Array(IEnumerable<T> enumerable)
        {
            if (enumerable != null)
                Inner = [.. enumerable];
            else
                Inner = [];
        }

        internal Array(int capacity)
        {
            Inner = new List<T>(capacity);
        }

        public int IndexOf(T item) => Inner.IndexOf(item);

        public void Insert(int index, T item) => Insert_Internal(index, item);
        protected virtual void Insert_Internal(int index, T item) => Inner.Insert(index, item);

        public void AddRange(IEnumerable<T> items) => Inner.AddRange(items);

        public void RemoveAt(int index) => Inner.RemoveAt(index);

        public virtual T this[int index]
        {
            get => Inner[index];
            set => Inner[index] = value;
        }

        public void Add(T item) => Insert(Inner.Count, item);

        public void Clear() => Inner.Clear();

        public bool Contains(T item) => Inner.Contains(item);

        public void CopyTo(T[] array, int offset)
        {
            CopyTo_Internal(array, offset);
        }

        protected virtual void CopyTo_Internal(T[] array, int offset) => Inner.CopyTo(array, offset);

        public int Count => Inner.Count;

        bool ICollection<T>.IsReadOnly { get { return false; } }

        public bool IsFixedSize => throw new NotImplementedException();

        public bool IsReadOnly => throw new NotImplementedException();

        public bool IsSynchronized => throw new NotImplementedException();

        public object SyncRoot => throw new NotImplementedException();

        object? IList.this[int index] { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }

        public bool Remove(T item) => Inner.Remove(item);

        public IEnumerator<T> GetEnumerator() => Inner.GetEnumerator();
        IEnumerator IEnumerable.GetEnumerator() => Inner.GetEnumerator();

        #region IList
        int IList.Add(object? value)
        {
            if (value is not null)
                Add((T)value);
            return Count;
        }

        bool IList.Contains(object? value)
        {
            if (value is null)
                return false;
            return Contains((T)value);
        }

        int IList.IndexOf(object? value)
        {
            if (value is null)
                throw new InvalidOperationException("Trying to get the index of a null object");

            return IndexOf((T)value);
        }

        void IList.Insert(int index, object? value)
        {
            if (value is null)
                throw new InvalidOperationException("Trying to insert a null object");

            Insert(index, (T)value);
        }

        bool IList.IsFixedSize { get { return false; } }
        bool IList.IsReadOnly { get { return false; } }

        void IList.Remove(object? value)
        {
            if (value is null)
                throw new InvalidOperationException("Trying to remove a null object");

            Remove((T)value);
        }

        void ICollection.CopyTo(Array array, int index)
        {
            CopyTo((T[])array, index);
        }

        #endregion IList
    }

    public class ElementArray : Array<Element>
    {
        public ElementArray() { }

        public ElementArray(IEnumerable<Element> enumerable)
            : base(enumerable)
        { }

        public ElementArray(int capacity)
            : base(capacity)
        { }

        /// <summary>
        /// Gets the values in the list without attempting destubbing.
        /// </summary>
        internal IEnumerable<Element> RawList { get { foreach (var elem in Inner) yield return elem; } }

        public override AttributeList? Owner
        {
            get => base.Owner;
            internal set
            {
                base.Owner = value;

                if (OwnerHumanoidRetargeterDmx != null)
                {
                    for (int i = 0; i < Count; i++)
                    {
                        var elem = Inner[i];

                        if (elem == null) continue;
                        if (elem.Owner == null)
                        {
                            var importedElement = OwnerHumanoidRetargeterDmx.ImportElement(elem, HumanoidRetargeterDmx.ImportRecursionMode.Stubs, HumanoidRetargeterDmx.ImportOverwriteMode.Stubs);
                            
                            if(importedElement is not null)
                            {
                                Inner[i] = importedElement;
                            }
                        }
                        else if (elem.Owner != OwnerHumanoidRetargeterDmx)
                            throw new ElementOwnershipException();
                    }
                }
            }
        }

        protected override void Insert_Internal(int index, Element item)
        {
            if (item != null && OwnerHumanoidRetargeterDmx != null)
            {
                if (item.Owner == null)
                {
                    var importedElement = OwnerHumanoidRetargeterDmx.ImportElement(item, HumanoidRetargeterDmx.ImportRecursionMode.Recursive, HumanoidRetargeterDmx.ImportOverwriteMode.Stubs);
                
                    if(importedElement is not null)
                    {
                        item = importedElement;
                    }
                }
                else if (item.Owner != OwnerHumanoidRetargeterDmx)
                {
                    throw new ElementOwnershipException();
                }
            }

            base.Insert_Internal(index, item!);
        }

        public override Element this[int index]
        {
            get
            {
                var elem = Inner[index];
                if (elem != null && elem.Stub && elem.Owner != null)
                {
                    try
                    {
                        elem = Inner[index] = elem.Owner.OnStubRequest(elem.ID)!;
                    }
                    catch (Exception err)
                    {
                        throw new DestubException(this, index, err);
                    }
                }

                if (elem is null)
                {
                    throw new InvalidOperationException("Element at specified index is null");
                }

                return elem;
            }
            set => base[index] = value;
        }
    }

    public class IntArray : Array<int>
    {
        public IntArray() { }
        public IntArray(IEnumerable<int> enumerable)
            : base(enumerable)
        { }
        public IntArray(int capacity)
            : base(capacity)
        { }
    }

    public class FloatArray : Array<float>
    {
        public FloatArray() { }
        public FloatArray(IEnumerable<float> enumerable)
            : base(enumerable)
        { }
        public FloatArray(int capacity)
            : base(capacity)
        { }
    }

    public class BoolArray : Array<bool>
    {
        public BoolArray() { }
        public BoolArray(IEnumerable<bool> enumerable)
            : base(enumerable)
        { }
        public BoolArray(int capacity)
            : base(capacity)
        { }
    }

    public class StringArray : Array<string>
    {
        public StringArray() { }
        public StringArray(IEnumerable<string> enumerable)
            : base(enumerable)
        { }
        public StringArray(int capacity)
            : base(capacity)
        { }
    }

    public class BinaryArray : Array<byte[]>
    {
        public BinaryArray() { }
        public BinaryArray(IEnumerable<byte[]> enumerable)
            : base(enumerable)
        { }
        public BinaryArray(int capacity)
            : base(capacity)
        { }
    }

    public class TimeSpanArray : Array<TimeSpan>
    {
        public TimeSpanArray() { }
        public TimeSpanArray(IEnumerable<TimeSpan> enumerable)
            : base(enumerable)
        { }
        public TimeSpanArray(int capacity)
            : base(capacity)
        { }
    }

    public class ColorArray : Array<Color>
    {
        public ColorArray() { }
        public ColorArray(IEnumerable<Color> enumerable)
            : base(enumerable)
        { }
        public ColorArray(int capacity)
            : base(capacity)
        { }
    }

    public class Vector2Array : Array<global::System.Numerics.Vector2>
    {
        public Vector2Array() { }
        public Vector2Array(IEnumerable<global::System.Numerics.Vector2> enumerable)
            : base(enumerable)
        { }
        public Vector2Array(int capacity)
            : base(capacity)
        { }
    }

    public class Vector3Array : Array<global::System.Numerics.Vector3>
    {
        public Vector3Array() { }
        public Vector3Array(IEnumerable<global::System.Numerics.Vector3> enumerable)
            : base(enumerable)
        { }
        public Vector3Array(int capacity)
            : base(capacity)
        { }
    }

    public class Vector4Array : Array<global::System.Numerics.Vector4>
    {
        public Vector4Array() { }
        public Vector4Array(IEnumerable<global::System.Numerics.Vector4> enumerable)
            : base(enumerable)
        { }
        public Vector4Array(int capacity)
            : base(capacity)
        { }
    }

    public class QuaternionArray : Array<global::System.Numerics.Quaternion>
    {
        public QuaternionArray() { }
        public QuaternionArray(IEnumerable<global::System.Numerics.Quaternion> enumerable)
            : base(enumerable)
        { }
        public QuaternionArray(int capacity)
            : base(capacity)
        { }
    }

    public class MatrixArray : Array<global::System.Numerics.Matrix4x4>
    {
        public MatrixArray() { }
        public MatrixArray(IEnumerable<global::System.Numerics.Matrix4x4> enumerable)
            : base(enumerable)
        { }
        public MatrixArray(int capacity)
            : base(capacity)
        { }
    }

    public class ByteArray : Array<byte>
    {
        public ByteArray() { }
        public ByteArray(IEnumerable<byte> enumerable)
            : base(enumerable)
        { }
        public ByteArray(int capacity)
            : base(capacity)
        { }
    }

    [CLSCompliant(false)]
    public class UInt64Array : Array<ulong>
    {
        public UInt64Array() { }
        public UInt64Array(IEnumerable<ulong> enumerable)
            : base(enumerable)
        { }
        public UInt64Array(int capacity)
            : base(capacity)
        { }
    }
}