/*
 * Copyright (C) 1999-2006 <a href="mailto:paschke@in.tum.de">Adrian Paschke</a>
 * 
 * The ECA Interpreter is part of the Rule Based Service Level Agreement (RBSLA)
 * framework, available at 
 * <a href=" http://ibis.in.tum.de/staff/paschke/rbsla/index.htm">RBSLA</a>
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
package rbsla.eca;

import org.apache.log4j.Logger;

import rbsla.wrapper.KnowledgeBaseWrapper;
import rbsla.wrapper.ProvaWrapper;
import rbsla.utils.JavaReflection;
import ws.prova.RListUtils;
import org.mandarax.kernel.ComplexTerm;
import ws.prova.reference.ComplexTermImpl;
import ws.prova.reference.ProvaResultSetImpl2;

import org.mandarax.kernel.ConstantTerm;
import org.mandarax.kernel.ResultSet;
import org.mandarax.kernel.Term;
import org.mandarax.kernel.VariableTerm;

import java.util.List;
import java.util.Map;
import java.util.ArrayList;
import ws.prova.reference.ComplexTermImpl;
import java.util.Iterator;


/**
 * An Event is defined with a query/goal in Prova.
 * 
 * @author <A HREF="mailto:paschke@in.tum.de">Adrian Paschke</A>
 * @version 0.1 <1 Sept 2005>
 * @since 0.1
 */
public class ProvaEvent implements XMLstorable, Event
{    		
	private String query;			// string representation of goal
	private List objs;				// list of embedded objects


    private static Logger logger = Logger.getLogger(ProvaEvent.class);

    /**
     * Constructor
     * @param query
     */
	public ProvaEvent(Object query)
    {				
		this.query = null;
		objs = new ArrayList();		// empty list of embedded variables
		
		if (query==null) this.query=null;				// null
		else if (query instanceof ComplexTermImpl) { // ComplexTerm query			
			this.query = RListUtils.decode((ComplexTerm) query,objs); // decode goal into string
			this.query=this.query.replaceAll("%","_");
        } else { // query is String
			if ((query.equals("")) || (query.equals("_"))) this.query=null; 
			else {
				this.query = query.toString();												
			}
        }
		
    }
	
    /**
     * Constructor
     * @param query
     * @param objs list of embeded objects
     */
	public ProvaEvent(Object query, List objs)
    {				
		this.query = null;
		this.objs = objs;		
		
		if (query==null) this.query=null;				// null
		else if (query instanceof ComplexTermImpl) { // ComplexTerm query			
			this.query = RListUtils.decode((ComplexTerm) query,objs); // decode goal into string			
        } else {
			if ((query.equals("")) || (query.equals("_"))) this.query=null; 
			else {
				this.query = query.toString();
				this.objs = objs;
			}
        }
	
    }
	
	/**
	 * Return the embedded objects
	 * @return list of embedded objects
	 */
	public List getObjects() {
		return objs;
	}
	
	/**
	 * Return the goal of the event
	 * @return goal of the event
	 */
	public String getGoal() {
		return query;
	}
	
	/**
	 * Clone the event.
	 * @return Event a ProvaEvent
	 */
	public Object clone() {
		List objects = new ArrayList(objs);	
		return new ProvaEvent(query,objs);
	}
	
	/**
	 * Substitute all variables occurences with their bound values.
	 * @param vars variable -> value map
	 */
	public void replace(Map vars) {		
		if (query==null) return;
		if (vars==null) return;
		if (vars.isEmpty()) return;

		for (Iterator it = vars.keySet().iterator();it.hasNext();) {
	
			VariableTerm vname = (VariableTerm) it.next(); // varible term			
			Object value = vars.get(vname); //variable value
			String variable=vname.getName(); // variable name			
			String oldName=variable.substring(variable.indexOf("_")+1); //old variable name			
			String qVariable = ((Term)value).getType().getName()+"."+oldName; // fully qualified old name			
			if (query.indexOf(qVariable)!=-1) {
				query=query.replaceAll(qVariable, variable);				
			}			
			if (query.indexOf(oldName)!=-1) { // query includes substitution variable
				
				String[] terms = query.split(","); // split query
				//query="";
				for (int i = 0; i< terms.length;i++) 			
					if (terms[i].indexOf(oldName)!=-1) { // contains variable
						
						// try substitution

                        // Careful here, it is a back-port from the Java 5 code that used String.replace
                        String rVariable = terms[i].replaceAll("\\[","").replaceAll("\\]","").replaceAll("\"","");
						if (rVariable.indexOf(oldName+".")!=-1) rVariable = rVariable.replaceAll(rVariable.substring(rVariable.lastIndexOf(".")),"");
						
						
						if (value instanceof ComplexTermImpl) {																	
							
							objs.add(value); // add embedded obj to object list
							query = query.replaceAll(variable,"_"+(objs.size()-1)); // replace variable with obj id
							
						} else { // constant term
							
							value = ((ConstantTerm)value).getObject();	
							
							if (value instanceof java.lang.String) { // replace with String (check Semantic Web type system)
								// TODO: typing with OWL
								if ((rVariable.equals(variable)) || (rVariable.indexOf("java.lang.String")!=-1) || (rVariable.indexOf("java.lang.Object")!=-1) || (rVariable.indexOf("@@")==0))
									query = query.replaceAll(rVariable,value.toString());
								
							} else	{ // replace with object (check Java type system)
															
								if ((rVariable.equals(variable)) || (rVariable.indexOf("java.lang.Object")!=-1) || (rVariable.indexOf("@@")==0)) {
									int id;
									if (objs.contains(value)) id=objs.indexOf(value);
									else {
										objs.add(value); // add embedded obj to object list
										id=objs.size()-1;										
									}
									query=query.replaceAll(rVariable,"_"+id);
								} else {
									Class vt1 = value.getClass();								
									Class vt2 = null;
									String clazz;
									try {										
										clazz = rVariable.substring(0,rVariable.indexOf("@@")-1);										
										vt2 = Class.forName(clazz);
									} catch (Exception e) {}
									if (vt2 != null)
									if (vt2.isAssignableFrom(vt1)) { // substitute
										int id;
										if (objs.contains(value)) id=objs.indexOf(value);
										else {
											objs.add(value); // add embedded obj to object list
											id=objs.size()-1;
										}
										query=query.replaceAll(rVariable,"_"+id);										
									}
								}
							}
						}						
				}
				
				if (query.indexOf(oldName)!=-1) {query=null;return;}
			}	
		}		
	}	
	
	
	/**
	 * Evaluate Event
	 * @param _wrapper
	 * @return ResultSet
	 */
    public ResultSet evaluate(KnowledgeBaseWrapper _wrapper)
    {
		
		if (query==null) return null;
				
    	try
        {			
			ProvaWrapper wrapper = (ProvaWrapper) _wrapper;
			if( query.indexOf(".")!=-1)
			{
		
				char c = query.charAt(query.lastIndexOf(".")+1);
		   		if ((Character.isUpperCase(c)) || (c == '@')) 
				{ // case typed variable 
					
					return wrapper.consult(query, objs.toArray());
					
				} else 
				{ // case procedural attachment					
					// TODO return correct ResultSet with results
					if (JavaReflection.invoke(query,objs.toArray())) return null;
					else return wrapper.consult(query, objs.toArray());
				}
			} else return 	wrapper.consult(query, objs.toArray()); 

        } 
        catch (Exception e)
        {
            logger.warn("Evaluation of event failed:", e);			
            return new ProvaResultSetImpl2();
        }

    }
    
    public String toString()
    {
    	return query;
    }

    public String toXML(int intend)
    {
        return intendStr[intend] + "<condition name\"" + query + "\">\n"
                + intendStr[intend] + "</condition>\n";
    }

    static final public Event NOEVENT = new ProvaEvent(null);
}