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

package ts.biff;


import java.awt.*;
import java.util.*;
import java.net.*;
import java.awt.event.*;

import javax.swing.*;
import javax.swing.event.*;


import ts.mail.*;
import ts.config.*;
import ts.ui.*;


/**
 * A simple dialog for sending a SMTP mail. Mail sent from this
 * dialog is not stored. The dialog send smtp mail, thats all.  The
 * dialog tries to be intelligent about sending mail. E.g. if a mail
 * has been sent and the send button is pressed again without any
 * changes in the fields, a warning appears. It will insert a
 * signature file (~/.signature) if it exist, and try to guess the
 * users from address.  The to: and from: fields are required, and the
 * message will not be sent if those fields are empty.  <P> The class
 * uses a SmtpAgent for the actual mail transport.
 *
 * The singelton pattern is used to have only one instance of this class
 * in the JVM at anytime.
 *
 * @author  Jan-Henrik Haukeland &lt;hauk@tildeslash.com&gt;
 * @version $Id: Smtp.java,v 1.20 1999/11/28 01:27:03 hauk Exp $
 * @see ts.mail.SmtpAgent 
 */

public final class Smtp extends JDialog 
    implements ActionListener, DocumentListener 
{

  /** If pressed send the composed mail */
  protected JButton  mySendButton; 

  /** If pressed clear all fields  */
  protected JButton  myClearButton; 

  protected JTextField mySubject, myTo, myCc, myFrom;
  protected JTextArea  myBody;
  protected JLabel myStatus;

  /** True if the message has been sent */
  protected boolean isSent= false;


  /** Smtp agent used to send mail */
  protected SmtpAgent smtp;


  /** Self */
  private static Smtp instance;



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


  /**
   * Instantiate the Dialog
   */
  private Smtp()
  {
    super(new JFrame(), Constant.SMTP_DIALOG_TITLE, false);
    buildUI();
    installListeners();
    installMenu();

    this.setLocation(100,100);

    addWindowListener( new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
	closeSmtp();
      }
    });
  }    


  // ---------------------- Public methods -------------------------- //


  /**
   * Returns the Smtp instance singleton
   * @return a Smtp instance singelton
   */
  public static Smtp getInstance()
  {
    if ( instance == null )
	instance= new Smtp();
    return instance;
  }



  /**
   * Show the dialog
   */
  public void showSmtp()
  {
    this.pack();
    this.show();
  }


  /**
   * Hide the dialog
   */
  public void closeSmtp()
  {
    this.setVisible(false);
    this.dispose();
  }




  /**
   * Returns true if the current message has been sent
   * @return boolean, if the message has been sent
   */
  public boolean isSent()
  {
    return isSent;
  }




  /**
   * Called if the send or clear button was clicked. The mail is
   * sent <I>iff</I> the send button receives an ActionEvent. If 
   * the clear button receives an ActionEvent, clear all fields
   * and mark mail as not sent (i.e. new mail)
   * @param e An ActionEvent
   */
  public void actionPerformed(ActionEvent e)
  {
    if ( e.getSource() == mySendButton ) {
      sendMail();
    } else {
      isSent= false;
      setStatus("");
      initFields();
    }
  }



  /** 
   * If a mail field is changed, mark mail as new 
   * @param e A DocumentEvent received when a field is changed
   */
  public void insertUpdate(DocumentEvent e)  {  isSent= false; }
  /** 
   * If a mail field is changed, mark mail as new 
   * @param e A DocumentEvent received when a field is changed
   */
  public void removeUpdate(DocumentEvent e)  {  isSent= false; }
  /** 
   * If a mail field is changed, mark mail as new 
   * @param e A DocumentEvent received when a field is changed
   */
  public void changedUpdate(DocumentEvent e) {  isSent= false; }
  



  // --------------------- Private methods ------------------------- //



  /**
   * Build the user interface
   */
  private void buildUI()
  {
    myFrom=    new JTextField(40);
    mySubject= new JTextField(40);
    myTo=      new JTextField(40); 
    myCc=      new JTextField(40); 
    myBody=    new JTextArea(20,40);
    myStatus=  new JLabel("Status: ");
    mySendButton=   new JButton("Send");
    myClearButton= new JButton("Clear");

    myBody.setLineWrap(true);
    myBody.setWrapStyleWord(true);

    GBLPanel fieldsPane= new GBLPanel();
    JPanel statusAndButtonPane= new JPanel(new BorderLayout());
    JPanel buttonPane= new JPanel(){
      public Insets getInsets() {
	return new Insets(20,0,0,0);
      }
    };
    JPanel contentPane= new JPanel(new BorderLayout()) {
      public Insets getInsets() {
	return new Insets(20,20,20,20);
      }
    };

    fieldsPane.add(new JLabel("From: "),    0,0,1,1,GBLPanel.EAST);
    fieldsPane.add(new JLabel("To: "),      0,1,1,1,GBLPanel.EAST);
    fieldsPane.add(new JLabel("Subject: "), 0,2,1,1,GBLPanel.EAST);
    fieldsPane.add(new JLabel("Cc: "),      0,3,1,1,GBLPanel.EAST);
    
    GBLPanel.constraints.fill= GridBagConstraints.HORIZONTAL;
    GBLPanel.constraints.weightx= 1.0;

    fieldsPane.add(myFrom,    1,0,GBLPanel.REST,1,GBLPanel.WEST);
    fieldsPane.add(myTo,      1,1,GBLPanel.REST,1,GBLPanel.WEST);
    fieldsPane.add(mySubject, 1,2,GBLPanel.REST,1,GBLPanel.WEST);
    fieldsPane.add(myCc,      1,3,GBLPanel.REST,1,GBLPanel.WEST);
    GBLPanel.constraints.weighty= 1.0;
    GBLPanel.constraints.fill= GridBagConstraints.BOTH;
    fieldsPane.add(myBody,    1,4,GBLPanel.REST,GBLPanel.REST);


    buttonPane.add(mySendButton);
    buttonPane.add(myClearButton);
    
    statusAndButtonPane.add("Center", buttonPane);
    statusAndButtonPane.add("South", myStatus);
    
    contentPane.add("Center",fieldsPane);
    contentPane.add("South",statusAndButtonPane);

    this.setContentPane(contentPane);

    initFields();
  }


  /**
   * Clear all fields and assign default values
   */
  private void initFields()
  {
    mySubject.setText("");
    myTo.setText("");
    myCc.setText("");
    myBody.setText(getSignature());
    myFrom.setText(getFromAddress());
  }


  /**
   * Create the menu system for this dialog
   */
  private void installMenu()
  {
    JMenuBar menuBar= new JMenuBar();
    JMenu file= buildFileMenu();
    menuBar.add(file);
    this.setJMenuBar(menuBar);   
  }
 


  /**
   * Returns the File menu
   * @return The file menu
   */
  private JMenu buildFileMenu() {
    JMenu file=     new JMenu("File");
    JMenuItem quit= new JMenuItem("Exit");
    
    quit.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
	closeSmtp();
      }});
    file.add(quit);
    return file;
  }



  /**
   * Install listeners
   */
  private void installListeners()
  {
    myFrom.getDocument().addDocumentListener(this);
    mySubject.getDocument().addDocumentListener(this);
    myTo.getDocument().addDocumentListener(this);
    myCc.getDocument().addDocumentListener(this);
    myBody.getDocument().addDocumentListener(this);
   
    mySendButton.addActionListener(this);
    myClearButton.addActionListener(this);
  }



  /**
   * Set a message in the status field
   * @param a status message
   */
  private void setStatus(String message)
  {
    myStatus.setText("Status :  "+message);
  }
    



  /**
   * Send the mail using a SmtpAgent
   */
  private void sendMail()
  {
    if ( isSent ) {
      if ( ! again() )
	  return;
    }
	
    
    try {
      
     if ( smtp == null) {
       String server= Config.get("smtp.server");
	int    port=   Config.getint("smtp.server.port");
	smtp= new SmtpAgent(server,port);
     }
     
     setWaitCursor();
     
     MailModel model= new DefaultMailModel();
     
     // Make the agent safe exit controlled while sending mail
     TerminateController.add(smtp);
     
     model.setFromField(myFrom.getText());
     model.setToField(myTo.getText());
     model.setSubjectField(mySubject.getText());
     model.setXHeader("X-Mailer:", Constant.X_MAILER );
     setCC(model);
     model.setMailBody(myBody.getText());
     
     model.validate(); 
     
     smtp.sendMail(model);
     
     if ( ! isSent )
	 setStatus("Mail successfully sent");
     else
	 setStatus("Mail successfully re-sent");
     
     isSent= true;
     
    }
    catch (Exception se) {
      String msg= "Failed to send the mail message"+
	  "\nReason: "+se.getMessage();
      
      Util.showError(this,msg);
      
      isSent= false;
      setStatus("Failed to send");
    }
    finally {
      // Remove the Agent from exit control
      TerminateController.remove(smtp);
      setDefaultCursor();
    }
  }



  /**
   * Set CC fields in the MailModel
   * @param a MailModel
   */
  private void setCC(MailModel model)
  {
    String cc= myCc.getText();
    if ( cc == null || cc.length() == 0 )
	return;
    
    StringTokenizer t;
    
    // Test for delimiter
    if ( cc.indexOf(",") > 0 )
	t= new StringTokenizer(cc, ",");
    else 
	t= new StringTokenizer(cc);
    
    while (t.hasMoreTokens()) {
      try {
	model.setCCHeader(t.nextToken());
      } catch(IllegalArgumentException ie) {
	break;
      }
    }
  }


  /**
   * Returns the users from address
   * @return users from address
   */
  private String getFromAddress()
  {
    try {
      String from= Config.get("user.name") + "@" +
	  InetAddress.getLocalHost().getHostName();
      return from;
    }catch (UnknownHostException whoops) {
      return "";
    }
  }


  
  /**
   * Returns the users signature file if any. Trying
   * "user.home".signature.
   * @return content of users signature file or an empty string if null
   */
  private String getSignature()
  {
    String sig= Util.slurp(Config.get("user.home") + 
			   Config.get("file.separator") +
			   ".signature");
    return sig != null ? "\n\n--\n"+sig : "";
  }
    

  /**
   * Ask the user if he really wants to send a message again
   * if it has already been sent.
   * @return true if send message again
   */
  private boolean again()
  {
    return Util.showYesNo(this,
			  "Warning",
			  "You have already sent this message\n"+
			  "would you like to send it again?");
  }



  private void setWaitCursor()
  {
    this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
  }


  private void setDefaultCursor()
  {
    this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
  }

}

  
