/*
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.framework.Framework;
import fr.gouv.culture.sdx.search.lucene.query.Query;
import fr.gouv.culture.sdx.utils.Bits;
import fr.gouv.culture.sdx.utils.constants.Node;
import org.apache.lucene.index.IndexReader;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;

import java.util.BitSet;
import java.util.Vector;


/*
 *	Un filtre sur une base de documents.
 *
 *	Cette definition de filtre est tres generique. Il s'agit d'un arbre de criteres
 *	( objets Criteria ), les criteres freres etant relies par des operateurs booleens
 *	( ET ou OU ). On identifie les documents qui repectent ce filtre en faisant une
 *	logique booleenne appropriee pour chaque critere.
 */

/** A filter on a base of documents.
 *
 * This definition of filter is very generic. It is composed of a tree of criteria (Criteria objects),
 * these criteria being connected by Boolean operators (AND or OR). One identifies the
 * documents which respect this filter by making a Boolean logic adapted for each criteria.
 *
 */
public class Filter extends AbstractFilter {

    /** The "AND" operator */
    public final static int BOOLEAN_OPERATOR_AND = Query.OPERATOR_AND;

    /**The "OR" operator */
    public final static int BOOLEAN_OPERATOR_OR = Query.OPERATOR_OR;

    /** The "NOT" operator */
    public final static int BOOLEAN_OPERATOR_NOT = Query.OPERATOR_NOT;

    /**	The operator connecting the criterion */
    private int operator;

    /** The children nodes of this filter  */
    private Vector children = new Vector();

    /** Documents respecting the filter  */
    private BitSet bits;

    public Filter() {
        //calling setup for default operator
        setUp(-1);
    }

    /**Constructs a new filter with the specified operator
     *
     * @param operator  The operator for the filter
     */

    public Filter(int operator) {
        this.operator = operator;
    }

    /**
     *	Builds a filter of several criterion connected by an operator.
     *
     *	@param	operator		The operator (AND or OR) connecting the criterion .
     */
    public void setUp(int operator) {
        // On conserve l'oprateur
        switch (operator) {
            case BOOLEAN_OPERATOR_AND:
            case BOOLEAN_OPERATOR_OR:
            case BOOLEAN_OPERATOR_NOT:
                this.operator = operator;
                break;
            default:
                this.operator = BOOLEAN_OPERATOR_AND; // Defaut
        }
    }

    /**
     *	Adds a criteria to the list.
     *
     *	@param		criteria	A criteria or a list of criterion
     */
    public void add(FilterCriteria criteria) {
        if (criteria != null) children.add(criteria);
    }

    /**
     *	Returns a list of documents which respect this filter.
     *
     *	To obtain this list, the lists provided by
     *  all the children of this filter are compiled
     *
     *	@param	r		An object for reading the index.
     */
    public BitSet bits(IndexReader r) {
        for (int i = 0; i < children.size(); i++) {
            if (i == 0)
                bits = ((FilterCriteria) children.get(i)).bits(r);
            else {
                switch (operator) {
                    case BOOLEAN_OPERATOR_AND:
                        bits.and(((FilterCriteria) children.get(i)).bits(r));
                        break;
                    case BOOLEAN_OPERATOR_OR:
                        bits.or(((FilterCriteria) children.get(i)).bits(r));
                        break;
                    case BOOLEAN_OPERATOR_NOT:
                        BitSet tempBits = new BitSet(bits.size());
                        tempBits.xor(((FilterCriteria) children.get(i)).bits(r));
                        bits.and(tempBits);
                        break;
                }
            }
        }
        return bits;
    }

    /**
     *	Returns a SAX representation of this criteria.
     *
     *	@param hdl  		The ContentHandler to which the events will be supplied.
     */
    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, "linear");

        switch (operator) {
            case BOOLEAN_OPERATOR_AND:
                atts.addAttribute("", Node.Name.OPERATOR, Node.Name.OPERATOR, Node.Type.CDATA, "and");
                break;
            case BOOLEAN_OPERATOR_OR:
                atts.addAttribute("", Node.Name.OPERATOR, Node.Name.OPERATOR, Node.Type.CDATA, "or");
                break;
            case BOOLEAN_OPERATOR_NOT:
                atts.addAttribute("", Node.Name.OPERATOR, Node.Name.OPERATOR, Node.Type.CDATA, "not");
                break;
        }
        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);

        for (int i = 0; i < children.size(); i++)
            ((FilterCriteria) children.get(i)).toSAX(hdl);

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

}