/*
 * Copyright 2010 JBoss Inc
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.drools.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Queue;

import org.drools.FactException;
import org.drools.KnowledgeBase;
import org.drools.RuleBase;
import org.drools.WorkingMemory;
import org.drools.command.Command;
import org.drools.command.Context;
import org.drools.command.impl.ContextImpl;
import org.drools.command.impl.GenericCommand;
import org.drools.command.impl.KnowledgeCommandContext;
import org.drools.command.runtime.BatchExecutionCommandImpl;
import org.drools.common.AbstractWorkingMemory;
import org.drools.common.EndOperationListener;
import org.drools.common.InternalAgenda;
import org.drools.common.InternalFactHandle;
import org.drools.common.InternalKnowledgeRuntime;
import org.drools.common.InternalWorkingMemory;
import org.drools.common.InternalWorkingMemoryEntryPoint;
import org.drools.common.ObjectStore;
import org.drools.common.ObjectTypeConfigurationRegistry;
import org.drools.common.WorkingMemoryAction;
import org.drools.event.ActivationCancelledEvent;
import org.drools.event.ActivationCreatedEvent;
import org.drools.event.AfterActivationFiredEvent;
import org.drools.event.AgendaGroupPoppedEvent;
import org.drools.event.AgendaGroupPushedEvent;
import org.drools.event.BeforeActivationFiredEvent;
import org.drools.event.ObjectInsertedEvent;
import org.drools.event.ObjectRetractedEvent;
import org.drools.event.ObjectUpdatedEvent;
import org.drools.event.RuleFlowGroupActivatedEvent;
import org.drools.event.RuleFlowGroupDeactivatedEvent;
import org.drools.event.process.ProcessEventListener;
import org.drools.event.rule.AgendaEventListener;
import org.drools.event.rule.WorkingMemoryEventListener;
import org.drools.event.rule.impl.ActivationCancelledEventImpl;
import org.drools.event.rule.impl.ActivationCreatedEventImpl;
import org.drools.event.rule.impl.AfterActivationFiredEventImpl;
import org.drools.event.rule.impl.AgendaGroupPoppedEventImpl;
import org.drools.event.rule.impl.AgendaGroupPushedEventImpl;
import org.drools.event.rule.impl.BeforeActivationFiredEventImpl;
import org.drools.event.rule.impl.ObjectInsertedEventImpl;
import org.drools.event.rule.impl.ObjectRetractedEventImpl;
import org.drools.event.rule.impl.ObjectUpdatedEventImpl;
import org.drools.reteoo.EntryPointNode;
import org.drools.reteoo.ReteooWorkingMemory;
import org.drools.rule.EntryPoint;
import org.drools.rule.Rule;
import org.drools.runtime.Calendars;
import org.drools.runtime.Channel;
import org.drools.runtime.Environment;
import org.drools.runtime.ExecutionResults;
import org.drools.runtime.ExitPoint;
import org.drools.runtime.Globals;
import org.drools.runtime.KnowledgeSessionConfiguration;
import org.drools.runtime.StatefulKnowledgeSession;
import org.drools.runtime.impl.ExecutionResultImpl;
import org.drools.runtime.process.InternalProcessRuntime;
import org.drools.runtime.process.ProcessInstance;
import org.drools.runtime.process.WorkItemManager;
import org.drools.runtime.rule.Agenda;
import org.drools.runtime.rule.AgendaFilter;
import org.drools.runtime.rule.FactHandle;
import org.drools.runtime.rule.LiveQuery;
import org.drools.runtime.rule.QueryResults;
import org.drools.runtime.rule.ViewChangedEventListener;
import org.drools.runtime.rule.WorkingMemoryEntryPoint;
import org.drools.runtime.rule.impl.AgendaImpl;
import org.drools.runtime.rule.impl.NativeQueryResults;
import org.drools.spi.Activation;
import org.drools.time.SessionClock;
import org.drools.time.TimerService;

public class StatefulKnowledgeSessionImpl
    implements
    StatefulKnowledgeSession,
    InternalWorkingMemoryEntryPoint,
    InternalKnowledgeRuntime {

    public ReteooWorkingMemory session;
    public KnowledgeBase   kbase;

    public StatefulKnowledgeSessionImpl(ReteooWorkingMemory session) {
        this( session,
              new KnowledgeBaseImpl( session.getRuleBase() ) );
    }

    public StatefulKnowledgeSessionImpl(ReteooWorkingMemory session,
                                        KnowledgeBase kbase) {
        this.session = session;
        this.kbase = kbase;
        this.session.setKnowledgeRuntime( this );
    }
    
    public void reset() {
        throw new UnsupportedOperationException("This should not be called");
    }
    
    public ObjectStore getObjectStore() {
        return this.session.getObjectStore();
    }

    public EntryPointNode getEntryPointNode() {
        return this.session.getEntryPointNode();
    }

    public int getId() {
        return this.session.getId();
    }

    public WorkingMemoryEntryPoint getWorkingMemoryEntryPoint(String name) {
        return session.getWorkingMemoryEntryPoint( name );
    }

    public Collection< ? extends org.drools.runtime.rule.WorkingMemoryEntryPoint> getWorkingMemoryEntryPoints() {
        return session.getWorkingMemoryEntryPoints();
    }

    public void addEventListener(WorkingMemoryEventListener listener) {
        WorkingMemoryEventListenerWrapper wrapper = new WorkingMemoryEventListenerWrapper( listener );
        this.session.addEventListener( wrapper );
    }

    public void removeEventListener(WorkingMemoryEventListener listener) {
        WorkingMemoryEventListenerWrapper wrapper = null;
        if ( listener != null && !(listener instanceof WorkingMemoryEventListenerWrapper) ) {
            wrapper = new WorkingMemoryEventListenerWrapper( listener );
        } else {
            wrapper = (WorkingMemoryEventListenerWrapper) listener;
        }
        this.session.removeEventListener( wrapper );
    }

    public Collection<WorkingMemoryEventListener> getWorkingMemoryEventListeners() {
        List<WorkingMemoryEventListener> listeners = new ArrayList<WorkingMemoryEventListener>();
        for ( Object  listener : this.session.getWorkingMemoryEventListeners() ) {
            if ( listener instanceof WorkingMemoryEventListenerWrapper ) {
                listeners.add( ((WorkingMemoryEventListenerWrapper) listener).unWrap() );
            } else {
                listeners.add( ( WorkingMemoryEventListener ) listener );
            }
        }
        return Collections.unmodifiableCollection( listeners );
    }

    public void addEventListener(AgendaEventListener listener) {
        AgendaEventListenerWrapper wrapper = new AgendaEventListenerWrapper( listener );
        this.session.addEventListener( wrapper );
    }

    public Collection<AgendaEventListener> getAgendaEventListeners() {
        List<AgendaEventListener> listeners = new ArrayList<AgendaEventListener>();
        for ( Object listener :  this.session.getAgendaEventListeners() ) {
            if ( listener instanceof AgendaEventListenerWrapper ) {
                listeners.add( ((AgendaEventListenerWrapper) listener).unWrap() );
            } else {
                listeners.add( (AgendaEventListener) listener );
            }
        }
        return Collections.unmodifiableCollection( listeners );
    }

    public void removeEventListener(AgendaEventListener listener) {
        AgendaEventListenerWrapper wrapper = null;
        if ( listener != null && !(listener instanceof AgendaEventListenerWrapper) ) {
            wrapper = new AgendaEventListenerWrapper( listener );
        } else {
            wrapper = (AgendaEventListenerWrapper) listener;
        }
        this.session.removeEventListener( wrapper );
    }

    public void addEventListener(ProcessEventListener listener) {
        ((InternalProcessRuntime) this.session.getProcessRuntime()).addEventListener( listener );
    }

    public Collection<ProcessEventListener> getProcessEventListeners() {
        return ((InternalProcessRuntime) this.session.getProcessRuntime()).getProcessEventListeners();
    }

    public void removeEventListener(ProcessEventListener listener) {
        ((InternalProcessRuntime) this.session.getProcessRuntime()).removeEventListener( listener );
    }

    public KnowledgeBase getKnowledgeBase() {
        if ( this.kbase == null ) {
            this.kbase = new KnowledgeBaseImpl( session.getRuleBase() );
        }
        return this.kbase;
    }

    public int fireAllRules() {
        return this.session.fireAllRules();
    }

    public int fireAllRules(int max) {
        return this.session.fireAllRules( max );
    }

    public int fireAllRules(AgendaFilter agendaFilter) {
        return this.session.fireAllRules( new AgendaFilterWrapper( agendaFilter ) );
    }

    public int fireAllRules(AgendaFilter agendaFilter, int max) {
        return this.session.fireAllRules( new AgendaFilterWrapper( agendaFilter ), max );
    }

    public void fireUntilHalt() {
        this.session.fireUntilHalt();
    }

    public void fireUntilHalt(AgendaFilter agendaFilter) {
        this.session.fireUntilHalt( new AgendaFilterWrapper( agendaFilter ) );
    }

    @SuppressWarnings("unchecked")
    public <T extends SessionClock> T getSessionClock() {
        return (T) this.session.getSessionClock();
    }

    public void halt() {
        this.session.halt();
    }

    public void dispose() {
        this.session.dispose();
    }

    public FactHandle insert(Object object) {
        return this.session.insert( object );
    }

    public void retract(FactHandle factHandle) {
        this.session.retract( factHandle );

    }

    public void update(FactHandle factHandle) {
        this.session.update( factHandle,
                             ((InternalFactHandle) factHandle).getObject() );
    }

    public void update(FactHandle factHandle,
                       Object object) {
        this.session.update( factHandle,
                             object );
    }

    public FactHandle getFactHandle(Object object) {
        return this.session.getFactHandle( object );
    }

    public Object getObject(FactHandle factHandle) {
        return this.session.getObject( factHandle );
    }

    public ProcessInstance getProcessInstance(long id) {
        return this.session.getProcessInstance( id );
    }

    public void abortProcessInstance(long id) {
        this.session.getProcessRuntime().abortProcessInstance(id);
    }

    public Collection<ProcessInstance> getProcessInstances() {
        List<ProcessInstance> result = new ArrayList<ProcessInstance>();
        result.addAll( this.session.getProcessInstances() );
        return result;
    }

    public WorkItemManager getWorkItemManager() {
        return this.session.getWorkItemManager();
    }

    public ProcessInstance startProcess(String processId) {
        return this.session.startProcess( processId );
    }

    public ProcessInstance startProcess(String processId,
                                        Map<String, Object> parameters) {
        return this.session.startProcess( processId,
                                          parameters );
    }

	public ProcessInstance createProcessInstance(String processId,
			                                     Map<String, Object> parameters) {
		return this.session.createProcessInstance(processId, parameters);
	}

	public ProcessInstance startProcessInstance(long processInstanceId) {
		return this.session.startProcessInstance(processInstanceId);
	}

    public void signalEvent(String type,
                            Object event) {
        this.session.getProcessRuntime().signalEvent( type, event );
    }

    public void signalEvent(String type,
                            Object event,
                            long processInstanceId) {
        this.session.getProcessRuntime().signalEvent(type, event, processInstanceId);
    }

    public void setGlobal(String identifier,
                          Object object) {
        this.session.setGlobal( identifier,
                                object );
    }

    public Object getGlobal(String identifier) {
        return this.session.getGlobal( identifier );
    }

    public Globals getGlobals() {
        return (Globals) this.session.getGlobalResolver();
    }

    public Calendars getCalendars() {
        return this.session.getCalendars();
    }

    public Environment getEnvironment() {
        return this.session.getEnvironment();
    }

    //    public Future<Object> asyncInsert(Object object) {
    //        return new FutureAdapter( this.session.asyncInsert( object ) );
    //    }
    //
    //    public Future<Object> asyncInsert(Object[] array) {
    //        return new FutureAdapter( this.session.asyncInsert( array ) );
    //    }
    //
    //    public Future<Object> asyncInsert(Iterable< ? > iterable) {
    //        return new FutureAdapter( this.session.asyncInsert( iterable ) );
    //    }
    //
    //    public Future< ? > asyncFireAllRules() {
    //        return new FutureAdapter( this.session.asyncFireAllRules() );
    //    }

    public <T extends org.drools.runtime.rule.FactHandle> Collection<T> getFactHandles() {
        return new ObjectStoreWrapper( session.getObjectStore(),
                                       null,
                                       ObjectStoreWrapper.FACT_HANDLE );
    }

    public <T extends org.drools.runtime.rule.FactHandle> Collection<T> getFactHandles(org.drools.runtime.ObjectFilter filter) {
        return new ObjectStoreWrapper( session.getObjectStore(),
                                       filter,
                                       ObjectStoreWrapper.FACT_HANDLE );
    }

    public Collection<Object> getObjects() {
        return new ObjectStoreWrapper( session.getObjectStore(),
                                       null,
                                       ObjectStoreWrapper.OBJECT );
    }

    public Collection<Object> getObjects(org.drools.runtime.ObjectFilter filter) {
        return new ObjectStoreWrapper( session.getObjectStore(),
                                       filter,
                                       ObjectStoreWrapper.OBJECT );
    }

    public void retract(org.drools.FactHandle factHandle,
                        boolean removeLogical,
                        boolean updateEqualsMap,
                        Rule rule,
                        Activation activation) throws FactException {
        ((AbstractWorkingMemory) this.session).retract( factHandle,
                                                        removeLogical,
                                                        updateEqualsMap,
                                                        rule,
                                                        activation );
    }

    public void update(FactHandle factHandle,
                       Object object,
                       Rule rule,
                       Activation activation) throws FactException {
        ((AbstractWorkingMemory) this.session).update( (org.drools.FactHandle) factHandle,
                                                       object,
                                                       rule,
                                                       activation );
    }

    public EntryPoint getEntryPoint() {
        return session.getEntryPoint();
    }

    public InternalWorkingMemory getInternalWorkingMemory() {
        return session;
    }

    public org.drools.FactHandle getFactHandleByIdentity(Object object) {
        return session.getFactHandleByIdentity( object );
    }

    public static abstract class AbstractImmutableCollection
        implements
        Collection {

        public boolean add(Object o) {
            throw new UnsupportedOperationException( "This is an immmutable Collection" );
        }

        public boolean addAll(Collection c) {
            throw new UnsupportedOperationException( "This is an immmutable Collection" );
        }

        public void clear() {
            throw new UnsupportedOperationException( "This is an immmutable Collection" );
        }

        public boolean remove(Object o) {
            throw new UnsupportedOperationException( "This is an immmutable Collection" );
        }

        public boolean removeAll(Collection c) {
            throw new UnsupportedOperationException( "This is an immmutable Collection" );
        }

        public boolean retainAll(Collection c) {
            throw new UnsupportedOperationException( "This is an immmutable Collection" );
        }
    }

    public static class ObjectStoreWrapper extends AbstractImmutableCollection {
        public ObjectStore                     store;
        public org.drools.runtime.ObjectFilter filter;
        public int                             type;           // 0 == object, 1 == facthandle
        public static final int                OBJECT      = 0;
        public static final int                FACT_HANDLE = 1;

        public ObjectStoreWrapper(ObjectStore store,
                                  org.drools.runtime.ObjectFilter filter,
                                  int type) {
            this.store = store;
            this.filter = filter;
            this.type = type;
        }

        public boolean contains(Object object) {
            if ( object instanceof FactHandle ) {
                return this.store.getObjectForHandle( (InternalFactHandle) object ) != null;
            } else {
                return this.store.getHandleForObject( object ) != null;
            }
        }

        public boolean containsAll(Collection c) {
            for ( Object object : c ) {
                if ( !contains( object ) ) {
                    return false;
                }
            }
            return true;
        }

        public boolean isEmpty() {
            if ( this.filter == null ) {
                return this.store.isEmpty();
            }

            return size() == 0;
        }

        public int size() {
            if ( this.filter == null ) {
                return this.store.size();
            }

            int i = 0;
            for ( Iterator it = iterator(); it.hasNext(); ) {
                it.next();
                i++;
            }

            return i;
        }

        public Iterator< ? > iterator() {
            Iterator it = null;
            if ( type == OBJECT ) {
                if ( filter != null ) {
                    it = store.iterateObjects( filter );
                } else {
                    it = store.iterateObjects();
                }
            } else {
                if ( filter != null ) {
                    it = store.iterateFactHandles( filter );
                } else {
                    it = store.iterateFactHandles();
                }
            }
            return it;
        }

        public Object[] toArray() {
            if ( type == FACT_HANDLE ) {
                return toArray( new InternalFactHandle[size()] );
            } else {
                return toArray( new Object[size()] );
            }            
            
        }

        public Object[] toArray(Object[] array) {
            if ( array == null || array.length != size() ) {
                if ( type == FACT_HANDLE ) {
                    array = new InternalFactHandle[size()];
                } else {
                    array = new Object[size()];
                }
            }

            int i = 0;
            for ( Iterator it = iterator(); it.hasNext(); ) {
                array[i++] = it.next();
            }

            return array;
        }
    }

    public static class WorkingMemoryEventListenerWrapper
        implements
        org.drools.event.WorkingMemoryEventListener {
        private final WorkingMemoryEventListener listener;

        public WorkingMemoryEventListenerWrapper(WorkingMemoryEventListener listener) {
            this.listener = listener;
        }

        public void objectInserted(ObjectInsertedEvent event) {
            listener.objectInserted( new ObjectInsertedEventImpl( event ) );
        }

        public void objectRetracted(ObjectRetractedEvent event) {
            listener.objectRetracted( new ObjectRetractedEventImpl( event ) );
        }

        public void objectUpdated(ObjectUpdatedEvent event) {
            listener.objectUpdated( new ObjectUpdatedEventImpl( event ) );
        }

        public WorkingMemoryEventListener unWrap() {
            return listener;
        }

        /**
         * Since this is a class adapter for API compatibility, the 
         * equals() and hashCode() methods simply delegate the calls 
         * to the wrapped instance. That is implemented this way
         * in order for them to be able to match corresponding instances
         * in internal hash-based maps and sets.  
         */
        @Override
        public int hashCode() {
            return listener != null ? listener.hashCode() : 0;
        }

        /**
         * Since this is a class adapter for API compatibility, the 
         * equals() and hashCode() methods simply delegate the calls 
         * to the wrapped instance. That is implemented this way
         * in order for them to be able to match corresponding instances
         * in internal hash-based maps and sets.  
         */
        @Override
        public boolean equals(Object obj) {
            if ( listener == null || obj == null ) {
                return obj == listener;
            }
            if ( obj instanceof WorkingMemoryEventListenerWrapper ) {
                return this.listener.equals( ((WorkingMemoryEventListenerWrapper) obj).unWrap() );
            }
            return this.listener.equals( obj );
        }
    }

    public static class AgendaEventListenerWrapper
        implements
        org.drools.event.AgendaEventListener {
        private final AgendaEventListener listener;

        public AgendaEventListenerWrapper(AgendaEventListener listener) {
            this.listener = listener;
        }

        public void activationCancelled(ActivationCancelledEvent event,
                                        WorkingMemory workingMemory) {

            listener.activationCancelled( new ActivationCancelledEventImpl( event.getActivation(),
                                                                            ((InternalWorkingMemory) workingMemory).getKnowledgeRuntime(),
                                                                            event.getCause() ) );

        }

        public void activationCreated(ActivationCreatedEvent event,
                                      WorkingMemory workingMemory) {
            listener.activationCreated( new ActivationCreatedEventImpl( event.getActivation(),
                                                                        ((InternalWorkingMemory) workingMemory).getKnowledgeRuntime() ) );
        }

        public void beforeActivationFired(BeforeActivationFiredEvent event,
                                          WorkingMemory workingMemory) {
            listener.beforeActivationFired( new BeforeActivationFiredEventImpl( event.getActivation(),
                                                                                ((InternalWorkingMemory) workingMemory).getKnowledgeRuntime() ) );
        }

        public void afterActivationFired(AfterActivationFiredEvent event,
                                         WorkingMemory workingMemory) {
            listener.afterActivationFired( new AfterActivationFiredEventImpl( event.getActivation(),
                                                                              ((InternalWorkingMemory) workingMemory).getKnowledgeRuntime() ) );
        }

        public void agendaGroupPopped(AgendaGroupPoppedEvent event,
                                      WorkingMemory workingMemory) {
            listener.agendaGroupPopped( new AgendaGroupPoppedEventImpl( event.getAgendaGroup(),
                                                                        ((InternalWorkingMemory) workingMemory).getKnowledgeRuntime() ) );
        }

        public void agendaGroupPushed(AgendaGroupPushedEvent event,
                                      WorkingMemory workingMemory) {
            listener.agendaGroupPushed( new AgendaGroupPushedEventImpl( event.getAgendaGroup(),
                                                                        ((InternalWorkingMemory) workingMemory).getKnowledgeRuntime() ) );
        }

        public AgendaEventListener unWrap() {
            return listener;
        }

        /**
         * Since this is a class adapter for API compatibility, the 
         * equals() and hashCode() methods simply delegate the calls 
         * to the wrapped instance. That is implemented this way
         * in order for them to be able to match corresponding instances
         * in internal hash-based maps and sets.  
         */
        @Override
        public int hashCode() {
            return listener != null ? listener.hashCode() : 0;
        }

        /**
         * Since this is a class adapter for API compatibility, the 
         * equals() and hashCode() methods simply delegate the calls 
         * to the wrapped instance. That is implemented this way
         * in order for them to be able to match corresponding instances
         * in internal hash-based maps and sets.  
         */
        @Override
        public boolean equals(Object obj) {
            if ( listener == null || obj == null ) {
                return obj == listener;
            }
            if ( obj instanceof AgendaEventListenerWrapper ) {
                return this.listener.equals( ((AgendaEventListenerWrapper) obj).unWrap() );
            }
            return this.listener.equals( obj );
        }

        public void afterRuleFlowGroupActivated(
                RuleFlowGroupActivatedEvent event, WorkingMemory workingMemory) {
        }

        public void afterRuleFlowGroupDeactivated(
                RuleFlowGroupDeactivatedEvent event, WorkingMemory workingMemory) {
        }

        public void beforeRuleFlowGroupActivated(
                RuleFlowGroupActivatedEvent event, WorkingMemory workingMemory) {
        }

        public void beforeRuleFlowGroupDeactivated(
                RuleFlowGroupDeactivatedEvent event, WorkingMemory workingMemory) {
        }
    }

    public static class AgendaFilterWrapper
        implements
        org.drools.spi.AgendaFilter {
        private AgendaFilter filter;

        public AgendaFilterWrapper(AgendaFilter filter) {
            this.filter = filter;
        }

        public boolean accept(Activation activation) {
            return filter.accept( activation );
        }
    }

    public Agenda getAgenda() {
        return new AgendaImpl( (InternalAgenda) this.session.getAgenda() );
    }

    /**
     * @deprecated Use {@link #registerChannel(String, Channel)} instead.
     */
    @Deprecated
    public void registerExitPoint(String name,
                                  ExitPoint exitPoint) {
        this.session.registerExitPoint( name,
                                        exitPoint );
    }

    /**
     * @deprecated Use {@link #unregisterChannel(String)} instead.
     */
    @Deprecated
    public void unregisterExitPoint(String name) {
        this.session.unregisterExitPoint( name );
    }

    public void registerChannel(String name,
                                Channel channel) {
        this.session.registerChannel( name,
                                      channel );
    }

    public void unregisterChannel(String name) {
        this.session.unregisterChannel( name );
    }

    public Map<String, Channel> getChannels() {
        return this.session.getChannels();
    }

    public ObjectTypeConfigurationRegistry getObjectTypeConfigurationRegistry() {
        return this.session.getObjectTypeConfigurationRegistry();
    }

    public RuleBase getRuleBase() {
        return ((KnowledgeBaseImpl) this.kbase).ruleBase;
    }

    public QueryResults getQueryResults(String query,
                                        Object... arguments) {
        return new NativeQueryResults( this.session.getQueryResults( query,
                                                                     arguments ) );
    }

    private KnowledgeCommandContext commandContext = new KnowledgeCommandContext( new ContextImpl( "ksession",
                                                                                                   null ),
                                                                                  null,
                                                                                  this.kbase,
                                                                                  this,
                                                                                  null );

    public <T> T execute(Command<T> command) {
        return execute( null,
                        command );
    }

    public <T> T execute(Context context,
                         Command<T> command) {

        ExecutionResultImpl results = null;
        if ( context != null ) {
            results = (ExecutionResultImpl) ((KnowledgeCommandContext) context).getExecutionResults();
        }

        if ( results == null ) {
            results = new ExecutionResultImpl();
        }

        if ( !(command instanceof BatchExecutionCommandImpl) ) {
            return (T) ((GenericCommand) command).execute( new KnowledgeCommandContext( context,
                                                                                        null,
                                                                                        this.kbase,
                                                                                        this,
                                                                                        results ) );
        }

        

        try {
            session.startBatchExecution( results );
            ((GenericCommand) command).execute( new KnowledgeCommandContext( context,
                                                                             null,
                                                                             this.kbase,
                                                                             this,
                                                                             results ) );
            ExecutionResults result = session.getExecutionResult();
            return (T) result;
        } finally {
            session.endBatchExecution();
        }
    }

    public String getEntryPointId() {
        return this.session.getEntryPointId();
    }

    public long getFactCount() {
        return this.session.getFactCount();
    }

    public LiveQuery openLiveQuery(String query,
                                   Object[] arguments,
                                   ViewChangedEventListener listener) {
        return this.session.openLiveQuery( query,
                                           arguments,
                                           listener );
    }
    
    public KnowledgeSessionConfiguration getSessionConfiguration() {
        return this.session.getSessionConfiguration();
    }

    public TimerService getTimerService() {
        return this.session.getTimerService();
    }

    public void startOperation() {
        this.session.startOperation();
    }

    public void endOperation() {
        this.session.endOperation();
    }

    public void executeQueuedActions() {
        this.session.executeQueuedActions();
    }

    public Queue<WorkingMemoryAction> getActionQueue() {
        return this.session.getActionQueue();
    }

    public InternalProcessRuntime getProcessRuntime() {
        return this.session.getProcessRuntime();
    }

    public void queueWorkingMemoryAction(WorkingMemoryAction action) {
        this.session.queueWorkingMemoryAction(action);
    }

    public void setId(int id) {
        this.session.setId(id);
    }

    public void setEndOperationListener(EndOperationListener listener) {
        this.session.setEndOperationListener(listener);
    }

    public long getLastIdleTimestamp() {
        return this.session.getLastIdleTimestamp();
    }


}
