#if FEATURE_COLLATION
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Icu;
using Icu.Collation;
using Lucene.Net.Analysis;
using Lucene.Net.Analysis.TokenAttributes;
using Lucene.Net.Analysis.Util;
using NUnit.Framework;

namespace Lucene.Net.Collation
{
    /*
     * 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.
     */

    [TestFixture]
	public class TestCollationKeyFilterFactory : BaseTokenStreamFactoryTestCase
	{
		/// <summary>
		/// Turkish has some funny casing.
		/// This test shows how you can solve this kind of thing easily with collation.
		/// Instead of using LowerCaseFilter, use a turkish collator with primary strength.
		/// Then things will sort and match correctly.
		/// </summary>
		public virtual void TestBasicUsage()
		{
			var turkishUpperCase = "I WİLL USE TURKİSH CASING";
			var turkishLowerCase = "ı will use turkish casıng";
			var factory = this.TokenFilterFactory("CollationKey", "language", "tr", "strength", "primary");
			var tsUpper = factory.Create(new MockTokenizer(new StringReader(turkishUpperCase), MockTokenizer.KEYWORD, false));
			var tsLower = factory.Create(new MockTokenizer(new StringReader(turkishLowerCase), MockTokenizer.KEYWORD, false));
			AssertCollatesToSame(tsUpper, tsLower);
		}

		/// <summary>
		/// Test usage of the decomposition option for unicode normalization.
		/// </summary>
		[Test]
		public virtual void TestNormalization()
		{
			var turkishUpperCase = "I W\u0049\u0307LL USE TURKİSH CASING";
			var turkishLowerCase = "ı will use turkish casıng";
			var factory = this.TokenFilterFactory("CollationKey", "language", "tr", "strength", "primary", "decomposition", "canonical");
			var tsUpper = factory.Create(new MockTokenizer(new StringReader(turkishUpperCase), MockTokenizer.KEYWORD, false));
			var tsLower = factory.Create(new MockTokenizer(new StringReader(turkishLowerCase), MockTokenizer.KEYWORD, false));
			AssertCollatesToSame(tsUpper, tsLower);
		}

		/// <summary>
		/// Test usage of the K decomposition option for unicode normalization.
		/// This works even with identical strength.
		/// </summary>
		[Test]
		public virtual void TestFullDecomposition()
		{
			var fullWidth = "Ｔｅｓｔｉｎｇ";
			var halfWidth = "Testing";
			var factory = this.TokenFilterFactory("CollationKey", "language", "zh", "strength", "identical", "decomposition", "full");
			var tsFull = factory.Create(new MockTokenizer(new StringReader(fullWidth), MockTokenizer.KEYWORD, false));
			var tsHalf = factory.Create(new MockTokenizer(new StringReader(halfWidth), MockTokenizer.KEYWORD, false));
			AssertCollatesToSame(tsFull, tsHalf);
		}

		/// <summary>
		/// Test secondary strength, for english case is not significant.
		/// </summary>
		[Test]
		public virtual void TestSecondaryStrength()
		{
			var upperCase = "TESTING";
			var lowerCase = "testing";
			var factory = this.TokenFilterFactory("CollationKey", "language", "en", "strength", "secondary", "decomposition", "no");
			var tsUpper = factory.Create(new MockTokenizer(new StringReader(upperCase), MockTokenizer.KEYWORD, false));
			var tsLower = factory.Create(new MockTokenizer(new StringReader(lowerCase), MockTokenizer.KEYWORD, false));
			AssertCollatesToSame(tsUpper, tsLower);
		}

		/// <summary>
		/// For german, you might want oe to sort and match with o umlaut.
		/// This is not the default, but you can make a customized ruleset to do this.
		///
		/// The default is DIN 5007-1, this shows how to tailor a collator to get DIN 5007-2 behavior.
		///  http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4423383
		/// </summary>
		[Test]
		public virtual void TestCustomRules()
		{
            // It is possible not to have the collator rules for a specific
            // country, fallback to the language if that is the case.
            var possiblelocales = new[] { new Locale("de-DE"), new Locale("de") };
            var allRules = RuleBasedCollator.GetAvailableCollationLocales();
            var localeToUse = possiblelocales.FirstOrDefault(locl => allRules.Contains(locl.Id));

            Assert.True(localeToUse != default(Locale), "Should have found a matching collation locale given the two locales to use.");

            const string DIN5007_2_tailorings = "& ae , a\u0308 & AE , A\u0308" + "& oe , o\u0308 & OE , O\u0308" + "& ue , u\u0308 & UE , u\u0308";
            var collationRules = Collator.GetCollationRules(localeToUse.Id);

            Assert.IsNotNull(collationRules, $"Rules should have been fetched for {localeToUse.Id}");

            string tailoredRules = collationRules + DIN5007_2_tailorings;

            RuleBasedCollator tailoredCollator = new RuleBasedCollator(tailoredRules);

            // at this point, you would save these tailoredRules to a file, 
            // and use the custom parameter.
            string germanUmlaut = "Töne";
            string germanOE = "Toene";
            IDictionary<string, string> args = new Dictionary<string, string>();
            args["custom"] = "rules.txt";
            args["strength"] = "primary";
#pragma warning disable 612, 618
            CollationKeyFilterFactory factory = new CollationKeyFilterFactory(args);
#pragma warning restore 612, 618
            factory.Inform(new StringMockResourceLoader(tailoredRules));
            TokenStream tsUmlaut = factory.Create(new MockTokenizer(new StringReader(germanUmlaut), MockTokenizer.KEYWORD, false));
            TokenStream tsOE = factory.Create(new MockTokenizer(new StringReader(germanOE), MockTokenizer.KEYWORD, false));

            AssertCollatesToSame(tsUmlaut, tsOE);
		}
		
		private static void AssertCollatesToSame(TokenStream stream1, TokenStream stream2)
		{
			stream1.Reset();
			stream2.Reset();
            ICharTermAttribute term1 = stream1.AddAttribute<ICharTermAttribute>();
            ICharTermAttribute term2 = stream2.AddAttribute<ICharTermAttribute>();
			assertTrue(stream1.IncrementToken());
			assertTrue(stream2.IncrementToken());
			assertEquals(term1.ToString(), term2.ToString());
			assertFalse(stream1.IncrementToken());
			assertFalse(stream2.IncrementToken());
			stream1.End();
			stream2.End();
			stream1.Dispose();
			stream2.Dispose();
		}
	}
}
#endif