/*  Copyright (C) 2003-2011 JabRef contributors.
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
// $ANTLR : "TreeParser.g" -> "SearchExpressionTreeParser.java"$

package net.sf.jabref.search;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import net.sf.jabref.BibtexEntry;
import net.sf.jabref.export.layout.format.RemoveLatexCommands;
import antlr.MismatchedTokenException;
import antlr.NoViableAltException;
import antlr.RecognitionException;
import antlr.collections.AST;

@SuppressWarnings({"unused"})
public class SearchExpressionTreeParser extends antlr.TreeParser       implements SearchExpressionTreeParserTokenTypes
 {

    // Formatter used on every field before searching. Removes Latex commands that
    // may interfere with the search:
    static RemoveLatexCommands removeLatexCommands = new RemoveLatexCommands();

    private static final int MATCH_EXACT = 0;
	private static final int MATCH_CONTAINS = 1;
	private static final int MATCH_DOES_NOT_CONTAIN = 2;

	private BibtexEntry bibtexEntry;
	private Object[] searchKeys;

    private static final int PSEUDOFIELD_TYPE = 1;

     public int apply(AST ast, BibtexEntry bibtexEntry) throws antlr.RecognitionException {
		this.bibtexEntry = bibtexEntry;
		// specification of fields to search is done in the search expression itself
		this.searchKeys = bibtexEntry.getAllFields().toArray();
		return tSearchExpression(ast) ? 1 : 0;
	}
public SearchExpressionTreeParser() {
	tokenNames = _tokenNames;
}

	public final boolean  tSearchExpression(AST _t) throws RecognitionException, PatternSyntaxException {
		boolean ret = false;

        AST tSearchExpression_AST_in = (_t == ASTNULL) ? null : (AST)_t;
		
			boolean a = false, b = false;
		
		
		try {      // for error handling
			if (_t==null) _t=ASTNULL;
			switch ( _t.getType()) {
			case And:
			{
				AST __t87 = _t;
				AST tmp1_AST_in = _t;
				match(_t,And);
				_t = _t.getFirstChild();
				a=tSearchExpression(_t);
				_t = _retTree;
				{
				if (_t==null) _t=ASTNULL;
				if ((((_t.getType() >= And && _t.getType() <= ExpressionSearch)))&&(a)) {
					b=tSearchExpression(_t);
					_t = _retTree;
				}
				else if (((_t.getType() >= LITERAL_and && _t.getType() <= ExpressionSearch))) {
					AST tmp2_AST_in = _t;
					if ( _t==null ) throw new MismatchedTokenException();
					_t = _t.getNextSibling();
				}
				else {
					throw new NoViableAltException(_t);
				}
				
				}
				_t = __t87;
				_t = _t.getNextSibling();
				ret = a && b;
				break;
			}
			case Or:
			{
				AST __t89 = _t;
				AST tmp3_AST_in = _t;
				match(_t,Or);
				_t = _t.getFirstChild();
				a=tSearchExpression(_t);
				_t = _retTree;
				{
				if (_t==null) _t=ASTNULL;
				if ((((_t.getType() >= And && _t.getType() <= ExpressionSearch)))&&(!a)) {
					b=tSearchExpression(_t);
					_t = _retTree;
				}
				else if (((_t.getType() >= LITERAL_and && _t.getType() <= ExpressionSearch))) {
					AST tmp4_AST_in = _t;
					if ( _t==null ) throw new MismatchedTokenException();
					_t = _t.getNextSibling();
				}
				else {
					throw new NoViableAltException(_t);
				}
				
				}
				_t = __t89;
				_t = _t.getNextSibling();
				ret = a || b;
				break;
			}
			case Not:
			{
				AST __t91 = _t;
				AST tmp5_AST_in = _t;
				match(_t,Not);
				_t = _t.getFirstChild();
				a=tSearchExpression(_t);
				_t = _retTree;
				_t = __t91;
				_t = _t.getNextSibling();
				ret = !a;
				break;
			}
			case ExpressionSearch:
			{
				ret=tExpressionSearch(_t);
				_t = _retTree;
				break;
			}
			default:
			{
				throw new NoViableAltException(_t);
			}
			}
		}
		catch (RecognitionException ex) {
			reportError(ex);
			if (_t!=null) {_t = _t.getNextSibling();}
		}
		_retTree = _t;
		return ret;
	}
	
	public final boolean  tExpressionSearch(AST _t) throws RecognitionException, PatternSyntaxException {
		 boolean ret = false;

		AST var_f = null;
		AST var_v = null;
		
			int matchType = 0;
		
		
		try {      // for error handling
			AST __t94 = _t;
			AST tmp6_AST_in = _t;
			match(_t,ExpressionSearch);
			_t = _t.getFirstChild();
			var_f = _t;
			match(_t,RegularExpression);
			_t = _t.getNextSibling();
			matchType=tSearchType(_t);
			_t = _retTree;
			var_v = _t;
			match(_t,RegularExpression);
			_t = _t.getNextSibling();
			
						Pattern fieldSpec = ((RegExNode)var_f).getPattern();
						Pattern valueSpec = ((RegExNode)var_v).getPattern();
			boolean noSuchField = true;
						// this loop iterates over all regular keys, then over pseudo keys like "type"
						for (int i = 0; i < searchKeys.length + PSEUDOFIELD_TYPE && !ret; ++i) {
							String content;
							switch (i - searchKeys.length + 1) {
								case PSEUDOFIELD_TYPE:
									if (!fieldSpec.matcher("entrytype").matches())
										continue;
									content = bibtexEntry.getType().getName();
									break;
								default: // regular field
									if (!fieldSpec.matcher(searchKeys[i].toString()).matches())
										continue;
                                    String field = bibtexEntry.getField(searchKeys[i].toString());
									content = field != null ? removeLatexCommands.format(field) : null;

							}
			noSuchField = false;
							if (content == null)
								continue; // paranoia
							Matcher matcher = valueSpec.matcher(content);
							switch (matchType) {
							case MATCH_CONTAINS:
								ret = matcher.find();
								break;
							case MATCH_EXACT:
								ret = matcher.matches();
								break;
							case MATCH_DOES_NOT_CONTAIN:
								ret = !matcher.find();
								break;
							}
						}
			if (noSuchField && matchType == MATCH_DOES_NOT_CONTAIN)
			ret = true; // special case
					
			_t = __t94;
			_t = _t.getNextSibling();
		}
		catch (RecognitionException ex) {
			reportError(ex);
			if (_t!=null) {_t = _t.getNextSibling();}
		}
		_retTree = _t;
		return ret;
	}
	
	public final int  tSearchType(AST _t) throws RecognitionException {
		 int matchType = 0;

        AST tSearchType_AST_in = (_t == ASTNULL) ? null : (AST)_t;
		
		try {      // for error handling
			if (_t==null) _t=ASTNULL;
			switch ( _t.getType()) {
			case LITERAL_contains:
			{
				AST tmp7_AST_in = _t;
				match(_t,LITERAL_contains);
				_t = _t.getNextSibling();
				matchType = MATCH_CONTAINS;
				break;
			}
			case LITERAL_matches:
			{
				AST tmp8_AST_in = _t;
				match(_t,LITERAL_matches);
				_t = _t.getNextSibling();
				matchType = MATCH_EXACT;
				break;
			}
			case EQUAL:
			{
				AST tmp9_AST_in = _t;
				match(_t,EQUAL);
				_t = _t.getNextSibling();
				matchType = MATCH_CONTAINS;
				break;
			}
			case EEQUAL:
			{
				AST tmp10_AST_in = _t;
				match(_t,EEQUAL);
				_t = _t.getNextSibling();
				matchType = MATCH_EXACT;
				break;
			}
			case NEQUAL:
			{
				AST tmp11_AST_in = _t;
				match(_t,NEQUAL);
				_t = _t.getNextSibling();
				matchType = MATCH_DOES_NOT_CONTAIN;
				break;
			}
			default:
			{
				throw new NoViableAltException(_t);
			}
			}
		}
		catch (RecognitionException ex) {
			reportError(ex);
			if (_t!=null) {_t = _t.getNextSibling();}
		}
		_retTree = _t;
		return matchType;
	}
	
	
	public static final String[] _tokenNames = {
		"<0>",
		"EOF",
		"<2>",
		"NULL_TREE_LOOKAHEAD",
		"\"and\"",
		"\"or\"",
		"\"not\"",
		"\"contains\"",
		"\"matches\"",
		"white space",
		"'('",
		"')'",
		"'='",
		"'=='",
		"'!='",
		"'\\\"'",
		"a text literal",
		"a letter",
		"a field type",
		"RegularExpression",
		"And",
		"Or",
		"Not",
		"ExpressionSearch"
	};
	
	}
