﻿using System;
using System.IO;

namespace UniGLTF.JsonSchema.Schemas
{
    public class ArrayJsonSchema : JsonSchemaBase
    {
        public readonly JsonSchemaBase Items;
        public readonly bool UniqueItems;
        public readonly int MinItems;
        public readonly int? MaxItems;

        public ArrayJsonSchema(in JsonSchemaSource source, bool useUpperCamelName) : base(source)
        {
            Items = source.items.Create(useUpperCamelName);
            Items.IsArrayItem = true;
            UniqueItems = source.uniqueItems.GetValueOrDefault();
            MinItems = source.minItems.GetValueOrDefault();
            if (source.maxItems.HasValue)
            {
                MaxItems = source.maxItems.Value;
            }
        }

        public override bool IsInline => false;

        bool ItemsIsPrimitiveType => Items is PrimitiveJsonSchemaBase;

        public override string ValueType
        {
            get
            {
                if (ItemsIsPrimitiveType)
                {
                    return $"{Items.ValueType}[]";
                }
                else
                {
                    return $"List<{Items.ValueType}>";
                }
            }
        }

        public override string GenerateDeserializerCall(string callName, string argName)
        {
            return $"{callName}({argName})";
        }

        public override void GenerateDeserializer(TraverseContext writer, string callName)
        {
            if (writer.Used.Contains(callName))
            {
                return;
            }
            writer.Used.Add(callName);

            var itemCallName = callName + "_ITEM";

            if (ItemsIsPrimitiveType)
            {

                writer.Write(@"
public static $0 $2(JsonNode parsed)
{
    var value = new $1[parsed.GetArrayCount()];
    int i=0;
    foreach(var x in parsed.ArrayItems())
    {
        value[i++] = $3;
    }
	return value;
} 
"
    .Replace("$0", ValueType)
    .Replace("$1", Items.ValueType)
    .Replace("$2", callName)
    .Replace("$3", Items.GenerateDeserializerCall(itemCallName, "x"))
    );

            }
            else
            {
                writer.Write(@"
public static $0 $2(JsonNode parsed)
{
    var value = new $1();
    foreach(var x in parsed.ArrayItems())
    {
        value.Add($3);
    }
	return value;
} 
"
    .Replace("$0", ValueType)
    .Replace("$1", ValueType)
    .Replace("$2", callName)
    .Replace("$3", Items.GenerateDeserializerCall(itemCallName, "x"))
    );

            }

            if (!Items.IsInline)
            {
                Items.GenerateDeserializer(writer, itemCallName);
            }
        }

        public override string CreateSerializationCondition(string argName)
        {
            return $"{argName}!=null&&{argName}.Count()>={MinItems}";
        }

        public override string GenerateSerializerCall(string callName, string argName)
        {
            return $"{callName}(f, {argName})";
        }

        public override void GenerateSerializer(TraverseContext writer, string callName)
        {
            if (writer.Used.Contains(callName))
            {
                return;
            }
            writer.Used.Add(callName);

            var itemCallName = callName + "_ITEM";
            writer.Write($@"
public static void {callName}(JsonFormatter f, {ValueType} value)
{{
    f.BeginList();

    foreach(var item in value)
    {{
    "
);

            writer.Write($"{Items.GenerateSerializerCall(itemCallName, "item")};\n");

            writer.Write(@"
    }
    f.EndList();
}
");

            if (!Items.IsInline)
            {
                Items.GenerateSerializer(writer, itemCallName);
            }
        }
    }
}
