/*
Copyright (C) 2000-2010  Ministere de la culture et de la communication (France), AJLSM
See LICENCE file
*/
package fr.gouv.culture.sdx.search.lucene.filter;

import fr.gouv.culture.sdx.exception.SDXException;
import fr.gouv.culture.sdx.exception.SDXExceptionCode;
import fr.gouv.culture.sdx.framework.Framework;
import fr.gouv.culture.sdx.search.lucene.Field;
import fr.gouv.culture.sdx.search.lucene.query.TermInfo;
import fr.gouv.culture.sdx.search.lucene.query.Terms;
import fr.gouv.culture.sdx.utils.Bits;
import fr.gouv.culture.sdx.utils.Utilities;
import fr.gouv.culture.sdx.utils.constants.Node;
import fr.gouv.culture.sdx.utils.logging.LoggingUtils;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.TermDocs;
import org.apache.lucene.search.Filter;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;

import java.io.IOException;
import java.util.BitSet;
import java.util.Iterator;
import java.util.TreeMap;


/**
 *	The Criteria for a Filter.
 */
public class Criteria extends AbstractFilter {

    /** Un bitset pour stocker les documents pertinents. */
    private BitSet bits;

    /** The field for the search */
    private Field field;

    /** The search pattern */
    private String pattern;

    /** The Lucene filter, if necessary */
    public Filter luceneFilter;


    public Criteria() {
    }

    /**
     *	Contructs a Criteria on the value of a field.
     *
     *	 This value can be a regular expression with the possibilities le ? and le *
     *
     *	@param	field		The field.
     *	@param	pattern		The search pattern.
     */
    public void setUp(Field field, String pattern) throws SDXException {
        //verifying parameters
        if (field == null) throw new SDXException(logger, SDXExceptionCode.ERROR_FIELD_NULL, null, null);
        if (!Utilities.checkString(pattern)) throw new SDXException(logger, SDXExceptionCode.ERROR_INVALID_VALUE, null, null);

        this.field = field;
        this.pattern = pattern;
    }

    /**
     *	Constructs an SDX filter (also a Lucene filter).
     *
     *	@param	luceneFilter		The SDX/Lucene filter.
     */
    public void setUp(Filter luceneFilter) throws SDXException {
        if (luceneFilter == null) throw new SDXException(logger, SDXExceptionCode.ERROR_LUCENE_FILTER_NULL, null, null);

        this.luceneFilter = luceneFilter;
    }


    /**
     *	Applies the criteria and turns over the documents which satisfy it
     *
     *	@param	r		The reader of the index.
     */
    public BitSet bits(IndexReader r) {

        //i removed this old line because the bits need to be recalculated for each indexReader in a multisearcher-rbp
        //if (bits != null) return bits;	// Au cas ou on redemande le filtre

        try {
            if (luceneFilter != null) {
                bits = luceneFilter.bits(r);
                return bits;
            }

            bits = new BitSet(r.numDocs());


            // La premiere chose a faire est de trouver les termes qui satisfont le
            // critere, il peut y en avoir plusieurs a cause des masques.

            TreeMap terms = null;
            try {
                terms = Terms.getTerms(r, field, pattern);
            } catch (SDXException e) {
                //contrained by the lucene interface, so we cannont throw the exception, but will log it for now
                LoggingUtils.logException(logger, e);
                return bits;
            }

            // Ensuite, on doit prendre tous les documents inclus dans
            // ces termes

            TermDocs docs;
            Iterator it = terms.keySet().iterator();
            while (it.hasNext()) {
                TermInfo termInfo = (TermInfo) terms.get((String) it.next());
                docs = r.termDocs(termInfo.getTerm());
                while (docs.next()) {
                    bits.set(docs.doc());
                }
            }

            return bits;
        } catch (IOException e) {
            //TODOException?:should we log messages here?-rbp
            return bits;
        }
    }

    /**
     *	Returns a SAX representation of this criteria.
     *
     *	@param	hdl		The ContentHandler to feed with events.
     */
    public void toSAX(ContentHandler hdl) throws SAXException {
        String sdxNsUri = Framework.SDXNamespaceURI;
        String sdxNsPrefix = Framework.SDXNamespacePrefix;

        //Creation of local variables which are later passed into startElement() and endElement() methods-rbp14/03/02
        String localName = Node.Name.FILTER;
        String qName = sdxNsPrefix + ":" + localName;
        AttributesImpl atts = new AttributesImpl();
        atts.addAttribute("", Node.Name.TYPE, Node.Name.TYPE, Node.Type.CDATA, "criteria");


        if (field != null) atts.addAttribute("", Node.Name.FIELD, Node.Name.FIELD, Node.Type.CDATA, field.getCode());
        if (pattern != null) atts.addAttribute("", Node.Name.VALUE, Node.Name.VALUE, Node.Type.CDATA, pattern);

        // S'il fut construit avec un filtre Lucene, on ne peut rien donner comme information

        if (bits != null) atts.addAttribute("", Node.Name.NB, Node.Name.NB, Node.Type.CDATA, String.valueOf(Bits.countBits(bits)));

        //startElement() method is called for "filter" and local variables are passed-rbp14/03/02
        hdl.startElement(sdxNsUri, localName, qName, atts);

        //endElement() method is called for "filter" and local variables are passed-rbp14/03/02
        hdl.endElement(sdxNsUri, localName, qName);
    }
}
