using Lucene.Net.Index;
using Lucene.Net.Search;
using NUnit.Framework;
using System;
using Console = Lucene.Net.Util.SystemConsole;

namespace Lucene.Net.QueryParsers.Surround.Query
{
    /*
     * 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.
     */

    public class BooleanQueryTst
    {
        private string queryText;
        private readonly int[] expectedDocNrs;
        private SingleFieldTestDb dBase;
        private string fieldName;
        private BasicQueryFactory qf;
        private bool verbose = true;

        public BooleanQueryTst(
            string queryText,
            int[] expectedDocNrs,
            SingleFieldTestDb dBase,
            string fieldName,
            BasicQueryFactory qf)
        {
            this.queryText = queryText;
            this.expectedDocNrs = expectedDocNrs;
            this.dBase = dBase;
            this.fieldName = fieldName;
            this.qf = qf;
        }

        public virtual bool Verbose { set => this.verbose = value; }

        public virtual string QueryText => this.queryText;

        public virtual int[] ExpectedDocNrs => this.expectedDocNrs;

        internal class TestCollector : ICollector
        { // FIXME: use check hits from Lucene tests
            private int totalMatched;
            private bool[] encountered;
            private Scorer scorer = null;
            private int docBase = 0;
            private BooleanQueryTst parent;

            public TestCollector(BooleanQueryTst parent)
            {
                totalMatched = 0;
                encountered = new bool[parent.expectedDocNrs.Length];
                this.parent = parent;
            }

            public virtual void SetScorer(Scorer scorer)
            {
                this.scorer = scorer;
            }

            public virtual bool AcceptsDocsOutOfOrder => true;

            public virtual void SetNextReader(AtomicReaderContext context)
            {
                docBase = context.DocBase;
            }

            public virtual void Collect(int docNr)
            {
                float score = scorer.GetScore();
                docNr += docBase;
                /* System.out.println(docNr + " '" + dBase.getDocs()[docNr] + "': " + score); */
                Assert.True(score > 0.0, parent.QueryText + ": positive score");
                Assert.True(totalMatched < parent.ExpectedDocNrs.Length, parent.QueryText + ": too many hits");
                int i;
                for (i = 0; i < parent.expectedDocNrs.Length; i++)
                {
                    if ((!encountered[i]) && (parent.ExpectedDocNrs[i] == docNr))
                    {
                        encountered[i] = true;
                        break;
                    }
                }
                if (i == parent.ExpectedDocNrs.Length)
                {
                    Assert.True(false, parent.QueryText + ": doc nr for hit not expected: " + docNr);
                }
                totalMatched++;
            }

            public void CheckNrHits()
            {
                Assert.AreEqual(parent.ExpectedDocNrs.Length, totalMatched, parent.QueryText + ": nr of hits");
            }
        }

        public void DoTest()
        {

            if (verbose)
            {
                Console.WriteLine("");
                Console.WriteLine("Query: " + queryText);
            }

            SrndQuery lq = Parser.QueryParser.Parse(queryText);

            /* if (verbose) System.out.println("Srnd: " + lq.toString()); */

            Search.Query query = lq.MakeLuceneQueryField(fieldName, qf);
            /* if (verbose) System.out.println("Lucene: " + query.toString()); */

            TestCollector tc = new TestCollector(this);
            using (IndexReader reader = DirectoryReader.Open(dBase.Db))
            {
                IndexSearcher searcher = new IndexSearcher(reader);

                searcher.Search(query, tc);
            }
            tc.CheckNrHits();
        }
    }
}
