/*
 * LdapSearchList.java
 *
 * Copyright 2011 John W Dawson
 *
 * This code is distributed under the terms of the GNU General Public License, version 3
 *
 * This class implements the list used to display search results
 */
 
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import javax.naming.*;
import javax.naming.directory.*;
import java.util.*;
import java.util.Hashtable;
import java.net.*;
import java.util.logging.*;
import java.util.regex.*;

public class LdapSearchList extends JList implements DocumentListener
{

  static Logger log = Logger.getLogger ("LdapAddressBook");
  
  private static final Pattern numberPattern = Pattern.compile ("[\\d\\W]+");
  private static final Pattern digitPattern = Pattern.compile ("(\\d)\\W*");
  
  private JTextField searchField;
  private AddressBookConfiguration configuration;
  private MessageLine messageLine;
  private EditPanel editPanel;
  private LdapConnection connection;
  private Map<String, AddressBookField> fieldList;
  private boolean connected = false;
  private java.util.Timer inputTimer = null;
  private static final long INPUT_TIME_DELAY = 1000;  

  public void initialise (JTextField searchField, MessageLine messageLine, 
                          EditPanel editPanel, LdapConnection connection,
                          AddressBookConfiguration configuration)
  {
    // Save ref to configuraration and message box
    this.searchField = searchField;
    this.configuration = configuration;
    this.messageLine = messageLine;
    this.connection = connection;
    this.editPanel = editPanel;
    this.fieldList = editPanel.getFieldList ();
    
    // Attach to search box as handler
    searchField.getDocument().addDocumentListener (this);
    
    // Disable the list initially
    setEnabled (false);
          
  }
   
  public void clear ()
  {
    setModel (new DefaultListModel ());
    setEnabled (false);
    repaint ();
  }
  
  public void search ()
  {
    try
    {
      // Clear the current list
      clear ();
                  
      // Check that search box has not been cleared
      Document doc = searchField.getDocument ();
      if (doc.getLength() > 0)
      {
      
        // Create the search filter by replacing %s in the configured query 
        String filter = configuration.getElementValue ("ldapfilter").replaceAll ("%s", doc.getText (0, doc.getLength()));
      
        // Search for objects using the filter
        NamingEnumeration<SearchResult> answer = connection.search (filter, true);
        
        // Create a list of the results
        Vector<ListSearchResult> results = new Vector<ListSearchResult> ();	
        int numMatches = 0;
        try
        {
          while (answer.hasMore()) 
          {
            results.add (new ListSearchResult (answer.next(), configuration, fieldList));
            ++numMatches;
          }
          messageLine.setTransientText ((numMatches > 0 ? numMatches : "No") + " match" + 
                                                         (numMatches == 1 ? "" : "es") + " found");
        }
        catch (SizeLimitExceededException e)
        {
          messageLine.setTransientText (">50 matches found");  
        } 
        
        // Sort the list
        Collections.sort (results);
        
        // Display the list
        setListData (results); 
        setEnabled (true);    
        repaint ();
        validate ();
      }
    }
    catch (Exception e)
    {
      // Some problem occured
      messageLine.setTransientText ("LDAP search failed - check log");
      log.warning ("LDAP search failed: " + e.getMessage());
      e.printStackTrace ();
    }
  }
   
  private void resetTimer ()
  {
    // Cancel & reset keep alive timer
    connection.restartKeepAliveTimer (true);
    
    // Check if input timer is already running
    if (inputTimer != null)
    {
      // Cancel timer
      inputTimer.cancel ();
      
    }
        
    // Start a new timer
    inputTimer = new java.util.Timer ();
    inputTimer.schedule (new TimerTask ()
    {
      public void run ()
      {
        search ();
      }
    }, INPUT_TIME_DELAY);
    
  }

   // When text is entered into search box reset timer to wait for any further input
  public void insertUpdate(DocumentEvent event) 
  {
    resetTimer ();
  }
  public void removeUpdate(DocumentEvent event) 
  {
    resetTimer ();
  }
  public void changedUpdate(DocumentEvent event) 
  {}
  
}
