private void SerializeValue(string name, Type type, object value, IValueWriter writer,
                                    Type collectionItemType = null, int?index = null)
        {
            var valueInfo = new ValueInfo
            {
                Name  = name,
                Index = index
            };

            if (value != null)
            {
                type = value.GetType();
            }

            var writeValueAsReference = false;

            if (value != null && !type.IsValueType() && type != typeof(string) && type != typeof(Uri))
            {
                var objectId = _idGenerator.GetId(value, out var isFirstTime);
                if (_specialIds.TryGetValue(objectId, out var specialId))
                {
                    valueInfo.SpecialId   = specialId;
                    writeValueAsReference = true;
                }
                else
                {
                    valueInfo.ReferenceId = objectId;
                    writeValueAsReference = !isFirstTime;
                }
            }

            if (value is Type valueAsType)
            {
                valueInfo.Type = _typeSerializerHelper.GetTypeSerializationInfo(valueAsType);
                type           = typeof(Type);
            }

            if (writeValueAsReference)
            {
                // Reset other fields, because they don't need to be written.
                valueInfo.Type      = null;
                valueInfo.ItemType  = null;
                valueInfo.ItemCount = null;

                writer.WriteStartValue(valueInfo);
                writer.WriteEndValue();
            }
            else if (value == null)
            {
                writer.WriteStartValue(valueInfo);
                writer.WriteValue(null);
                writer.WriteEndValue();
            }
            else if (writer.CanWriteValueWithoutTypeInfo(type))
            {
                writer.WriteStartValue(valueInfo);
                writer.WriteValue(value);
                writer.WriteEndValue();
            }
            else if (type.IsEnum())
            {
                writer.WriteStartValue(valueInfo);
                value = Convert.ChangeType(value, type.GetEnumUnderlyingType());
                writer.WriteValue(value);
                writer.WriteEndValue();
            }
#warning Treat other types of collections as arrays
            else if (type.IsArray)
            {
                var items    = (IList)value;
                var itemType = type.GetElementType();

                valueInfo.IsCollection = true;
#warning Write collection type
                //valueInfo.Type =
                valueInfo.ItemType  = itemType == typeof(object) || itemType == collectionItemType ? null : _typeSerializerHelper.GetTypeSerializationInfo(itemType);
                valueInfo.ItemCount = items.Count;

                writer.WriteStartValue(valueInfo);
                var itemIndex = 0;
                foreach (var item in items)
                {
                    SerializeValue(null, item?.GetType(), item, writer, collectionItemType: itemType, index: itemIndex++);
                }
                writer.WriteEndValue();
            }
            else if (value is IValueContainer valueAsContainer)
            {
                writer.WriteStartValue(valueInfo);
                if (valueAsContainer.GetCount() == 0)
                {
                    writer.WriteValue(null);
                }
                else
                {
                    SerializeValueContainer(valueAsContainer, writer);
                }
                writer.WriteEndValue();
            }
            else
            {
                if (!TryDecomposeValue(type, value, out var typeInfo, out var nestedContainer))
                {
                    throw new UnserializableTypeException(type);
                }
                valueInfo.Type = typeInfo;
                writer.WriteStartValue(valueInfo);
                SerializeValueContainer(nestedContainer, writer);
                writer.WriteEndValue();
            }
        }