/*
 * 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 Eric J. Bowersox/Silverwrist Design Studios.  All Rights Reserved.
 * 
 * Contributor(s): 
 */
package com.silverwrist.dynamo.mail;

import java.io.*;
import java.util.*;
import javax.mail.*;
import javax.mail.internet.*;
import org.apache.log4j.Logger;
import org.apache.velocity.*;
import com.silverwrist.dynamo.except.*;
import com.silverwrist.dynamo.iface.*;

class MailMessageImpl implements MailMessage
{
  /*--------------------------------------------------------------------------------
   * Static data members
   *--------------------------------------------------------------------------------
   */

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

  private static final InternetAddress[] ADDR_TEMPLATE = new InternetAddress[0];

  private static final Message.RecipientType[] TYPE_VALS = {
      Message.RecipientType.TO,
      Message.RecipientType.CC,
      Message.RecipientType.BCC
  };

  private static final Set VERBOTEN_HEADERS;

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

  private MailMessageImpl m_parent = null;    // "parent" (template) mail message
  private MailSubSystem m_subsystem;          // backreference to subsystem
  private MimeMessage m_msg;                  // the MimeMessage we're working with
  private boolean m_user_flag;                // user flag for message
  private ArrayList[] m_recipients;           // all recipients
  private String m_subject = null;            // subject (may be template)
  private String m_text = null;               // message text
  private String m_text_resource = null;      // resource name for text
  private VelocityContext m_vars = null;      // holds variables for Velocity replacement
  private HashMap m_headers;                  // holds header values

  /*--------------------------------------------------------------------------------
   * Constructors
   *--------------------------------------------------------------------------------
   */

  MailMessageImpl(MailSubSystem subsystem, MimeMessage msg, boolean user)
  {
    m_subsystem = subsystem;
    m_msg = msg;
    m_user_flag = user;
    m_recipients = new ArrayList[3];
    for (int i=0; i<m_recipients.length; i++)
      m_recipients[i] = new ArrayList();
    m_headers = new HashMap();

  } // end constructor

  MailMessageImpl(MailMessageImpl parent, MimeMessage msg)
  {
    m_parent = parent;
    m_subsystem = parent.m_subsystem;
    m_msg = msg;
    m_user_flag = parent.m_user_flag;
    m_recipients = new ArrayList[3];
    for (int i=0; i<m_recipients.length; i++)
      m_recipients[i] = (ArrayList)(parent.m_recipients[i].clone());
    m_subject = parent.m_subject;
    m_text = parent.m_text;
    m_text_resource = parent.m_text_resource;
    if (parent.m_vars!=null)
      m_vars = (VelocityContext)(parent.m_vars.clone());
    m_headers = (HashMap)(parent.m_headers.clone());

  } // end constructor

  /*--------------------------------------------------------------------------------
   * Internal operations
   *--------------------------------------------------------------------------------
   */

  private static final InternetAddress createAddress(String addr, String personal) throws MailException
  {
    InternetAddress rc = null;
    try
    { // create the address object!
      rc = new InternetAddress(addr,true);
      if (personal!=null)
	rc.setPersonal(personal);
      return rc;

    } // end try
    catch (AddressException e)
    { // the address parse failed!
      MailException me = new MailException(MailMessageImpl.class,"MailMessages","addr.invalid",e);
      me.setParameter(0,addr);
      throw me;

    } // end catch
    catch (UnsupportedEncodingException e)
    { // this does not make sense
      logger.error("UnsupportedEncodingException thrown in createAddress() where it shouldn't be",e);
      return rc;

    } // end catch

  } // end createAddress

  private final void checkType(int type) throws MailException
  {
    if ((type<0) || (type>=m_recipients.length))
    { // throw a mail exception!
      MailException me = new MailException(MailMessageImpl.class,"MailMessages","index.invalid");
      me.setParameter(0,String.valueOf(type));
      throw me;

    } // end if

  } // end checkType

  private final boolean badHeader(String name)
  {
    if (VERBOTEN_HEADERS.contains(name.toLowerCase()))
      return true;

    if (m_user_flag)
    { // test additional headers
      if (name.toLowerCase().startsWith("x-disclaimer-"))
	return true;
      if (name.equalsIgnoreCase(m_subsystem.getUserInfoHeader()))
	return true;

    } // end if

    return false;

  } // end badHeader

  /*--------------------------------------------------------------------------------
   * Implementations from interface MailMessage
   *--------------------------------------------------------------------------------
   */

  public void send() throws MailException
  {
    try
    { // configure and send the mail message!
      for (int i=0; i<m_recipients.length; i++)
      { // set the recipient lists for the message
	if (m_recipients[i].isEmpty())
	  m_msg.setRecipients(TYPE_VALS[i],ADDR_TEMPLATE);
	else
	{ // get the addresses as an array and add them
	  InternetAddress[] list = (InternetAddress[])(m_recipients[i].toArray(ADDR_TEMPLATE));
	  m_msg.setRecipients(TYPE_VALS[i],list);

	} // end else

      } // end for

      if (m_subject!=null)
      { // set the message subject
	if (m_vars==null)
	  m_msg.setSubject(m_subject);
	else
	  m_msg.setSubject(m_subsystem.formatStringTemplate(m_vars,m_subject));

      } // end if

      StringBuffer my_text = new StringBuffer();
      if (m_text!=null)
      { // set the message text
	if (m_vars==null)
	  my_text.append(m_text);
	else
	  my_text.append(m_subsystem.formatStringTemplate(m_vars,m_text));

      } // end if
      else if (m_text_resource!=null)
      { // load template from resources and format it
	VelocityContext vars = m_vars;
	if (vars==null)
	  vars = new VelocityContext();
	my_text.append(m_subsystem.formatResourceTemplate(vars,m_text_resource));

      } // end else if

      String sig = m_subsystem.getSignature();
      if (sig!=null)
	my_text.append(sig);
      m_msg.setText(my_text.toString());

      // Add headers to the message.
      Iterator it = m_headers.keySet().iterator();
      while (it.hasNext())
      { // load each header in turn
	String hdr = (String)(it.next());
	m_msg.setHeader(hdr,(String)(m_headers.get(hdr)));

      } // end while

      // Now send the message!
      Transport.send(m_msg);

    } // end try
    catch (SendFailedException e)
    { // send failed - throw exception
      MailException me = new MailException(MailMessageImpl.class,"MailMessages","send.failed",e);
      me.setParameter(0,e.getMessage());
      throw me;

    } // end catch
    catch (MessagingException e)
    { // huh?  this should not be throwing here...
      logger.warn("MessagingException thrown in send() where it shouldn't be",e);

    } // end catch
    finally
    { // erase the headers
      try
      { // erase any headers that were set
	Iterator it = m_headers.keySet().iterator();
	while (it.hasNext())
	  m_msg.removeHeader((String)(it.next()));

      } // end try
      catch (Exception e)
      { // do nothing here
      } // end catch

    } // end finally

  } // end send

  public void reset()
  {
    if (m_parent!=null)
    { // re-clone the parent fields
      for (int i=0; i<m_recipients.length; i++)
	m_recipients[i] = (ArrayList)(m_parent.m_recipients[i].clone());
      m_subject = m_parent.m_subject;
      m_text = m_parent.m_text;
      m_text_resource = m_parent.m_text_resource;
      if (m_parent.m_vars==null)
	m_vars = null;
      else
	m_vars = (VelocityContext)(m_parent.m_vars.clone());
      m_headers = (HashMap)(m_parent.m_headers.clone());

    } // end if
    else
    { // reset everything to zero
      for (int i=0; i<m_recipients.length; i++)
	m_recipients[i].clear();
      m_subject = null;
      m_text = null;
      m_text_resource = null;
      m_vars = null;
      m_headers.clear();

    } // end else

  } // end reset

  public Object addRecipient(int type, String address) throws MailException
  {
    checkType(type);
    InternetAddress iaddr = createAddress(address,null);
    m_recipients[type].add(iaddr);
    return iaddr;

  } // end addRecipient

  public Object addRecipient(int type, String address, String personal) throws MailException
  {
    checkType(type);
    InternetAddress iaddr = createAddress(address,personal);
    m_recipients[type].add(iaddr);
    return iaddr;

  } // end addRecipient

  public void removeRecipient(Object token)
  {
    for (int i=0; i<m_recipients.length; i++)
    { // hunt in all lists till we find the object
      if (m_recipients[i].contains(token))
	m_recipients[i].remove(token);

    } // end for

  } // end removeRecipient

  public void clearRecipients(int type) throws MailException
  {
    checkType(type);
    m_recipients[type].clear();

  } // end clearRecipients

  public void setSubject(String subject)
  {
    m_subject = subject;

  } // end setSubject

  public void setText(String text)
  {
    m_text = text;
    m_text_resource = null;

  } // end setText

  public void loadTextTemplate(String resource_name)
  {
    m_text = null;
    m_text_resource = resource_name;

  } // end loadTextTemplate

  public Object setVariable(String name, Object value)
  {
    if (m_vars==null)
      m_vars = new VelocityContext();
    return m_vars.put(name,value);

  } // end setVariable

  public Object removeVariable(String name)
  {
    if (m_vars!=null)
      return m_vars.remove(name);
    else
      return null;

  } // end removeVariable

  public void addHeader(String name, String value) throws MailException
  {
    if (badHeader(name))
    { // this header is bad!
      MailException me = new MailException(MailMessageImpl.class,"MailMessages","hdr.noAdd");
      me.setParameter(0,name);
      throw me;

    } // end if

    m_headers.put(name,value);

  } // end addHeader

  public void removeHeader(String name) throws MailException
  {
    if (badHeader(name))
    { // this header is bad!
      MailException me = new MailException(MailMessageImpl.class,"MailMessages","hdr.noRemove");
      me.setParameter(0,name);
      throw me;

    } // end if

    m_headers.remove(name);

  } // end removeHeader

  public MailMessage createNew()
  {
    MimeMessage new_msg = m_subsystem.copyMessage(m_msg,m_user_flag);
    return new MailMessageImpl(this,new_msg);

  } // end createNew

  /*--------------------------------------------------------------------------------
   * Static initializer
   *--------------------------------------------------------------------------------
   */

  static
  { // Initialize the sets of forbidden headers.
    HashSet tmp = new HashSet();
    tmp.add("from");
    tmp.add("to");
    tmp.add("cc");
    tmp.add("bcc");
    tmp.add("date");
    tmp.add("subject");
    tmp.add("sender");
    tmp.add("x-mailer");
    VERBOTEN_HEADERS = Collections.unmodifiableSet(tmp);

  } // end static initializer

} // end class MailMessageImpl
