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

package org.apache.solr.analytics.accumulator.facet;

import java.io.IOException;

import org.apache.lucene.index.AtomicReaderContext;
import org.apache.lucene.index.NumericDocValues;
import org.apache.lucene.index.SortedDocValues;
import org.apache.lucene.index.SortedSetDocValues;
import org.apache.lucene.util.Bits;
import org.apache.lucene.util.BytesRef;
import org.apache.solr.analytics.accumulator.FacetingAccumulator;
import org.apache.solr.analytics.accumulator.ValueAccumulator;
import org.apache.solr.analytics.util.AnalyticsParsers;
import org.apache.solr.analytics.util.AnalyticsParsers.NumericParser;
import org.apache.solr.analytics.util.AnalyticsParsers.Parser;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrException.ErrorCode;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.schema.SchemaField;
import org.apache.solr.schema.TrieDateField;
import org.apache.solr.search.SolrIndexSearcher;

/**
 * An Accumulator that manages the faceting for fieldFacets.
 * Collects the field facet values.
 */
public class FieldFacetAccumulator extends ValueAccumulator {
  protected final Parser parser;
  protected final FacetValueAccumulator parent;
  protected final String name;
  protected final SolrIndexSearcher searcher;
  protected final SchemaField schemaField;
  protected final boolean multiValued;
  protected final boolean numField;
  protected final boolean dateField;
  protected final BytesRef value;
  protected SortedSetDocValues setValues;
  protected SortedDocValues sortValues; 
  protected NumericDocValues numValues; 
  protected Bits numValuesBits; 
  
  public FieldFacetAccumulator(SolrIndexSearcher searcher, FacetValueAccumulator parent, SchemaField schemaField) throws IOException {  
    if( !schemaField.hasDocValues() ){
      throw new SolrException(ErrorCode.BAD_REQUEST, "Field '"+schemaField.getName()+"' does not have docValues");
    }
    this.searcher = searcher;
    this.schemaField = schemaField;
    this.name = schemaField.getName();
    if (!schemaField.hasDocValues()) {
      throw new IOException(name+" does not have docValues and therefore cannot be faceted over.");
    }
    this.multiValued = schemaField.multiValued();
    this.numField = schemaField.getType().getNumericType()!=null;
    this.dateField = schemaField.getType().getClass().equals(TrieDateField.class);
    this.parent = parent;  
    this.value = new BytesRef();
    this.parser = AnalyticsParsers.getParser(schemaField.getType().getClass());
  }

  public static FieldFacetAccumulator create(SolrIndexSearcher searcher, FacetValueAccumulator parent, SchemaField facetField) throws IOException{
    return new FieldFacetAccumulator(searcher,parent,facetField);
  }

  /**
   * Move to the next set of documents to add to the field facet.
   */
  @Override
  public void setNextReader(AtomicReaderContext context) throws IOException { 
    if (multiValued) {
      setValues = context.reader().getSortedSetDocValues(name);
    } else {
      if (numField) {
        numValues = context.reader().getNumericDocValues(name);
        numValuesBits = context.reader().getDocsWithField(name);
      } else {
        sortValues = context.reader().getSortedDocValues(name);
      }
    }
  }

  /**
   * Tell the FacetingAccumulator to collect the doc with the 
   * given fieldFacet and value(s).
   */
  @Override
  public void collect(int doc) throws IOException {
    if (multiValued) {
      boolean exists = false;
      if (setValues!=null) {
        setValues.setDocument(doc);
        int term;
        while ((term = (int)setValues.nextOrd()) != SortedSetDocValues.NO_MORE_ORDS) {
          exists = true;
          setValues.lookupOrd(term, value);
          parent.collectField(doc, name, parser.parse(value) );
        }
      }
      if (!exists) {
        parent.collectField(doc, name, FacetingAccumulator.MISSING_VALUE );
      }
    } else {
      if(numField){
        long v = numValues.get(doc);
        if( v != 0 || numValuesBits.get(doc) ){
          parent.collectField(doc, name, ((NumericParser)parser).parseNum(numValues.get(doc)));
        } else {
          parent.collectField(doc, name, FacetingAccumulator.MISSING_VALUE );
        }
      } else {
        sortValues.get(doc,value);
        if( BytesRef.EMPTY_BYTES == value.bytes ){
          parent.collectField(doc, name, FacetingAccumulator.MISSING_VALUE );
        } else {
          parent.collectField(doc, name, parser.parse(value) );
        }
      }
    }
  }

  @Override
  public void compute() {}
 
  @Override
  public NamedList<?> export() { return null; }

}

@@ -4746,16 +4746,50 @@
 Field)%7B%0A
+        if(numValues != null) %7B%0A  
         
@@ -4809,32 +4809,34 @@
 alues.get(doc);%0A
+  
         if( v !=
@@ -4863,24 +4863,26 @@
 get(doc) )%7B%0A
+  
           pa
@@ -4947,29 +4947,123 @@
 Num(
-numValues.get(doc)));
+v));%0A          %7D else %7B%0A            parent.collectField(doc, name, FacetingAccumulator.MISSING_VALUE );%0A          %7D
 %0A   
@@ -5171,32 +5171,67 @@
 %0A      %7D else %7B%0A
+        if(sortValues != null) %7B%0A  
         sortValu
@@ -5241,32 +5241,34 @@
 get(doc,value);%0A
+  
         if( Byte
@@ -5304,32 +5304,34 @@
 es )%7B%0A          
+  
 parent.collectFi
@@ -5382,32 +5382,34 @@
 ALUE );%0A        
+  
 %7D else %7B%0A       
@@ -5403,32 +5403,34 @@
 lse %7B%0A          
+ 
+ 
 parent.collectFi
@@ -5459,32 +5459,139 @@
 parse(value) );%0A
+          %7D%0A        %7D else %7B%0A          parent.collectField(doc, name, FacetingAccumulator.MISSING_VALUE );%0A
         %7D%0A      

fix FieldFacetAccumulator