/*
 * COPYRIGHT (c) 1998-1999 Jan-Henrik Haukeland, http://www.tildeslash.com/
 */

package ts.biff;

import java.awt.Toolkit;
import java.util.*;
import javax.swing.event.*;

import ts.config.Constant;
import ts.config.Config;
import ts.config.ConfigException;
import ts.mail.FetchAgent;
import ts.mail.MailModel;
import ts.mail.GetMailException;
import ts.event.MailEvent;
import ts.event.MailListener;


/**
 * This class uses a FetchAgent e.g. a POP3 agent to repeatly scan
 * a users mailbox for changes.  When there is a change in the
 * mailbox, this controller will notify any MailEvent listeners by
 * firing a MailEvent. 
 *
 * @author  Jan-Henrik Haukeland
 * @version $Id: Controller.java,v 1.21 2001/08/13 12:48:28 hauk Exp $
 *
 * @see Model 
 */
public class Controller extends Thread 
{

  /** The Fetch Agent used to scan mailbox messages */
  protected FetchAgent myAgent;

  /** A list holding MailListeners */
  protected EventListenerList   listenerList= new EventListenerList();

  /** A vector holding mail fields of interest */
  protected Vector myMailFields;

  /** A vector holding "raw" MailModel objects from last scan */
  protected Vector myMailModels;


  // --------------------- Constructors -------------------------- //


  /**
   * Instantiate the controller.
   * @param anAgent A FetchAgent that is used to retrieve mail messages
   */
  public Controller(FetchAgent anAgent) 
  {
    myAgent= anAgent;
    this.setPriority(Thread.NORM_PRIORITY-1);
    start();
  }



  /**
   * Stop this thread
   */
  public void terminate()
  {
    // Stop any agent scan first
    myAgent.stopScan();
    this.stop();
  }


  /**
   * Use the FetchAgent to scan for new Mail messages in a 
   * low priority Threaded loop
   */
  public void run()
  {
    for(;;) 
    {
      try {
	myMailModels= myAgent.getMail();
	handleMessages(myMailModels);
	sleep(Config.getint("update")*1000);
      }
      catch (GetMailException ge) {
	if ( ge.getStatus() == GetMailException.ERROR ) {
	  handleException(ge);
	}
	if ( ge.getStatus() == GetMailException.WARNING ) {
	  //System.err.println("Info: "+ge.getMessage());
	}
	try {
	  sleep(Config.getint("update")*1000);
	}catch (InterruptedException e) {}
      }
      catch (InterruptedException e) { /* Ring! */ }
      catch (Exception a) {
	handleException(a);
      }
    }
  }


  /**
   * Interrupt the sleep between each mail scan
   */
  public void wakeUp()
  {
    this.interrupt();
  }


  /**
   * Returns a vector with mail messages from the last scan
   * @return a Vector with mail messages
   */
  public Vector getMail()
  {
    return myMailFields;
  }


  
  /**
   * Returns the MailModel with the submitted id
   * @param id a Mail message id
   * @return a MailModel for the submitted id or null if not found
   */
  public MailModel getMailModel(String id)
  {
    if ( myMailModels == null )
	return (MailModel)null;
    
    for ( Enumeration e= myMailModels.elements(); e.hasMoreElements();) {
      MailModel m= (MailModel)e.nextElement();
      if ( m.getIdField().equals(id) )
	  return m;
    }
    
    return (MailModel)null;
  }


  // ------------------- handle Mail events -------------------- //
 
  
  /**
   * Adds a MailListener. 
   *
   * @param aMailListener A mail listener object
   */
  public void addMailListener(MailListener aMailListener) 
  {
    listenerList.add(MailListener.class, aMailListener);
  }
  

  /**
   * Removes a MailListener
   *
   * @param aMailListener An mail listener object
   */
  public void removeMailListener(MailListener aMailListener) 
  {
    listenerList.remove(MailListener.class, aMailListener);
  }


  /*
   * Notify all listeners that have registered interest for
   * notification on this event type. 
   * @param m A mail event
   * @see EventListenerList
   */
  protected void fireMailEvent(MailEvent m)
  {
    class Fire implements Runnable
    {
      MailEvent m;

      public Fire(MailEvent m) { this.m= m; }

      public void run()
      {
	Object[] listeners = listenerList.getListenerList();
	for (int i = listeners.length-2; i>=0; i-=2) {
	  if (listeners[i]==MailListener.class) {
	    ((MailListener)listeners[i+1]).mailEvent(m);
	  }          
	}
      }
    }
    
    
    (new Thread(new Fire(m))).start();
    
  }    


  // ---------------------------------------------------------------- //


  /**
   * Fire a new MailEvent if and only if the new mail vector
   * is different from the previous one.
   * @param v a Vector with mail models from the latest scan
   */
  protected void handleMessages(Vector v)
  {
    Vector m= MailUtil.getMail(v);
  
    int newSize= m == null ? 0 : m.size();
    int oldSize= myMailFields == null ? 0 : myMailFields.size();

    if ( newSize == 0 && oldSize == 0 )
	return;
  
    boolean fire= false;
    boolean beep= false;

    // New Mail
    if ( newSize > oldSize ) {
      beep= true;
      fire= true;
    }
    // Changed mailbox. User read since last scan, but still, 
    // possible new mail
    else if ( newSize < oldSize ){
      if ( newSize != 0 ) { 
	beep= true; // Assume if not completely empty -> new mail.
      }
      fire= true;
    }
    // Same number of messages, but possible n read and n new mail 
    // between scan. Rare, but possible. 
    //   We assume here that the user will start reading the first
    //   message first and so on. Therefor we will only test for 
    //   difference between the first elements in the two vectors. 
    else {
      // At this point both vectors should guaranteed be none null
      String newId= (String)((Vector)m.elementAt(0)).elementAt(0);
      String oldId= (String)((Vector)myMailFields.elementAt(0)).elementAt(0);
      if ( ! newId.equals(oldId) ) {
	fire= true;
	beep= true;
      }
    }

    myMailFields= m;

    if ( fire )
	fireMailEvent(new MailEvent(this, newSize));
     
    if ( beep && Config.getboolean("beep") )	
	Util.beep();		
    
  }


  /**
   * Show an exception in a dialog and ask the user if he wants to continue
   * 
   * @param e exception to display
   */
  private void handleException(Exception e)
  {
    Util.beep();
    if (Util.showYesNo("Error",
		       "Got exception: \n"+
		       e.getMessage()+
		       "\nWould you like to exit tsbiff? \n")) {
      TerminateController.exit(1);
    }
  }


}
