package daruma.wfs.filter.predicates;

import daruma.wfs.filter.PredicateDescription;

import daruma.xml.Lexicon;
import daruma.xml.util.ElementUtil;
import daruma.xml.util.XMLParseErrorException;

import daruma.storage_manager.StorageManager;
import daruma.storage_manager.type_definition.TypeName;
import daruma.storage_manager.type_definition.TypeDefinition;

import daruma.geometry.TransformationContext;

import org.w3c.dom.Element;
import org.w3c.dom.Node;

import java.util.List;

public class LogicalPredicateDescription extends PredicateDescription
{
	public	static	boolean	isAcceptablePredicate( String  localName )
	{
		return( localName.equals( Lexicon.MispFilterAnd.localname )
		     || localName.equals( Lexicon.MispFilterOr.localname )
		     || localName.equals( Lexicon.MispFilterNot.localname ) );
	}

	private	String	sqlExpression;

	public LogicalPredicateDescription
				( Element  element ,
				  TypeName  typeName ,
				  TypeDefinition  type ,
				  TransformationContext  trans ,
				  StorageManager  storage )
		throws XMLParseErrorException
	{
		super( storage , typeName , type , trans );

		this.sqlExpression = this.createSQLExpression( element );
	}

	public	String	getSQLExpression()
	{
		return( this.sqlExpression );
	}

	public	String	createSQLExpression( Element  element )
						throws XMLParseErrorException
	{
		String	operatorName = element.getLocalName();

		if ( Lexicon.MispFilterAnd.matches(element) ) {
		    this.tagName = Lexicon.MispFilterAnd ;
		    return( this.parseMultipleArgumentsOperation
			    ( element , "AND" ) );
		} else if ( Lexicon.MispFilterOr.matches(element) ) {
		    this.tagName = Lexicon.MispFilterOr ;
		    return( this.parseMultipleArgumentsOperation
			    ( element , "OR" ) );
		} else if ( Lexicon.MispFilterNot.matches(element) ) {
		    this.tagName = Lexicon.MispFilterNot ;
		    return( this.parseUnaryOperation( element , "NOT" ) );
		}
		else
		{
			throw new XMLParseErrorException
			  ( "cannot handle filter \"" + operatorName + "\"" );
		}
	}

	public	String	parseUnaryOperation( Element  element ,
					     String  operationString )
						throws XMLParseErrorException
	{
		Element	subPredicate;

		subPredicate = ElementUtil.getSingleChildElement( element );

		PredicateDescription child = 
		    new IntegratedPredicateDescription
		    ( subPredicate ,
		      super.getTypeName() ,
		      super.getTypeDefinition() ,
		      super.getTransformationContext() ,
		      super.getStorage() ) ;
		this.addChild(child) ;

		return( operationString + " ("
			+ child.getSQLExpression()
			+ ")" );
	}

	public	String	parseMultipleArgumentsOperation
						( Element  element ,
						  String  operationString )
						throws XMLParseErrorException
	{
		List<Element>	childElements = ElementUtil.getChildElements
								  ( element );

		if ( childElements.size() < 2 )
		{
			throw new XMLParseErrorException
				( "too few arguments of element \""
				  + element.getLocalName() + "\"" );
		}

		StringBuilder	buf = new StringBuilder( "(" );

		boolean	firstChild = true;

		for ( Element  c : childElements )
		{
			PredicateDescription child = 
			    new IntegratedPredicateDescription
			    ( c ,
			      super.getTypeName() ,
			      super.getTypeDefinition() ,
			      super.getTransformationContext() ,
			      super.getStorage() ) ;
			this.addChild(child) ;

			if ( ! firstChild ) {
			    buf.append( ") " + operationString + " (" );
			}

			buf.append( child.getSQLExpression() );

			firstChild = false;
		}

		buf.append( ")" );

		return( buf.toString() );
	}

    //--------------------------------------------------
    /**
     * 詳細チェック
     * 地理検索などのチェックを精密に行う、2nd pass の処理
     * 精密には or と not の場合におかしくなる。
     */
    public boolean detailedCheck(Node feature) 
    	throws XMLParseErrorException
    {
	boolean r = false ;
	if(this.tagName == Lexicon.MispFilterAnd) {
	    r = true ;
	} else if (this.tagName == Lexicon.MispFilterOr) {
	    r = false ;
	} 

	for(PredicateDescription child : this.getChildren()) {
	    boolean childR = child.detailedCheck(feature) ;
	    if(this.tagName == Lexicon.MispFilterAnd) {
		r = (r && childR) ;
	    } else if (this.tagName == Lexicon.MispFilterOr) {
		r = (r || childR) ;
	    } else if (this.tagName == Lexicon.MispFilterNot) {
		/* !!! [06/10/17 03:57 I.Noda] !!! 
		 * 本来は (! childR) だが、 childR が正確でないので、
		 * true に集約。
		 * どこまで精密にすべきか、難しいところ。
		 * あるいは 3 値論理をいれるとある程度解決するかもしれない
		 */
		//r = (! childR) ;
		r = true ;
	    }
	}

	return r ;
    }
}
