using System.Collections.Generic;
using NUnit.Framework;

namespace Lucene.Net.Facet
{

    /*
     * 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 MockAnalyzer = Lucene.Net.Analysis.MockAnalyzer;
    using MockTokenizer = Lucene.Net.Analysis.MockTokenizer;
    using Document = Lucene.Net.Documents.Document;
    using Field = Lucene.Net.Documents.Field;
    using TextField = Lucene.Net.Documents.TextField;
    using TaxonomyReader = Lucene.Net.Facet.Taxonomy.TaxonomyReader;
    using ITaxonomyWriter = Lucene.Net.Facet.Taxonomy.ITaxonomyWriter;
    using DirectoryTaxonomyReader = Lucene.Net.Facet.Taxonomy.Directory.DirectoryTaxonomyReader;
    using DirectoryTaxonomyWriter = Lucene.Net.Facet.Taxonomy.Directory.DirectoryTaxonomyWriter;
    using AtomicReader = Lucene.Net.Index.AtomicReader;
    using AtomicReaderContext = Lucene.Net.Index.AtomicReaderContext;
    using IndexReader = Lucene.Net.Index.IndexReader;
    using OpenMode = Lucene.Net.Index.OpenMode;
    using RandomIndexWriter = Lucene.Net.Index.RandomIndexWriter;
    using IndexSearcher = Lucene.Net.Search.IndexSearcher;
    using MatchAllDocsQuery = Lucene.Net.Search.MatchAllDocsQuery;
    using Directory = Lucene.Net.Store.Directory;
    using IOUtils = Lucene.Net.Util.IOUtils;

    public class TestMultipleIndexFields : FacetTestCase
    {

        private static readonly FacetField[] CATEGORIES = new FacetField[] { new FacetField("Author", "Mark Twain"), new FacetField("Author", "Stephen King"), new FacetField("Author", "Kurt Vonnegut"), new FacetField("Band", "Rock & Pop", "The Beatles"), new FacetField("Band", "Punk", "The Ramones"), new FacetField("Band", "Rock & Pop", "U2"), new FacetField("Band", "Rock & Pop", "REM"), new FacetField("Band", "Rock & Pop", "Dave Matthews Band"), new FacetField("Composer", "Bach") };

        private FacetsConfig Config
        {
            get
            {
                FacetsConfig config = new FacetsConfig();
                config.SetHierarchical("Band", true);
                return config;
            }
        }

        [Test]
        public virtual void TestDefault()
        {
            Directory indexDir = NewDirectory();
            Directory taxoDir = NewDirectory();

            // create and open an index writer
            var iw = new RandomIndexWriter(Random(), indexDir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random(), MockTokenizer.WHITESPACE, false)));
            // create and open a taxonomy writer
            var tw = new DirectoryTaxonomyWriter(taxoDir, OpenMode.CREATE);
            var config = Config;

            seedIndex(tw, iw, config);

            IndexReader ir = iw.Reader;
            tw.Commit();

            // prepare index reader and taxonomy.
            var tr = new DirectoryTaxonomyReader(taxoDir);

            // prepare searcher to search against
            IndexSearcher searcher = NewSearcher(ir);

            FacetsCollector sfc = PerformSearch(tr, ir, searcher);

            // Obtain facets results and hand-test them
            AssertCorrectResults(GetTaxonomyFacetCounts(tr, config, sfc));

            assertOrdinalsExist("$facets", ir);

            IOUtils.Dispose(tr, ir, iw, tw, indexDir, taxoDir);
        }

        [Test]
        public virtual void TestCustom()
        {
            Directory indexDir = NewDirectory();
            Directory taxoDir = NewDirectory();

            // create and open an index writer
            RandomIndexWriter iw = new RandomIndexWriter(Random(), indexDir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random(), MockTokenizer.WHITESPACE, false)));
            // create and open a taxonomy writer
            var tw = new DirectoryTaxonomyWriter(taxoDir, OpenMode.CREATE);

            FacetsConfig config = Config;
            config.SetIndexFieldName("Author", "$author");
            seedIndex(tw, iw, config);

            IndexReader ir = iw.Reader;
            tw.Commit();

            // prepare index reader and taxonomy.
            var tr = new DirectoryTaxonomyReader(taxoDir);

            // prepare searcher to search against
            IndexSearcher searcher = NewSearcher(ir);

            FacetsCollector sfc = PerformSearch(tr, ir, searcher);

            IDictionary<string, Facets> facetsMap = new Dictionary<string, Facets>();
            facetsMap["Author"] = GetTaxonomyFacetCounts(tr, config, sfc, "$author");
            Facets facets = new MultiFacets(facetsMap, GetTaxonomyFacetCounts(tr, config, sfc));

            // Obtain facets results and hand-test them
            AssertCorrectResults(facets);

            assertOrdinalsExist("$facets", ir);
            assertOrdinalsExist("$author", ir);

            IOUtils.Dispose(tr, ir, iw, tw, indexDir, taxoDir);
        }

        [Test]
        public virtual void TestTwoCustomsSameField()
        {
            Directory indexDir = NewDirectory();
            Directory taxoDir = NewDirectory();

            // create and open an index writer
            RandomIndexWriter iw = new RandomIndexWriter(Random(), indexDir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random(), MockTokenizer.WHITESPACE, false)));
            // create and open a taxonomy writer
            var tw = new DirectoryTaxonomyWriter(taxoDir, OpenMode.CREATE);

            FacetsConfig config = Config;
            config.SetIndexFieldName("Band", "$music");
            config.SetIndexFieldName("Composer", "$music");
            seedIndex(tw, iw, config);

            IndexReader ir = iw.Reader;
            tw.Commit();

            // prepare index reader and taxonomy.
            var tr = new DirectoryTaxonomyReader(taxoDir);

            // prepare searcher to search against
            IndexSearcher searcher = NewSearcher(ir);

            FacetsCollector sfc = PerformSearch(tr, ir, searcher);

            IDictionary<string, Facets> facetsMap = new Dictionary<string, Facets>();
            Facets facets2 = GetTaxonomyFacetCounts(tr, config, sfc, "$music");
            facetsMap["Band"] = facets2;
            facetsMap["Composer"] = facets2;
            Facets facets = new MultiFacets(facetsMap, GetTaxonomyFacetCounts(tr, config, sfc));

            // Obtain facets results and hand-test them
            AssertCorrectResults(facets);

            assertOrdinalsExist("$facets", ir);
            assertOrdinalsExist("$music", ir);
            assertOrdinalsExist("$music", ir);

            IOUtils.Dispose(tr, ir, iw, tw, indexDir, taxoDir);
        }

        private void assertOrdinalsExist(string field, IndexReader ir)
        {
            foreach (AtomicReaderContext context in ir.Leaves)
            {
                AtomicReader r = context.AtomicReader;
                if (r.GetBinaryDocValues(field) != null)
                {
                    return; // not all segments must have this DocValues
                }
            }
            Fail("no ordinals found for " + field);
        }

        [Test]
        public virtual void TestDifferentFieldsAndText()
        {
            Directory indexDir = NewDirectory();
            Directory taxoDir = NewDirectory();

            // create and open an index writer
            var iw = new RandomIndexWriter(Random(), indexDir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random(), MockTokenizer.WHITESPACE, false)));
            // create and open a taxonomy writer
            var tw = new DirectoryTaxonomyWriter(taxoDir, OpenMode.CREATE);

            FacetsConfig config = Config;
            config.SetIndexFieldName("Band", "$bands");
            config.SetIndexFieldName("Composer", "$composers");
            seedIndex(tw, iw, config);

            IndexReader ir = iw.Reader;
            tw.Commit();

            // prepare index reader and taxonomy.
            var tr = new DirectoryTaxonomyReader(taxoDir);

            // prepare searcher to search against
            IndexSearcher searcher = NewSearcher(ir);

            FacetsCollector sfc = PerformSearch(tr, ir, searcher);

            IDictionary<string, Facets> facetsMap = new Dictionary<string, Facets>();
            facetsMap["Band"] = GetTaxonomyFacetCounts(tr, config, sfc, "$bands");
            facetsMap["Composer"] = GetTaxonomyFacetCounts(tr, config, sfc, "$composers");
            Facets facets = new MultiFacets(facetsMap, GetTaxonomyFacetCounts(tr, config, sfc));

            // Obtain facets results and hand-test them
            AssertCorrectResults(facets);
            assertOrdinalsExist("$facets", ir);
            assertOrdinalsExist("$bands", ir);
            assertOrdinalsExist("$composers", ir);

            IOUtils.Dispose(tr, ir, iw, tw, indexDir, taxoDir);
        }

        [Test]
        public virtual void TestSomeSameSomeDifferent()
        {
            Directory indexDir = NewDirectory();
            Directory taxoDir = NewDirectory();

            // create and open an index writer
            RandomIndexWriter iw = new RandomIndexWriter(Random(), indexDir, NewIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(Random(), MockTokenizer.WHITESPACE, false)));
            // create and open a taxonomy writer
            ITaxonomyWriter tw = new DirectoryTaxonomyWriter(taxoDir, OpenMode.CREATE);

            FacetsConfig config = Config;
            config.SetIndexFieldName("Band", "$music");
            config.SetIndexFieldName("Composer", "$music");
            config.SetIndexFieldName("Author", "$literature");
            seedIndex(tw, iw, config);

            IndexReader ir = iw.Reader;
            tw.Commit();

            // prepare index reader and taxonomy.
            var tr = new DirectoryTaxonomyReader(taxoDir);

            // prepare searcher to search against
            IndexSearcher searcher = NewSearcher(ir);

            FacetsCollector sfc = PerformSearch(tr, ir, searcher);

            IDictionary<string, Facets> facetsMap = new Dictionary<string, Facets>();
            Facets facets2 = GetTaxonomyFacetCounts(tr, config, sfc, "$music");
            facetsMap["Band"] = facets2;
            facetsMap["Composer"] = facets2;
            facetsMap["Author"] = GetTaxonomyFacetCounts(tr, config, sfc, "$literature");
            Facets facets = new MultiFacets(facetsMap, GetTaxonomyFacetCounts(tr, config, sfc));

            // Obtain facets results and hand-test them
            AssertCorrectResults(facets);
            assertOrdinalsExist("$music", ir);
            assertOrdinalsExist("$literature", ir);

            IOUtils.Dispose(tr, ir, iw, tw);
            IOUtils.Dispose(indexDir, taxoDir);
        }

        
        private void AssertCorrectResults(Facets facets)
        {
            Assert.AreEqual(5, facets.GetSpecificValue("Band"));
            Assert.AreEqual("dim=Band path=[] value=5 childCount=2\n  Rock & Pop (4)\n  Punk (1)\n", facets.GetTopChildren(10, "Band").ToString());
            Assert.AreEqual("dim=Band path=[Rock & Pop] value=4 childCount=4\n  The Beatles (1)\n  U2 (1)\n  REM (1)\n  Dave Matthews Band (1)\n", facets.GetTopChildren(10, "Band", "Rock & Pop").ToString());
            Assert.AreEqual("dim=Author path=[] value=3 childCount=3\n  Mark Twain (1)\n  Stephen King (1)\n  Kurt Vonnegut (1)\n", facets.GetTopChildren(10, "Author").ToString());
        }

        
        private static FacetsCollector PerformSearch(TaxonomyReader tr, IndexReader ir, IndexSearcher searcher)
        {
            FacetsCollector fc = new FacetsCollector();
            FacetsCollector.Search(searcher, new MatchAllDocsQuery(), 10, fc);
            return fc;
        }

        private static void seedIndex(ITaxonomyWriter tw, RandomIndexWriter iw, FacetsConfig config)
        {
            foreach (FacetField ff in CATEGORIES)
            {
                Document doc = new Document();
                doc.Add(ff);
                doc.Add(new TextField("content", "alpha", Field.Store.YES));
                iw.AddDocument(config.Build(tw, doc));
            }
        }
    }
}