Editor/HumanoidRetargeter/Embedded/ValveResourceFormat/Resource/Blocks/ResourceEditInfo2.cs
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using HumanoidRetargeterVrf.Utils;
using System.Globalization;
using System.IO;
using HumanoidRetargeterVrf.Blocks.ResourceEditInfoStructs;
using HumanoidRetargeterVrf.ResourceTypes;
using HumanoidRetargeterVrf.Serialization.KeyValues;

#nullable disable

namespace HumanoidRetargeterVrf.Blocks
{
    /// <summary>
    /// "RED2" block. CResourceEditInfo.
    /// </summary>
    public class ResourceEditInfo2 : ResourceEditInfo
    {
        /// <inheritdoc/>
        public override BlockType Type => BlockType.RED2;

        private BinaryKV3 BackingData;

        /// <summary>
        /// Gets the list of weak references.
        /// </summary>
        public List<string> WeakReferenceList { get; } = [];

        /// <summary>
        /// Gets the subasset references.
        /// </summary>
        public Dictionary<string, Dictionary<string, int>> SubassetReferences { get; private set; }

        /// <summary>
        /// Gets the subasset definitions.
        /// </summary>
        public Dictionary<string, string[]> SubassetDefinitions { get; private set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="ResourceEditInfo2"/> class.
        /// </summary>
        public ResourceEditInfo2()
        {
            //
        }

        /// <inheritdoc/>
        public override void Read(BinaryReader reader)
        {
            var kv3 = new BinaryKV3
            {
                Offset = Offset,
                Size = Size,
                Resource = Resource,
            };

            kv3.Read(reader);
            BackingData = kv3;

            static void ReadItems<T>(BinaryKV3 kv3, List<T> list, string key, Func<KVObject, T> constructor)
            {
                var container = kv3.Data.Properties.GetValueOrDefault(key).Value as KVObject;
                ArgumentNullException.ThrowIfNull(container, key);
                ArgumentOutOfRangeException.ThrowIfEqual(container.IsArray, false, key);

                list.EnsureCapacity(container.Count);

                foreach (var item in container)
                {
                    var kvObject = item.Value as KVObject;
                    var newItem = constructor.Invoke(kvObject);
                    list.Add(newItem);
                }
            }

            ReadItems(kv3, InputDependencies, "m_InputDependencies", static (KVObject data) => new InputDependency(data));
            ReadItems(kv3, AdditionalInputDependencies, "m_AdditionalInputDependencies", static (KVObject data) => new InputDependency(data));
            ReadItems(kv3, ArgumentDependencies, "m_ArgumentDependencies", static (KVObject data) => new ArgumentDependency(data));
            ReadItems(kv3, SpecialDependencies, "m_SpecialDependencies", static (KVObject data) => new SpecialDependency(data));
            ReadItems(kv3, AdditionalRelatedFiles, "m_AdditionalRelatedFiles", static (KVObject data) => new AdditionalRelatedFile(data));

            var childResources = kv3.Data.GetArray<string>("m_ChildResourceList");
            ChildResourceList.AddRange(childResources);

            var weakReferences = kv3.Data.GetArray<string>("m_WeakReferenceList");
            if (weakReferences is not null)
            {
                WeakReferenceList.AddRange(weakReferences);
            }

            var searchableData = kv3.Data.GetProperty<KVObject>("m_SearchableUserData");
            if (searchableData is not null)
            {
                SearchableUserData.Properties.EnsureCapacity(searchableData.Properties.Count);

                foreach (var property in searchableData.Properties)
                {
                    SearchableUserData.Properties.Add(property.Key, property.Value);
                }
            }

            var subassetReferences = kv3.Data.GetProperty<KVObject>("m_SubassetReferences");
            if (subassetReferences != null)
            {
                SubassetReferences = new(capacity: subassetReferences.Count);

                foreach (var property in subassetReferences)
                {
                    var subassetType = property.Key;
                    var perTypeReferencesKv = property.Value as KVObject;

                    var perTypeReferences = new Dictionary<string, int>(capacity: perTypeReferencesKv.Count);

                    foreach (var (refName, refCount) in perTypeReferencesKv)
                    {
                        perTypeReferences.Add(refName, Convert.ToInt32(refCount, CultureInfo.InvariantCulture));
                    }

                    SubassetReferences.Add(subassetType, perTypeReferences);
                }
            }

            var subassetDefinitions = kv3.Data.GetProperty<KVObject>("m_SubassetDefinitions");
            if (subassetDefinitions != null)
            {
                SubassetDefinitions = new(capacity: subassetDefinitions.Count);

                foreach (var property in subassetDefinitions)
                {
                    var subassetType = property.Key;
                    var definitions = subassetDefinitions.GetArray<string>(subassetType);

                    SubassetDefinitions.Add(subassetType, definitions);
                }
            }
        }

        /// <inheritdoc/>
        public override void Serialize(Stream stream)
        {
            BackingData.Serialize(stream);
        }

        /// <inheritdoc/>
        public override void WriteText(IndentedTextWriter writer)
        {
            BackingData.WriteText(writer);
        }
    }
}