using System;
using System.IO;
using System.Linq;
using System.Reflection;

public static class BinarySerializationHelper
{
    public static void SerializeObject<T>(T obj, BinaryWriter writer)
    {
        if (obj == null)
        {
            writer.Write(false); // Null flag
            return;
        }

        writer.Write(true); // Not null

        Type type = typeof(T);

        // Serialize all non-indexed properties
        foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
        {
            if (property.CanRead && property.GetIndexParameters().Length == 0) // Skip indexers
            {
                var value = property.GetValue(obj);
                SerializeValue(value, writer);
            }
        }

        // Serialize all public fields
        foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.Instance))
        {
            var value = field.GetValue(obj);
            SerializeValue(value, writer);
        }
    }

    public static T DeserializeObject<T>(BinaryReader reader) where T : new()
    {
        if (!reader.ReadBoolean()) // Null flag
        {
            return default;
        }

        T obj = new T();
        Type type = typeof(T);

        foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
        {
            if (property.CanWrite && property.GetIndexParameters().Length == 0) // Skip indexers
            {
                var value = DeserializeValue(property.PropertyType, reader);
                property.SetValue(obj, value);
            }
        }

        foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.Instance))
        {
            var value = DeserializeValue(field.FieldType, reader);
            field.SetValue(obj, value);
        }

        return obj;
    }

    private static void SerializeValue(object value, BinaryWriter writer)
    {
        if (value == null)
        {
            writer.Write(false); // Null flag for the value
            return;
        }

        writer.Write(true); // Not null!

        Type type = value.GetType();

        if (type == typeof(int))
        {
            writer.Write((int)value);
        }
        else if (type == typeof(string))
        {
            writer.Write((string)value);
        }
        else if (type == typeof(bool))
        {
            writer.Write((bool)value);
        }
        else if (type == typeof(double))
        {
            writer.Write((double)value);
        }
        else if (type == typeof(float))
        {
            writer.Write((float)value);
        }
        else if (type == typeof(long))
        {
            writer.Write((long)value);
        }
        else if (type.IsArray)
        {
            var array = (Array)value;
            writer.Write(array.Length); // Write array length
            foreach (var item in array)
            {
                SerializeValue(item, writer);
            }
        }
        else if (typeof(System.Collections.IList).IsAssignableFrom(type))
        {
            var list = (System.Collections.IList)value;
            writer.Write(list.Count); // Write list count
            foreach (var item in list)
            {
                SerializeValue(item, writer); // Serialize each item
            }
        }
        else
        {
            SerializeObject(value, writer); // Serialize nested objects
        }
    }

    private static object DeserializeValue(Type type, BinaryReader reader)
    {
        if (!reader.ReadBoolean()) // Null flag
        {
            return null;
        }

        if (type == typeof(int))
        {
            return reader.ReadInt32();
        }
        else if (type == typeof(string))
        {
            return reader.ReadString();
        }
        else if (type == typeof(bool))
        {
            return reader.ReadBoolean();
        }
        else if (type == typeof(double))
        {
            return reader.ReadDouble();
        }
        else if (type.IsArray)
        {
            int length = reader.ReadInt32();
            var array = Array.CreateInstance(type.GetElementType(), length);
            for (int i = 0; i < length; i++)
            {
                array.SetValue(DeserializeValue(type.GetElementType(), reader), i);
            }
            return array;
        }
        else if (typeof(System.Collections.IList).IsAssignableFrom(type))
        {
            int count = reader.ReadInt32();
            var list = (System.Collections.IList)Activator.CreateInstance(type);
            for (int i = 0; i < count; i++)
            {
                list.Add(DeserializeValue(type.GenericTypeArguments[0], reader));
            }
            return list;
        }
        else
        {
            var method = typeof(BinarySerializationHelper)
                .GetMethod("DeserializeObject")
                .MakeGenericMethod(type);

            return method.Invoke(null, new object[] { reader });
        }
    }
}