/*
 * The contents of this file are subject to the Mozilla Public License Version 1.1
 * (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.mozilla.org/MPL/>.
 * 
 * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
 * WARRANTY OF ANY KIND, either express or implied. See the License for the specific
 * language governing rights and limitations under the License.
 * 
 * The Original Code is the Venice Web Communities System.
 * 
 * The Initial Developer of the Original Code is Eric J. Bowersox <erbo@silcom.com>,
 * for Silverwrist Design Studios.  Portions created by Eric J. Bowersox are
 * Copyright (C) 2002-03 Eric J. Bowersox/Silverwrist Design Studios.  All Rights Reserved.
 * 
 * Contributor(s): 
 */
package com.silverwrist.dynamo.app;

import java.util.*;
import org.apache.log4j.Logger;
import com.silverwrist.dynamo.iface.*;
import com.silverwrist.dynamo.util.*;

/**
 * A class used internally by the {@link com.silverwrist.dynamo.app.ApplicationContainer ApplicationContainer}
 * to handle background execution of tasks.  It implements the
 * {@link com.silverwrist.dynamo.iface.BackgroundScheduler BackgroundScheduler} service.
 *
 * @author Eric J. Bowersox &lt;erbo@silcom.com&gt;
 * @version X
 */
class BackgroundProcessor implements BackgroundScheduler, ComponentShutdown
{
  /*--------------------------------------------------------------------------------
   * Internal thread class for scheduling normal-priority background tasks
   *--------------------------------------------------------------------------------
   */

  private class NormalPriorityThread extends Thread
  {
    /*====================================================================
     * Constructor
     *====================================================================
     */

    NormalPriorityThread(int index)
    {
      super("BG-Normal-" + index);
      setPriority(NORM_PRIORITY);

    } // end constructor

    /*====================================================================
     * Overrides from class Thread
     *====================================================================
     */

    public void run()
    {
      while (m_running)
      { // get tasks then execute them
	BackgroundTask task = null;
	synchronized (m_normal_sync)
	{ // test and remove from normal queue
	  boolean get_task = true;
	  while (get_task && m_normal_queue.isEmpty())
	  { // nothing to run right now...
	    try
	    { // sleep waiting for a new background task
	      m_normal_sync.wait();

	    } // end try
	    catch (InterruptedException e)
	    { // don't bother getting a task
	      get_task = false;

	    } // end catch

	  } // end if

	  if (get_task)
	    task = (BackgroundTask)(m_normal_queue.removeFirst());

	} // end synchronized block

	if (task!=null)
	{ // found a task to run, now execute it
	  try
	  { // run the task
	    task.run(m_services);
	    task = null;

	  } // end try
	  catch (Exception e)
	  { // whoops!  don't let this stop us
	    logger.warn("BackgroundTask threw exception",e);

	  } // end catch

	} // end if

      } // end while

    } // end run

  } // end class NormalPriorityThread

  /*--------------------------------------------------------------------------------
   * Internal thread class for scheduling low-priority background tasks
   *--------------------------------------------------------------------------------
   */

  private class LowPriorityThread extends Thread
  {
    /*====================================================================
     * Constructor
     *====================================================================
     */

    LowPriorityThread(int index)
    {
      super("BG-Low-" + index);
      setPriority(MIN_PRIORITY);

    } // end constructor

    /*====================================================================
     * Overrides from class Thread
     *====================================================================
     */

    public void run()
    {
      while (m_running)
      { // get tasks then execute them
	BackgroundTask task = null;
	synchronized (m_lp_sync)
	{ // test and remove from normal queue
	  boolean get_task = true;
	  while (get_task && m_lp_queue.isEmpty())
	  { // nothing to run right now...
	    try
	    { // sleep waiting for a new background task
	      m_lp_sync.wait();

	    } // end try
	    catch (InterruptedException e)
	    { // don't bother getting a task
	      get_task = false;

	    } // end catch

	  } // end if

	  // Get the task we waited for.
	  if (get_task)
	    task = (BackgroundTask)(m_lp_queue.removeFirst());

	} // end synchronized block

	if (task!=null)
	{ // found a task to run, now execute it
	  try
	  { // run the task
	    task.run(m_services);
	    task = null;

	  } // end try
	  catch (Exception e)
	  { // whoops!  don't let this stop us
	    logger.warn("BackgroundTask threw exception",e);

	  } // end catch

	} // end if

      } // end while

    } // end run

  } // end class LowPriorityThread

  /*--------------------------------------------------------------------------------
   * Internal class for timer operations
   *--------------------------------------------------------------------------------
   */

  private class MyTimerTask extends TimerTask implements ComponentShutdown
  {
    /*====================================================================
     * Attributes
     *====================================================================
     */

    private BackgroundTask m_task;

    /*====================================================================
     * Constructor
     *====================================================================
     */

    MyTimerTask(BackgroundTask task)
    {
      super();
      m_task = task;

    } // end constructor

    /*====================================================================
     * Overrides from class TimerTask
     *====================================================================
     */

    public void run()
    {
      try
      { // run the task
	m_task.run(m_services);

      } // end try
      catch (Exception e)
      { // whoops!  don't let this stop us
	logger.warn("BackgroundTask threw exception",e);

      } // end catch

    } // end run

    /*====================================================================
     * Implementations from interface ComponentShutdown
     *====================================================================
     */

    /**
     * Shuts down the component associated with this interface, in a component-specific manner.
     */
    public void shutdown()
    {
      cancel();

    } // end shutdown

  } // end class MyTimerTask

  /*--------------------------------------------------------------------------------
   * Static data members
   *--------------------------------------------------------------------------------
   */

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

  /*--------------------------------------------------------------------------------
   * Attributes
   *--------------------------------------------------------------------------------
   */

  private ServiceProvider m_services;
  private volatile boolean m_running = true;
  private Object m_normal_sync = new Object();
  private LinkedList m_normal_queue = new LinkedList();
  private Thread[] m_normal_threads;
  private Object m_lp_sync = new Object();
  private LinkedList m_lp_queue = new LinkedList();
  private Thread[] m_lp_threads;
  private Timer m_timer;

  /*--------------------------------------------------------------------------------
   * Constructor
   *--------------------------------------------------------------------------------
   */

  /**
   * Constructs the instance of the <CODE>BackgroundProcessor</CODE> internal to the
   * {@link com.silverwrist.dynamo.app.ApplicationContainer ApplicationContainer}.
   *
   * @param num_normal_threads Number of "normal" threads in our runtime thread pool.
   * @param num_lp_threads Number of "low priority" threads in our runtime thread pool.
   * @param services The {@link com.silverwrist.dynamo.iface.ServiceProvider ServiceProvider} to be supplied
   *                 to all {@link com.silverwrist.dynamo.iface.BackgroundTask BackgroundTask}s.
   */
  BackgroundProcessor(int num_norm_threads, int num_lp_threads, ServiceProvider services)
  {
    m_services = new SingletonServiceProvider("BackgroundProcessor",services,BackgroundScheduler.class,this);

    // Create the normal priority background threads.
    m_normal_threads = new Thread[num_norm_threads];
    int i;
    for (i=0; i<num_norm_threads; i++)
      m_normal_threads[i] = new NormalPriorityThread(i);

    // Create the low priority background threads.
    m_lp_threads = new Thread[num_lp_threads];
    for (i=0; i<num_lp_threads; i++)
      m_lp_threads[i] = new LowPriorityThread(i);

    // Create the timer.
    m_timer = new Timer();

    // Start everything up.
    for (i=0; i<num_norm_threads; i++)
      m_normal_threads[i].start();
    for (i=0; i<num_lp_threads; i++)
      m_lp_threads[i].start();

  } // end constructor

  /*--------------------------------------------------------------------------------
   * Implementations from interface BackgroundScheduler
   *--------------------------------------------------------------------------------
   */

  /**
   * Schedule a task to be run "later," i.e., at any time after the present time, without interrupting the
   * current thread's processing.  Background processing of tasks submitted by this method are handled by a
   * pool of threads.
   *
   * @param task The task to be scheduled to run later.
   * @param low_priority If this is <CODE>true</CODE>, the task is handled by a pool of low-priority threads
   *                     instead of by a pool of normal-priority threads.  Low-priority threads will generally
   *                     execute only after normal-priority threads, but are not guaranteed to.
   */
  public void runTaskLater(BackgroundTask task, boolean low_priority)
  {
    if (low_priority)
    { // add to low priority queue
      synchronized (m_lp_sync)
      { // add item and notify a thread to wake up
	m_lp_queue.addLast(task);
	m_lp_sync.notify();

      } // end synchronized block

    } // end if
    else
    { // add to normal queue
      synchronized (m_normal_sync)
      { // add item and notify a thread to wake up
	m_normal_queue.addLast(task);
	m_normal_sync.notify();

      } // end synchronized block

    } // end else

  } // end runTaskLater

  /**
   * Schedule a task to be run on a one-shot basis after a delay.
   *
   * @param task The task to be scheduled to run.
   * @param delay The delay, in milliseconds, before this task is to be run.  The actual delay may be longer
   *              than this.
   * @return An instance of a {@link com.silverwrist.dynamo.iface.ComponentShutdown ComponentShutdown} object,
   *         whose {@link com.silverwrist.dynamo.iface.ComponentShutdown#shutdown() shutdown()} method may be
   *         called to abort the execution of this task at any time before it is actually run.  If this is called
   *         after the task is run, it has no effect.
   */
  public ComponentShutdown runTaskAfter(BackgroundTask task, long delay)
  {
    MyTimerTask my_task = new MyTimerTask(task);
    m_timer.schedule(my_task,delay);
    return my_task;

  } // end runTaskAfter

  /**
   * Schedule a task to be run on a one-shot basis at a certain time.
   *
   * @param task The task to be scheduled to run.
   * @param when The date/time at which the task is to be run.  The actual task start time may be any time
   *             after this.
   * @return An instance of a {@link com.silverwrist.dynamo.iface.ComponentShutdown ComponentShutdown} object,
   *         whose {@link com.silverwrist.dynamo.iface.ComponentShutdown#shutdown() shutdown()} method may be
   *         called to abort the execution of this task at any time before it is actually run.  If this is called
   *         after the task is run, it has no effect.
   */
  public ComponentShutdown runTaskAt(BackgroundTask task, Date when)
  {
    MyTimerTask my_task = new MyTimerTask(task);
    m_timer.schedule(my_task,when);
    return my_task;

  } // end runTaskAt

  /**
   * Schedule a task to be run periodically, after a delay.
   *
   * @param task The task to be scheduled to run.
   * @param delay The delay, in milliseconds, before this task is to be run for the first time.  The actual
   *              delay may be longer than this.
   * @param period The time, in milliseconds, between successive executions of the task.  The task will be
   *               scheduled for repeated execution, delaying at least this long between executions.
   * @return An instance of a {@link com.silverwrist.dynamo.iface.ComponentShutdown ComponentShutdown} object,
   *         whose {@link com.silverwrist.dynamo.iface.ComponentShutdown#shutdown() shutdown()} method may be
   *         called to abort the periodic execution of this task at any time.
   */
  public ComponentShutdown runTaskPeriodic(BackgroundTask task, long delay, long period)
  {
    MyTimerTask my_task = new MyTimerTask(task);
    m_timer.schedule(my_task,delay,period);
    return my_task;

  } // end runTaskPeriodic

  /**
   * Schedule a task to be run periodically, beginning at a specified time.
   *
   * @param task The task to be scheduled to run.
   * @param when The date/time at which the task is to be run for the first time.  The actual task start time
   *             may be any time after this.
   * @param period The time, in milliseconds, between successive executions of the task.  The task will be
   *               scheduled for repeated execution, delaying at least this long between executions.
   * @return An instance of a {@link com.silverwrist.dynamo.iface.ComponentShutdown ComponentShutdown} object,
   *         whose {@link com.silverwrist.dynamo.iface.ComponentShutdown#shutdown() shutdown()} method may be
   *         called to abort the periodic execution of this task at any time.
   */
  public ComponentShutdown runTaskPeriodic(BackgroundTask task, Date start, long period)
  {
    MyTimerTask my_task = new MyTimerTask(task);
    m_timer.schedule(my_task,start,period);
    return my_task;

  } // end runTaskPeriodic

  /**
   * Schedule a task to be run periodically, after a delay.
   *
   * @param task The task to be scheduled to run.
   * @param delay The delay, in milliseconds, before this task is to be run for the first time.  The actual
   *              delay may be longer than this.
   * @param period The time, in milliseconds, between successive executions of the task.  The task will be
   *               scheduled for repeated execution, with the task being started with roughly this many
   *               milliseconds between each start attempt.  If execution is delayed, this task will try to
   *               "catch up" by scheduling executions more frequently.
   * @return An instance of a {@link com.silverwrist.dynamo.iface.ComponentShutdown ComponentShutdown} object,
   *         whose {@link com.silverwrist.dynamo.iface.ComponentShutdown#shutdown() shutdown()} method may be
   *         called to abort the periodic execution of this task at any time.
   */
  public ComponentShutdown runTaskFixedRate(BackgroundTask task, long delay, long period)
  {
    MyTimerTask my_task = new MyTimerTask(task);
    m_timer.scheduleAtFixedRate(my_task,delay,period);
    return my_task;

  } // end runTaskFixedRate

  /**
   * Schedule a task to be run periodically, beginning at a specified time.
   *
   * @param task The task to be scheduled to run.
   * @param when The date/time at which the task is to be run for the first time.  The actual task start time
   *             may be any time after this.
   * @param period The time, in milliseconds, between successive executions of the task.  The task will be
   *               scheduled for repeated execution, with the task being started with roughly this many
   *               milliseconds between each start attempt.  If execution is delayed, this task will try to
   *               "catch up" by scheduling executions more frequently.
   * @return An instance of a {@link com.silverwrist.dynamo.iface.ComponentShutdown ComponentShutdown} object,
   *         whose {@link com.silverwrist.dynamo.iface.ComponentShutdown#shutdown() shutdown()} method may be
   *         called to abort the periodic execution of this task at any time.
   */
  public ComponentShutdown runTaskFixedRate(BackgroundTask task, Date start, long period)
  {
    MyTimerTask my_task = new MyTimerTask(task);
    m_timer.scheduleAtFixedRate(my_task,start,period);
    return my_task;

  } // end runTaskFixedRate

  /*--------------------------------------------------------------------------------
   * Implementations from interface ComponentShutdown
   *--------------------------------------------------------------------------------
   */

  /**
   * Shuts down the <CODE>BackgroundProcessor</CODE>, stopping all its associated threads and tasks.
   */
  public void shutdown()
  {
    m_timer.cancel();
    m_running = false;
    int i;
    for (i=0; i<m_normal_threads.length; i++)
      m_normal_threads[i].interrupt();
    for (i=0; i<m_lp_threads.length; i++)
      m_lp_threads[i].interrupt();

  } // end shutdown

} // end class BackgroundProcessor
