/*
 * Copyright (C) The Apache Software Foundation. All rights reserved.
 *
 * This software is published under the terms of the Apache Software License
 * version 1.1, a copy of which has been included with this distribution in
 * the LICENSE file.
 */
package org.apache.james.transport.mailets;

import java.util.* ;
import java.io.* ;
import java.sql.* ;

import org.apache.avalon.cornerstone.services.datasource.DataSourceSelector;
import org.apache.avalon.excalibur.datasource.DataSourceComponent;
import org.apache.avalon.framework.component.ComponentException;
import org.apache.avalon.framework.component.ComponentManager;
import org.apache.avalon.framework.configuration.DefaultConfiguration;
import org.apache.james.Constants;
import org.apache.james.core.MailImpl;
import org.apache.james.services.MailRepository;
import org.apache.james.services.MailStore;
import org.apache.mailet.GenericMailet;
import org.apache.mailet.Mail;

/**
 * Stores incoming Mail in the specified Repository.
 * If the "passThrough" in confs is true the mail will be returned untouched in
 * the pipe. If false will be destroyed.
 * @version 1.0.0, 24/04/1999
 * @author  Federico Barbieri <scoobie@pop.systemy.it>
 *
 * This is $Revision: 1.4 $
 * Committed on $Date: 2002/01/18 02:48:38 $ by: $Author: darrell $
 */
public class JDBCTokenCounter
       extends GenericMailet
{
  private DataSourceComponent datasource ;
  private Hashtable words = new Hashtable () ;

  private String repositoryPath ;
  private String repositoryTable ;
  private String statisticTable ;
  private String statisticField ;

  private int messageCount = 0 ;

  public void init ()
  {
    String statisticPath = getInitParameter ("statisticPath") ;
    repositoryPath = getInitParameter ("repositoryPath") ;

    try
    {
      ComponentManager componentManager = (ComponentManager) getMailetContext().getAttribute (Constants.AVALON_COMPONENT_MANAGER) ;

      // Get the DataSourceSelector block
      DataSourceSelector datasources = (DataSourceSelector) componentManager.lookup (DataSourceSelector.ROLE) ;

      // Get the data-source required.
      int stindex =   repositoryPath.indexOf ("://")
                    + 3 ;
      int endindex = repositoryPath.indexOf ("/",
                                             stindex) ;
      String datasourceName = repositoryPath.substring (stindex,
                                                        endindex) ;
      datasource = (DataSourceComponent) datasources.select (datasourceName) ;

      stindex =   repositoryPath.lastIndexOf ("/")
                + 1 ;
      repositoryTable = repositoryPath.substring (stindex) ;

      stindex =   statisticPath.lastIndexOf ("/")
                + 1 ;
      statisticTable = statisticPath.substring (0,
                                                  stindex
                                                - 1) ;

      statisticField = statisticPath.substring (stindex) ;
    }
    catch (Exception e)
    {
      log ("can't get datasource ") ;
    }

    try
    {
      loadTokens () ;
    }
    catch (java.sql.SQLException se)
    {
      log (   "(loadTokens) SQL Exception: "
            + se.getMessage ()) ;
    }
  }

  private void loadTokens ()
          throws java.sql.SQLException
  {
    String selectWords =   "SELECT WORD, OCCURANCES FROM "
                         + repositoryTable ;
    String selectStatistic =   "SELECT "
                             + statisticField
                             + " FROM "
                             + statisticTable ;

    Connection conn = null ;
    PreparedStatement pstmt = null ;
    ResultSet rs = null ;

    try
    {
      conn = datasource.getConnection () ;
      pstmt = conn.prepareStatement (selectWords) ;
      rs = pstmt.executeQuery () ;

      while (rs.next ())
        words.put (rs.getString (1),
                   new Integer (rs.getInt (2))) ;

      System.out.println (words.size () + " words loaded") ;

      pstmt = conn.prepareStatement (selectStatistic) ;
      rs = pstmt.executeQuery () ;
      if (rs.next ())
        messageCount = rs.getInt (1) ;
    }
    finally
    {
      if (rs != null)
        try
        {
          rs.close () ;
          rs = null ;
        }
        catch (java.sql.SQLException se)
        {
        }

      if (pstmt != null)
        try
        {
          pstmt.close () ;
          pstmt = null ;
        }
        catch (java.sql.SQLException se)
        {
        }

      conn = null ;
    }
  }

  private void saveTokens ()
          throws java.sql.SQLException
  {
    String delete =   "DELETE FROM "
                    + repositoryTable ;
    String insert =   "INSERT INTO "
                    + repositoryTable
                    + " (WORD, OCCURANCES) VALUES (?,?)" ;
    String updateStatistic =   "UPDATE "
                             + statisticTable
                             + " SET "
                             + statisticField
                             + " = ?" ;

    Connection conn = null ;
    PreparedStatement pstmt = null ;
    ResultSet rs = null ;

    try
    {
      conn = datasource.getConnection () ;

      //Delete all existing entries.
      pstmt = conn.prepareStatement (delete) ;
      pstmt.executeUpdate () ;

      if (!conn.getAutoCommit ())
        conn.commit () ;

      pstmt.close () ;

      pstmt = conn.prepareStatement (insert) ;

      //Insert new entries.
      log ("Saving " + words.size() + " words...") ;
      Enumeration e = words.keys () ;
      while (e.hasMoreElements ())
      {
        String key = (String) e.nextElement () ;

        pstmt.setString (1,
                         key) ;
        pstmt.setInt (2,
                      ((Integer) words.get (key)).intValue ()) ;

        pstmt.executeUpdate () ;
      }

      if (!conn.getAutoCommit ())
        conn.commit () ;

      pstmt.close () ;

      //Update statistic (message count).
      log ("Saving messageCounter") ;
      pstmt = conn.prepareStatement (updateStatistic) ;
      pstmt.setInt (1,
                    messageCount) ;

      pstmt.executeUpdate () ;

      if (!conn.getAutoCommit ())
        conn.commit () ;
    }
    finally
    {
      if (pstmt != null)
        try
        {
          pstmt.close () ;
          pstmt = null ;
        }
        catch (java.sql.SQLException se)
        {
        }

      conn = null ;
    }
  }

  public void destroy ()
  {
    System.out.println ("JDBCTokenCounter: Saving word/counts...") ;

    try
    {
      saveTokens () ;
    }
    catch (java.sql.SQLException se)
    {
      log ("(saveTokens) SQL Exception: " + se.getMessage ()) ;
    }

    super.destroy () ;
  }

  public void service (Mail genericmail)
  {
     MailImpl mail = (MailImpl) genericmail ;

     System.out.println ("JDBCTokenCounter: service") ;
     ByteArrayOutputStream baos = new ByteArrayOutputStream () ;

     try
     {
       mail.writeMessageTo (baos) ;

       parse (new BufferedReader (new StringReader (baos.toString ()))) ;
     }
     catch (java.io.IOException ioe)
     {
      System.out.println ("error writeMessageTo:" + ioe.getMessage ()) ;
     }
     catch (javax.mail.MessagingException me)
     {
       System.out.println ("error writeMessageTo:" + me.getMessage()) ;
     }
  }

  public String getMailetInfo ()
  {
    return "JDBCTokenCounter Mailet" ;
  }

  private void parse (BufferedReader stream)
          throws java.io.IOException
  {
    StreamTokenizer st = new StreamTokenizer (stream) ;

    st.resetSyntax () ;
    st.eolIsSignificant (false) ;
    st.slashSlashComments (false) ;
    st.slashStarComments (false) ;
    st.whitespaceChars (0, 45) ;
    st.whitespaceChars (47, 47) ;
    st.whitespaceChars (58, 63) ;
    st.whitespaceChars (91, 96) ;
    st.wordChars (46, 46) ;
    st.wordChars (48, 57) ;
    st.wordChars (64, 90) ;
    st.wordChars (97, 122) ;
    //st.parseNumbers () ;

    Vector s = new Vector () ;

    //Build a Vector of the word tokens encountered.
    while (st.nextToken () != st.TT_EOF)
      if (st.ttype == st.TT_WORD)
      {
        if (st.sval.charAt (  st.sval.length ()
                            - 1) == '.')
          s.add (st.sval.substring (0,
                                      st.sval.length ()
                                    - 1)) ;
        else
          s.add (st.sval) ;
      }
      else
        if (st.ttype == st.TT_NUMBER)
          s.add (String.valueOf (st.nval)) ;

    //Translate an occurance into a count...
    /** Todo
     *  Possibly should merge this block with the one above, and skip the
     *  overhead of building the Vector.
     */
    Iterator i = s.iterator () ;
    while (i.hasNext ())
    {
      String key = (String) i.next () ;
      Integer value = null ;

      if (words.containsKey (key))
        value = new Integer (((Integer) words.get (key)).intValue () + 1) ;
      else
        value = new Integer (1) ;

      words.put (key,
                 value) ;
    }

    messageCount++ ;
    System.out.println ("Message parsed") ;
  }

}
