/*
 * 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 java.util.Calendar;
import java.util.Enumeration;
import java.util.GregorianCalendar;

import EDU.oswego.cs.dl.util.concurrent.BoundedBuffer;
import EDU.oswego.cs.dl.util.concurrent.PooledExecutor;
import org.mandarax.kernel.ResultSet;

/**
 * Uses Multithreading for condition and action.
 * 
 * @author <A HREF="mailto:paschke@in.tum.de">Adrian Paschke</A>
 * @version 0.1 <1 Sept 2005>
 * @since 0.1
 */
public class DaemonMultithreading extends Daemon
{

    final static int THREAD_COUNT = 10;

    private boolean debug = false;

    private boolean doAbort = false;
	
	private boolean timer = false; // hide timer;
	
	private int interval = 1;  // default loop interval = 1 sec.

    private PooledExecutor eventConditionActionPool;

    public DaemonMultithreading()
    {
        eventConditionActionPool = new PooledExecutor(
                new BoundedBuffer(10), 100);
		eventConditionActionPool.createThreads(THREAD_COUNT);
		eventConditionActionPool.setMinimumPoolSize(4);
		eventConditionActionPool.setKeepAliveTime(1000 * 60 * 2); // 2 minutes

    }
	
	/**
	 * Print timer information to console
	 * @param _timer boolean true = show / false = hide
	 */
	public void showTimer(boolean _timer) {
		timer = _timer;
	}
	
	/**
	 * set the loop interval for the eca daemon.
	 * @param _interval defines the query interval of the daemon in seconds	 
	 */
	public void setInterval(int _interval) {
		interval = _interval;
	}
	
	/**
	 * Returns the loop interval (in seconds)
	 */
	public int getInterval() {
		return interval;
	}
	
	/**
	 * Start the daemon loop
	 */
    public void run()
    {
        
		Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
		
		setUpDaemon();
		
		Calendar currentTime;

        long timeDiff = 0;


        // refresh active knowledge
        akb = getActiveRules();
		if (akb==null) {
			System.out.println("No active rules found in ContractLog script - Execution finished");
			return;
		}
        akb.startSystemTime(SystemTime.onStartup);

        if (!debug) {
            currentTime = Calendar.getInstance();
			try { // synchronize start with milliseconds ~ 0
				Thread.sleep(1010-currentTime.get(Calendar.MILLISECOND));
			} catch (InterruptedException ex) {}
        } else
        {
            currentTime = new GregorianCalendar(2004, 1, 1, 0, 0, 0);
        }
		
		
        while (!doAbort)
        {
            try
            {
				currentTime = Calendar.getInstance();	
                // refresh active knowledge                
				akb = getActiveRules();		
				
//				 print current time
				if (timer) {
					Calendar timer = Calendar.getInstance();
					System.out.println("");
					System.out.println("----------------- time "
                        + timer.get(Calendar.HOUR_OF_DAY) + ":"
                        + timer.get(Calendar.MINUTE) + ":"
                        + timer.get(Calendar.SECOND) + " ("
                        + timer.get(Calendar.MILLISECOND) + ")"
                        + " ----------------------");

				}

                for (Enumeration e = akb.getRuleEnumeration(); e.hasMoreElements();)
                {
                    
					ActiveRule rule = (ActiveRule) e.nextElement();				
					// evaluate time part and add bound variables			
					ResultSet rs = rule.getTime().evaluate(akb.getKnowledgeBaseWrapper());
					boolean evaluate = false;
					if (rs==null) evaluate = true;	
					else try {			
						if (rs.first()) evaluate = true;					
					} catch (Exception ex) { }
	                if (evaluate)
	                {
						// time succeeded --> evaluate the ECA part of the rule 
	                    sheduleEventConditionAction(rule, rs);
	                }
					
                }

                try
                {
                    if (debug)
                        // needed to have time for updating the
                        // views, while debugging
                        Thread.sleep(800);
                    else
                    {
                        // check if we have a time problem
						Calendar actualTime = Calendar.getInstance();
                        timeDiff = actualTime.getTimeInMillis()
                                - currentTime.getTimeInMillis();
						
						//System.out.println(timeDiff); // processing time
						
                        if (timeDiff > 1000*interval) 
							
							
							throw new RuleDelay(
									"The Validation of the eca rules cost to much time.");
                        else
                        {
                            // not bussy wait, give processor power to action
                            // execution
							
                            Thread.sleep((1010-actualTime.get(Calendar.MILLISECOND))+((interval-1)*1000));																					
                        }
                    }

                } catch (InterruptedException ex)
                {
                    //	should not happen
					System.err.println(ex);
                }
                						

            } catch (RuleDelay ex)
            {
                
				//logger.info("", ex);

				// trigger delay handling via meta ECA rule (with SysTime event)
				akb.startSystemTime(SystemTime.onDelay);						
				currentTime = Calendar.getInstance();					
            }
            catch (Exception ex)
            {
				logger.info("", ex);
				
				// trigger delay handling via meta ECA rule (with SysTime event)
				akb.startSystemTime(SystemTime.onSystemError);
				
				currentTime = Calendar.getInstance();
            }        
		}
		
		
		
        
        akb.startSystemTime(SystemTime.onShutdown);
    }

    private void sheduleEventConditionAction(final ActiveRule rule, ResultSet rs)
    {
		EventConditionActionExecutor eventConditionAction = new EventConditionActionExecutor(
                akb, rule, rs);
        try
        {    			
			eventConditionActionPool.execute(eventConditionAction);		
        } catch (Exception ex)
        {           
			System.err.println(ex);
			logger.info("", ex);
        }
    }
    
    public void end()
    {
        doAbort = false;
    }
}

class RuleDelay extends Exception
{
    public RuleDelay(String s)
    {
        super(s);
    }

}