package org.apache.lucene.search;

/*
 * 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.
 */

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;

import org.apache.lucene.document.Document;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.MultiFields;
import org.apache.lucene.index.RandomIndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.index.Terms;
import org.apache.lucene.index.TermsEnum;
import org.apache.lucene.store.Directory;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.LineFileDocs;
import org.apache.lucene.util.LuceneTestCase;
import org.apache.lucene.util.RamUsageEstimator;
import org.apache.lucene.util._TestUtil;

public class TestSameScoresWithThreads extends LuceneTestCase {

  public void test() throws Exception {
    final Directory dir = newDirectory();
    final RandomIndexWriter w = new RandomIndexWriter(random(), dir);
    LineFileDocs docs = new LineFileDocs(random());
    int charsToIndex = atLeast(100000);
    int charsIndexed = 0;
    //System.out.println("bytesToIndex=" + charsToIndex);
    while(charsIndexed < charsToIndex) {
      Document doc = docs.nextDoc();
      charsIndexed += doc.get("body").length();
      w.addDocument(doc);
      //System.out.println("  bytes=" + charsIndexed + " add: " + doc);
    }
    IndexReader r = w.getReader();
    //System.out.println("numDocs=" + r.numDocs());
    w.close();

    final IndexSearcher s = new IndexSearcher(r);
    Terms terms = MultiFields.getFields(r).terms("body");
    int termCount = 0;
    TermsEnum termsEnum = terms.iterator(null);
    while(termsEnum.next() != null) {
      termCount++;
    }
    assertTrue(termCount > 0);
    
    // Target ~10 terms to search:
    double chance = 10.0 / termCount;
    termsEnum = terms.iterator(termsEnum);
    final Map<BytesRef,TopDocs> answers = new HashMap<BytesRef,TopDocs>();
    while(termsEnum.next() != null) {
      if (random().nextDouble() <= chance) {
        BytesRef term = BytesRef.deepCopyOf(termsEnum.term());
        answers.put(term,
                    s.search(new TermQuery(new Term("body", term)), 100));
      }
    }

    if (!answers.isEmpty()) {
      final CountDownLatch startingGun = new CountDownLatch(1);
      int numThreads = _TestUtil.nextInt(random(), 2, 5);
      Thread[] threads = new Thread[numThreads];
      for(int threadID=0;threadID<numThreads;threadID++) {
        Thread thread = new Thread() {
            @Override
            public void run() {
              try {
                startingGun.await();
                for(int i=0;i<20;i++) {
                  List<Map.Entry<BytesRef,TopDocs>> shuffled = new ArrayList<Map.Entry<BytesRef,TopDocs>>(answers.entrySet());
                  Collections.shuffle(shuffled);
                  for(Map.Entry<BytesRef,TopDocs> ent : shuffled) {
                    TopDocs actual = s.search(new TermQuery(new Term("body", ent.getKey())), 100);
                    TopDocs expected = ent.getValue();
                    assertEquals(expected.totalHits, actual.totalHits);
                    assertEquals("query=" + ent.getKey().utf8ToString(), expected.scoreDocs.length, actual.scoreDocs.length);
                    for(int hit=0;hit<expected.scoreDocs.length;hit++) {
                      assertEquals(expected.scoreDocs[hit].doc, actual.scoreDocs[hit].doc);
                      // Floats really should be identical:
                      assertTrue(expected.scoreDocs[hit].score == actual.scoreDocs[hit].score);
                    }
                  }
                }
              } catch (Exception e) {
                throw new RuntimeException(e);
              }
            }
          };
        threads[threadID] = thread;
        thread.start();
      }
      startingGun.countDown();
      for(Thread thread : threads) {
        thread.join();
      }
      r.close();
    }

    dir.close();
  }
}

@@ -4744,24 +4744,28 @@
 ;%0A      %7D%0A  
+  %7D%0A
     r.close(
@@ -4767,23 +4767,16 @@
 lose();%0A
-    %7D%0A%0A
     dir.

fix test bug