/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.avro.io;

import org.apache.avro.Schema;

import org.apache.avro.SchemaParser;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

import java.io.IOException;
import java.util.stream.Stream;

public class TestResolvingIOResolving {

  @ParameterizedTest
  @MethodSource("data3")
  public void testResolving(TestValidatingIO.Encoding encoding, int skipLevel, String jsonWriterSchema,
      String writerCalls, Object[] writerValues, String jsonReaderSchema, String readerCalls, Object[] readerValues)
      throws IOException {
    Schema writerSchema = SchemaParser.parseSingle(jsonWriterSchema);
    byte[] bytes = TestValidatingIO.make(writerSchema, writerCalls, writerValues, encoding);
    Schema readerSchema = SchemaParser.parseSingle(jsonReaderSchema);
    TestValidatingIO.print(encoding, skipLevel, writerSchema, readerSchema, writerValues, readerValues);
    TestResolvingIO.check(writerSchema, readerSchema, bytes, readerCalls, readerValues, encoding, skipLevel);
  }

  public static Stream<Arguments> data3() {
    return TestValidatingIO.convertTo2dStream(TestResolvingIO.encodings, TestResolvingIO.skipLevels,
        dataForResolvingTests());
  }

  private static Object[][] dataForResolvingTests() {
    // The mnemonics are the same as {@link TestValidatingIO#testSchemas}
    return new Object[][] {
        // Projection
        { "{\"type\":\"record\",\"name\":\"r\",\"fields\":[" + "{\"name\":\"f1\", \"type\":\"string\"},"
            + "{\"name\":\"f2\", \"type\":\"string\"}," + "{\"name\":\"f3\", \"type\":\"int\"}]}", "S10S10IS10S10I",
            new Object[] { "s1", "s2", 100, "t1", "t2", 200 },
            "{\"type\":\"record\",\"name\":\"r\",\"fields\":[" + "{\"name\":\"f1\", \"type\":\"string\" },"
                + "{\"name\":\"f2\", \"type\":\"string\"}]}",
            "RS10S10RS10S10", new Object[] { "s1", "s2", "t1", "t2" } },
        // Reordered fields
        { "{\"type\":\"record\",\"name\":\"r\",\"fields\":["
            + "{\"name\":\"f1\", \"type\":\"int\"}," + "{\"name\":\"f2\", \"type\":\"string\"}]}", "IS10",
            new Object[] { 10, "hello" },
            "{\"type\":\"record\",\"name\":\"r\",\"fields\":[" + "{\"name\":\"f2\", \"type\":\"string\" },"
                + "{\"name\":\"f1\", \"type\":\"long\"}]}",
            "RLS10", new Object[] { 10L, "hello" } },

        // Default values
        { "{\"type\":\"record\",\"name\":\"r\",\"fields\":[]}", "", new Object[] {},
            "{\"type\":\"record\",\"name\":\"r\",\"fields\":["
                + "{\"name\":\"f\", \"type\":\"int\", \"default\": 100}]}",
            "RI", new Object[] { 100 } },
        { "{\"type\":\"record\",\"name\":\"r\",\"fields\":[" + "{\"name\":\"f2\", \"type\":\"int\"}]}", "I",
            new Object[] { 10 },
            "{\"type\":\"record\",\"name\":\"r\",\"fields\":["
                + "{\"name\":\"f1\", \"type\":\"int\", \"default\": 101}," + "{\"name\":\"f2\", \"type\":\"int\"}]}",
            "RII", new Object[] { 10, 101 } },
        { "{\"type\":\"record\",\"name\":\"outer\",\"fields\":[" + "{\"name\": \"g1\", "
            + "\"type\":{\"type\":\"record\",\"name\":\"inner\",\"fields\":["
            + "{\"name\":\"f2\", \"type\":\"int\"}]}}, " + "{\"name\": \"g2\", \"type\": \"long\"}]}", "IL",
            new Object[] { 10, 11L },
            "{\"type\":\"record\",\"name\":\"outer\",\"fields\":[" + "{\"name\": \"g1\", "
                + "\"type\":{\"type\":\"record\",\"name\":\"inner\",\"fields\":["
                + "{\"name\":\"f1\", \"type\":\"int\", \"default\": 101}," + "{\"name\":\"f2\", \"type\":\"int\"}]}}, "
                + "{\"name\": \"g2\", \"type\": \"long\"}]}",
            "RRIIL", new Object[] { 10, 101, 11L } },
        // Default value for a record.
        { "{\"type\":\"record\",\"name\":\"outer\",\"fields\":[" + "{\"name\": \"g2\", \"type\": \"long\"}]}", "L",
            new Object[] { 11L },
            "{\"type\":\"record\",\"name\":\"outer\",\"fields\":[" + "{\"name\": \"g1\", "
                + "\"type\":{\"type\":\"record\",\"name\":\"inner\",\"fields\":["
                + "{\"name\":\"f1\", \"type\":\"int\" }," + "{\"name\":\"f2\", \"type\":\"int\"}] }, "
                + "\"default\": { \"f1\": 10, \"f2\": 101 } }, " + "{\"name\": \"g2\", \"type\": \"long\"}]}",
            "RLRII", new Object[] { 11L, 10, 101 } },
        { "{\"type\":\"record\",\"name\":\"r\",\"fields\":[]}", "", new Object[] {},
            "{\"type\":\"record\",\"name\":\"r\",\"fields\":["
                + "{\"name\":\"f\", \"type\":{ \"type\": \"array\", \"items\": \"int\" }, " + "\"default\": [100]}]}",
            "[c1sI]", new Object[] { 100 } },
        { "{ \"type\": \"array\", \"items\": {\"type\":\"record\"," + "\"name\":\"r\",\"fields\":[]} }", "[c1s]",
            new Object[] {},
            "{ \"type\": \"array\", \"items\": {\"type\":\"record\"," + "\"name\":\"r\",\"fields\":["
                + "{\"name\":\"f\", \"type\":\"int\", \"default\": 100}]} }",
            "[c1sI]", new Object[] { 100 } },
        // Enum resolution
        { "{\"type\":\"enum\",\"name\":\"e\",\"symbols\":[\"x\",\"y\",\"z\"]}", "e2", new Object[] {},
            "{\"type\":\"enum\",\"name\":\"e\",\"symbols\":[ \"y\", \"z\" ]}", "e1", new Object[] {} },
        { "{\"type\":\"enum\",\"name\":\"e\",\"symbols\":[ \"x\", \"y\" ]}", "e1", new Object[] {},
            "{\"type\":\"enum\",\"name\":\"e\",\"symbols\":[ \"y\", \"z\" ]}", "e0", new Object[] {} },

        // Union
        { "\"int\"", "I", new Object[] { 100 }, "[ \"long\", \"int\"]", "U1I", new Object[] { 100 } },
        { "[ \"long\", \"int\"]", "U1I", new Object[] { 100 }, "\"int\"", "I", new Object[] { 100 } },
        // Union + promotion
        { "\"int\"", "I", new Object[] { 100 }, "[ \"long\", \"string\"]", "U0L", new Object[] { 100L } },
        { "[ \"int\", \"string\"]", "U0I", new Object[] { 100 }, "\"long\"", "L", new Object[] { 100L } },
        // Record where union field is skipped.
        { "{\"type\":\"record\",\"name\":\"r\",\"fields\":["
            + "{\"name\":\"f0\", \"type\":\"boolean\"}," + "{\"name\":\"f1\", \"type\":\"int\"},"
            + "{\"name\":\"f2\", \"type\":[\"int\", \"long\"]}," + "{\"name\":\"f3\", \"type\":\"float\"}" + "]}",
            "BIU0IF", new Object[] { true, 100, 121, 10.75f },
            "{\"type\":\"record\",\"name\":\"r\",\"fields\":[" + "{\"name\":\"f0\", \"type\":\"boolean\"},"
                + "{\"name\":\"f1\", \"type\":\"long\"}," + "{\"name\":\"f3\", \"type\":\"double\"}]}",
            "BLD", new Object[] { true, 100L, 10.75d } },
        // Array of record with arrays.
        { "{ \"type\": \"array\", \"items\":" + "{\"type\":\"record\",\"name\":\"r\",\"fields\":["
            + "{\"name\":\"f0\", \"type\":\"boolean\"},"
            + "{\"name\":\"f1\", \"type\": {\"type\":\"array\", \"items\": \"boolean\" }}" + "]}}",
            "[c2sB[c2sBsB]sB[c3sBsBsB]]", new Object[] { true, false, false, false, true, true, true },
            "{ \"type\": \"array\", \"items\":" + "{\"type\":\"record\",\"name\":\"r\",\"fields\":["
                + "{\"name\":\"f0\", \"type\":\"boolean\"}" + "]}}",
            "[c2sBsB]", new Object[] { true, false } }, };
  }
}
