/**
 * 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
 *
 *     http://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.
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using NUnit.Framework;
using Avro;

namespace Avro.Test
{
    [TestFixture]
    public class ProtocolTest
    {
        [TestCase(@"{
  ""protocol"": ""TestProtocol"",
  ""namespace"": ""com.acme"",
  ""doc"": ""HelloWorld"",

  ""types"": [
    {""name"": ""Greeting"", ""type"": ""record"", ""fields"": [
      {""name"": ""message"", ""type"": ""string""}]},
    {""name"": ""Curse"", ""type"": ""error"", ""fields"": [
      {""name"": ""message"", ""type"": ""string""}]},
    {""name"": ""CurseMore"", ""type"": ""error"", ""fields"": [
      {""name"": ""message"", ""type"": ""string""}]}
  ],

  ""messages"": {
    ""hello"": {
      ""request"": [{""name"": ""greeting"", ""type"": ""Greeting"" }],
      ""response"": ""Greeting"",
      ""errors"": [""Curse"", ""CurseMore""]
    }
  }
}", true)]
        [TestCase(@"{
  ""protocol"" : ""MyProtocol"",
  ""namespace"" : ""com.foo"",
  ""types"" : [ 
   {
	""type"" : ""record"",
	""name"" : ""A"",
	""fields"" : [ { ""name"" : ""f1"", ""type"" : ""long"" } ]
   },
   {
	""type"" : ""enum"",
	""name"" : ""MyEnum"",
	""symbols"" : [ ""A"", ""B"", ""C"" ]
   },
   {
   ""type"": ""fixed"", 
   ""size"": 16, 
   ""name"": ""MyFixed""
   },
   {
	""type"" : ""record"",
	""name"" : ""Z"",
	""fields"" : 
			[ 	
				{ ""name"" : ""myUInt"", ""type"" : [ ""int"", ""null"" ] },
				{ ""name"" : ""myULong"", ""type"" : [ ""long"", ""null"" ] },
				{ ""name"" : ""myUBool"", ""type"" : [ ""boolean"", ""null"" ] },
				{ ""name"" : ""myUDouble"", ""type"" : [ ""double"", ""null"" ] },
				{ ""name"" : ""myUFloat"", ""type"" : [ ""float"", ""null"" ] },
				{ ""name"" : ""myUBytes"", ""type"" : [ ""bytes"", ""null"" ] },
				{ ""name"" : ""myUString"", ""type"" : [ ""string"", ""null"" ] },
				
				{ ""name"" : ""myInt"", ""type"" : ""int"" },
				{ ""name"" : ""myLong"", ""type"" : ""long"" },
				{ ""name"" : ""myBool"", ""type"" : ""boolean"" },
				{ ""name"" : ""myDouble"", ""type"" : ""double"" },
				{ ""name"" : ""myFloat"", ""type"" : ""float"" },
				{ ""name"" : ""myBytes"", ""type"" : ""bytes"" },
				{ ""name"" : ""myString"", ""type"" : ""string"" },
				{ ""name"" : ""myNull"", ""type"" : ""null"" },

				{ ""name"" : ""myFixed"", ""type"" : ""MyFixed"" },								
				{ ""name"" : ""myFixed2"", ""type"" : ""MyFixed"" },								
				{ ""name"" : ""myA"", ""type"" : ""A"" },
				{ ""name"" : ""myE"", ""type"" : ""MyEnum"" },
				{ ""name"" : ""myArray"", ""type"" : { ""type"" : ""array"", ""items"" : ""bytes"" } },
				{ ""name"" : ""myArray2"", ""type"" : { ""type"" : ""array"", ""items"" : { ""type"" : ""record"", ""name"" : ""newRec"", ""fields"" : [ { ""name"" : ""f1"", ""type"" : ""long""} ] } } },
				{ ""name"" : ""myMap"", ""type"" : { ""type"" : ""map"", ""values"" : ""string"" } },
				{ ""name"" : ""myMap2"", ""type"" : { ""type"" : ""map"", ""values"" : ""newRec"" } },
				{ ""name"" : ""myObject"", ""type"" : [ ""MyEnum"", ""A"", ""null"" ] },
				{ ""name"" : ""next"", ""type"" : [ ""A"", ""null"" ] }
			]
   } ,
   {
	""type"" : ""int""
   }
   ]
}", true)]
        [TestCase(@"{
  ""protocol"" : ""MyProtocol"",
  ""namespace"" : ""com.bar"",
  ""types"" : [ 
   {
	""type"" : ""record"",
	""name"" : ""A"",
	""fields"" : 
		[ 
			{ ""name"" : ""f1"", ""type"" : ""long"" }
		]
   },
   {
	""type"" : ""enum"",
	""name"" : ""MyEnum"",
	""symbols"" : [ ""A"", ""B"", ""C"" ]
   },
   {
   ""type"": ""fixed"", 
   ""size"": 16, 
   ""name"": ""MyFixed""
   },
   {
	""type"" : ""record"",
	""name"" : ""Z"",
	""fields"" : 
			[ 	
				{ ""name"" : ""myUInt"", ""type"" : [ ""int"", ""null"" ], ""default"" : 1 },
				{ ""name"" : ""myULong"", ""type"" : [ ""long"", ""null"" ], ""default"" : 2 },
				{ ""name"" : ""myUBool"", ""type"" : [ ""boolean"", ""null"" ], ""default"" : true },
				{ ""name"" : ""myUDouble"", ""type"" : [ ""double"", ""null"" ], ""default"" : 3 },
				{ ""name"" : ""myUFloat"", ""type"" : [ ""float"", ""null"" ], ""default"" : 4.5 },
				{ ""name"" : ""myUBytes"", ""type"" : [ ""bytes"", ""null"" ], ""default"" : ""\u00ff"" },
				{ ""name"" : ""myUString"", ""type"" : [ ""string"", ""null"" ], ""default"" : ""foo"" },
				
				{ ""name"" : ""myInt"", ""type"" : ""int"", ""default"" : 10 },
				{ ""name"" : ""myLong"", ""type"" : ""long"", ""default"" : 11 },
				{ ""name"" : ""myBool"", ""type"" : ""boolean"", ""default"" : false },
				{ ""name"" : ""myDouble"", ""type"" : ""double"", ""default"" : 12 },
				{ ""name"" : ""myFloat"", ""type"" : ""float"", ""default"" : 13.14 },
				{ ""name"" : ""myBytes"", ""type"" : ""bytes"", ""default"" : ""\u00ff"" },
				{ ""name"" : ""myString"", ""type"" : ""string"", ""default"" : ""bar"" },
				{ ""name"" : ""myNull"", ""type"" : ""null"", ""default"" : null },

				{ ""name"" : ""myFixed"", ""type"" : ""MyFixed"", ""default"" : ""\u00FFFFFFFFFFFFFFFFA"" },
				{ ""name"" : ""myFixed2"", ""type"" : ""MyFixed"", ""default"" : ""\u00FFFFFFFFFFFFFFFFA"" },
				{ ""name"" : ""myA"", ""type"" : ""A"", ""default"" : {""f1"":5} },
				{ ""name"" : ""myE"", ""type"" : ""MyEnum"", ""default"" : ""C"" },
				{ ""name"" : ""myArray"", ""type"" : { ""type"" : ""array"", ""items"" : ""bytes"" }, ""default"" : [ ""a12b"", ""cc50"" ] },
				{ ""name"" : ""myArray2"", ""type"" : { ""type"" : ""array"", ""items"" : { ""type"" : ""record"", ""name"" : ""newRec"", ""fields"" : [ { ""name"" : ""f2"", ""type"" : ""long""} ], ""default"" : {""f2"":5} } }, ""default"" : [ {""f2"":6}, {""f2"":7} ] },
				{ ""name"" : ""myMap"", ""type"" : { ""type"" : ""map"", ""values"" : ""string"" }, ""default"" : {""a"":""A"", ""b"":""B""} },
				{ ""name"" : ""myMap2"", ""type"" : { ""type"" : ""map"", ""values"" : ""newRec"" }, ""default"" : { ""key1"":{""f2"":6}, ""key2"":{""f2"":7} } },
				{ ""name"" : ""myObject"", ""type"" : [ ""MyEnum"", ""A"", ""null"" ], ""default"" : ""A"" },
				{ ""name"" : ""next"", ""type"" : [ ""null"" , ""A"" ], ""default"" : null }
			]
   } ,
   {
	""type"" : ""int""
   }
   ]
}", true)]
        public static void TestProtocol(string str, bool valid)
        {
            Protocol protocol = Protocol.Parse(str);
            Assert.IsTrue(valid);
            string json = protocol.ToString();

            Protocol protocol2 = Protocol.Parse(json);
            string json2 = protocol2.ToString();

            Assert.AreEqual(json,json2);
        }

        // Protocols match
        [TestCase(
@"{
  ""protocol"": ""TestProtocol"",
  ""namespace"": ""com.acme"",

  ""types"": [
    {""name"": ""Greeting"", ""type"": ""record"", ""fields"": [
      {""name"": ""message"", ""type"": ""string""}]},
    {""name"": ""Curse"", ""type"": ""error"", ""fields"": [
      {""name"": ""message"", ""type"": ""string""}]}
  ],

  ""messages"": {
    ""hello"": {
      ""request"": [{""name"": ""greeting"", ""type"": ""Greeting"" }],
      ""response"": ""Greeting"",
      ""errors"": [""Curse""]
    }
  }
}", 
@"{
  ""protocol"": ""TestProtocol"",
  ""namespace"": ""com.acme"",

  ""types"": [
    {""name"": ""Greeting"", ""type"": ""record"", ""fields"": [
      {""name"": ""message"", ""type"": ""string""}]},
    {""name"": ""Curse"", ""type"": ""error"", ""fields"": [
      {""name"": ""message"", ""type"": ""string""}]}
  ],

  ""messages"": {
    ""hello"": {
      ""request"": [{""name"": ""greeting"", ""type"": ""Greeting"" }],
      ""response"": ""Greeting"",
      ""errors"": [""Curse""]
    }
  }
}",
  true,true)]
        // Protocols match, order of schemas in 'types' are different
        [TestCase(
@"{
  ""protocol"": ""TestProtocol"",
  ""namespace"": ""com.acme"",

  ""types"": [
    {""name"": ""Curse"", ""type"": ""error"", ""fields"": [
      {""name"": ""message"", ""type"": ""string""}]},
    {""name"": ""Greeting"", ""type"": ""record"", ""fields"": [
      {""name"": ""message"", ""type"": ""string""}]}
  ],

  ""messages"": {
    ""hello"": {
      ""request"": [{""name"": ""greeting"", ""type"": ""Greeting"" }],
      ""response"": ""Greeting"",
      ""errors"": [""Curse""]
    }
  }
}",
@"{
  ""protocol"": ""TestProtocol"",
  ""namespace"": ""com.acme"",

  ""types"": [
    {""name"": ""Greeting"", ""type"": ""record"", ""fields"": [
      {""name"": ""message"", ""type"": ""string""}]},
    {""name"": ""Curse"", ""type"": ""error"", ""fields"": [
      {""name"": ""message"", ""type"": ""string""}]}
  ],

  ""messages"": {
    ""hello"": {
      ""request"": [{""name"": ""greeting"", ""type"": ""Greeting"" }],
      ""response"": ""Greeting"",
      ""errors"": [""Curse""]
    }
  }
}",
  false,true)]
        // Name of protocol is different
        [TestCase(
@"{
  ""protocol"": ""TestProtocol1"",
  ""namespace"": ""com.acme"",

  ""types"": [
    {""name"": ""Greeting"", ""type"": ""record"", ""fields"": [
      {""name"": ""message"", ""type"": ""string""}]},
    {""name"": ""Curse"", ""type"": ""error"", ""fields"": [
      {""name"": ""message"", ""type"": ""string""}]}
  ],

  ""messages"": {
    ""hello"": {
      ""request"": [{""name"": ""greeting"", ""type"": ""Greeting"" }],
      ""response"": ""Greeting"",
      ""errors"": [""Curse""]
    }
  }
}",
@"{
  ""protocol"": ""TestProtocol"",
  ""namespace"": ""com.acme"",

  ""types"": [
    {""name"": ""Greeting"", ""type"": ""record"", ""fields"": [
      {""name"": ""message"", ""type"": ""string""}]},
    {""name"": ""Curse"", ""type"": ""error"", ""fields"": [
      {""name"": ""message"", ""type"": ""string""}]}
  ],

  ""messages"": {
    ""hello"": {
      ""request"": [{""name"": ""greeting"", ""type"": ""Greeting"" }],
      ""response"": ""Greeting"",
      ""errors"": [""Curse""]
    }
  }
}",
  false,false)]
        // Name of a message request is different: 'hi'
        [TestCase(
@"{
  ""protocol"": ""TestProtocol"",
  ""namespace"": ""com.acme"",

  ""types"": [
    {""name"": ""Greeting"", ""type"": ""record"", ""fields"": [
      {""name"": ""message"", ""type"": ""string""}]},
    {""name"": ""Curse"", ""type"": ""error"", ""fields"": [
      {""name"": ""message"", ""type"": ""string""}]}
  ],

  ""messages"": {
    ""hello"": {
      ""request"": [{""name"": ""greeting"", ""type"": ""Greeting"" }],
      ""response"": ""Greeting"",
      ""errors"": [""Curse""]
    }
  }
}",
@"{
  ""protocol"": ""TestProtocol"",
  ""namespace"": ""com.acme"",

  ""types"": [
    {""name"": ""Greeting"", ""type"": ""record"", ""fields"": [
      {""name"": ""message"", ""type"": ""string""}]},
    {""name"": ""Curse"", ""type"": ""error"", ""fields"": [
      {""name"": ""message"", ""type"": ""string""}]}
  ],

  ""messages"": {
    ""hi"": {
      ""request"": [{""name"": ""greeting"", ""type"": ""Greeting"" }],
      ""response"": ""Greeting"",
      ""errors"": [""Curse""]
    }
  }
}",
  false,false)]
        // Name of a type is different : Curse1
        [TestCase(
@"{
  ""protocol"": ""TestProtocol"",
  ""namespace"": ""com.acme"",

  ""types"": [
    {""name"": ""Greeting"", ""type"": ""record"", ""fields"": [
      {""name"": ""message"", ""type"": ""string""}]},
    {""name"": ""Curse1"", ""type"": ""error"", ""fields"": [
      {""name"": ""message"", ""type"": ""string""}]}
  ],

  ""messages"": {
    ""hello"": {
      ""request"": [{""name"": ""greeting"", ""type"": ""Greeting"" }],
      ""response"": ""Greeting"",
      ""errors"": [""Curse1""]
    }
  }
}",
@"{
  ""protocol"": ""TestProtocol"",
  ""namespace"": ""com.acme"",

  ""types"": [
    {""name"": ""Greeting"", ""type"": ""record"", ""fields"": [
      {""name"": ""message"", ""type"": ""string""}]},
    {""name"": ""Curse"", ""type"": ""error"", ""fields"": [
      {""name"": ""message"", ""type"": ""string""}]}
  ],

  ""messages"": {
    ""hi"": {
      ""request"": [{""name"": ""greeting"", ""type"": ""Greeting"" }],
      ""response"": ""Greeting"",
      ""errors"": [""Curse""]
    }
  }
}",
  false,false)]
        // Name of a record field is different: 'mymessage'
        [TestCase(
@"{
  ""protocol"": ""TestProtocol"",
  ""namespace"": ""com.acme"",

  ""types"": [
    {""name"": ""Greeting"", ""type"": ""record"", ""fields"": [
      {""name"": ""message"", ""type"": ""string""}]},
    {""name"": ""Curse"", ""type"": ""error"", ""fields"": [
      {""name"": ""message"", ""type"": ""string""}]}
  ],

  ""messages"": {
    ""hello"": {
      ""request"": [{""name"": ""greeting"", ""type"": ""Greeting"" }],
      ""response"": ""Greeting"",
      ""errors"": [""Curse""]
    }
  }
}",
@"{
  ""protocol"": ""TestProtocol"",
  ""namespace"": ""com.acme"",

  ""types"": [
    {""name"": ""Greeting"", ""type"": ""record"", ""fields"": [
      {""name"": ""message"", ""type"": ""string""}]},
    {""name"": ""Curse"", ""type"": ""error"", ""fields"": [
      {""name"": ""mymessage"", ""type"": ""string""}]}
  ],

  ""messages"": {
    ""hi"": {
      ""request"": [{""name"": ""greeting"", ""type"": ""Greeting"" }],
      ""response"": ""Greeting"",
      ""errors"": [""Curse""]
    }
  }
}",
  false,false)]
        public static void TestProtocolHash(string str1, string str2, bool md5_equal, bool hash_equal)
        {
            Protocol protocol1 = Protocol.Parse(str1);
            Protocol protocol2 = Protocol.Parse(str2);

            byte[] md51 = protocol1.MD5;
            byte[] md52 = protocol2.MD5;

            int hash1 = protocol1.GetHashCode();
            int hash2 = protocol2.GetHashCode();

            Assert.AreEqual(md5_equal, md51.SequenceEqual(md52));
            Assert.AreEqual(hash_equal, hash1 == hash2);
        }
    }
}
