/* -*- Mode: java; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.0 (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 Grendel mail/news client.
 *
 * The Initial Developer of the Original Code is Netscape Communications
 * Corporation.  Portions created by Netscape are Copyright (C) 1997
 * Netscape Communications Corporation.  All Rights Reserved.
 */

package grendel.addressbook;

import grendel.addressbook.AddressCard.*;

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

import com.sun.java.swing.*;
import com.sun.java.swing.text.*;
import com.sun.java.swing.ImageIcon;
import com.sun.java.swing.table.*;
//import com.sun.java.swing.table.DefaultTableModel;
import com.sun.java.swing.event.TableModelEvent;
import com.sun.java.swing.border.EmptyBorder;

import netscape.ldap.*;
import netscape.orion.toolbars.*;
import netscape.orion.menus.NsMenuManager;

/**
 *
 * @author  Lester Schueler
 */
public class AddressBook extends JFrame {
    private Hashtable       mCommands;
    private Hashtable       mMenuItems;

    private JMenuBar        mMenubar;
    private NSToolbar       mTtoolbar;
//    private Component       mStatusbar;
    private JTable          mTable;
    private JButton         mSearchButton;
    protected  DataSourceList  mDataSourceList;
    protected JComboBox       mSearchSource;
    protected  JTextField      mSearchField;

    public static void main(String[] args) {
        AddressBook AddressBookFrame = new AddressBook();
        AddressBookFrame.addWindowListener(new AppCloser());
        AddressBookFrame.show();
    }

    protected static final class AppCloser extends WindowAdapter {
        public void windowClosing(WindowEvent e) {
            System.exit(0);
        }
    }

    //***************************
    class DataSource {
        private String  mReadableName;
        private String  mDomainName;
        private int     mPort;

        DataSource (String aReadableName, String aDomainName) {
            this (aReadableName, aDomainName, 389);
        }

        DataSource (String aReadableName, String aDomainName, int aPort) {
            mReadableName   = aReadableName;;
            mDomainName     = aDomainName;
            mPort           = aPort;
        }

        public String   getReadableName ()  { return mReadableName; }
        public String   getDomainName ()    { return mDomainName; }
        public int      getPort ()          { return mPort; }
    }

    //***************************
    class DataSourceList {
        private Vector mDataSources;

        DataSourceList () {
            mDataSources = new Vector ();
        }

        public Enumeration getEnumeration () { return mDataSources.elements(); }
        public void addEntry (DataSource aDataSource) { mDataSources.addElement (aDataSource); }

        public DataSource find (String aReadableName) {
            for (Enumeration e = mDataSources.elements() ; e.hasMoreElements() ;) {
                DataSource ds = (DataSource) e.nextElement();

                if (ds.getReadableName ().equalsIgnoreCase(aReadableName))
                    return ds;
           }

           return null;
        }
    }

    /**
     *
     */
    public AddressBook() {
        super("Address Book");

        setBackground(Color.lightGray);
        //setBorderStyle(JPanel.ETCHED);
        setLayout(new BorderLayout());

//        addWindowListener(new FrameHider());

        //create menubar (top)
        //merge both the editors commands with this applications commands.
        mMenubar = NsMenuManager.createMenuBar("grendel.addressbook.Menus", "grendel.addressbook.MenuLabels", "mainMenubar", defaultActions);

            //collapsble panels holds toolbar.
            CollapsibleToolbarPanel collapsePanel = new CollapsibleToolbarPanel(this);
            collapsePanel.setBorder (new EmptyBorder(5,5,5,5));

            //toolbar buttons
            mTtoolbar = createToolbar();

            //collapsible item
                collapsePanel.add(mTtoolbar);

        //create status bar (bottom)
//        mStatusbar = createStatusbar();

        JPanel panel1 = new JPanel();
        panel1.setLayout(new BorderLayout());
        panel1.add(collapsePanel, BorderLayout.NORTH);

        //hack togetther the data sources.
        mDataSourceList = new DataSourceList ();
        mDataSourceList.addEntry (new DataSource ("Four11 Directory", "ldap.four11.com"));
        mDataSourceList.addEntry (new DataSource ("InfoSpace Directory", "ldap.infospace.com"));
        mDataSourceList.addEntry (new DataSource ("WhoWhere Directory", "ldap.whowhere.com"));

        //Create address panel
        AddressPanel addressPanel = new AddressPanel (mDataSourceList);
        panel1.add(addressPanel, BorderLayout.CENTER);

        add(mMenubar, BorderLayout.NORTH);
        add(panel1, BorderLayout.CENTER);

        setSize (600, 400);
    }

    /**
     * Hide this frame.
     */
    protected void hideThisFrame () {
        setVisible(false);
    }

    /**
     * Handles windowClosing for window listener.
     */
    protected class FrameHider extends WindowAdapter {
        public void windowClosing(WindowEvent e) {
            hideThisFrame();
        }
    }

    /**
     * Find the hosting frame, for the file-chooser dialog.
     */
    protected Frame getParentFrame() {
//        for (Container p = getParent(); p != null; p = p.getParent()) {
//            if (p instanceof Frame) {
//              return (Frame) p;
//            }
//        }
        return this;
    }

    //**********************
    //**********************
    // menus
    private JMenuBar menubar;

    //"File" actions
    public static final String newCardTag               ="newCard";
    public static final String newListTag               ="newList";
    public static final String importTag                ="import";
    public static final String saveAsTag                ="saveAs";
    public static final String callTag                  ="call";
    public static final String closeWindowTag           ="closeWindow";

    // "file->new" actions
    public static final String navigatorWindowTag       ="navigatorWindow";
    public static final String messageTag               ="message";
    public static final String blankPageTag             ="blankPage";
    public static final String pageFromTemplateTag      ="pageFromTemplate";
    public static final String pageFromWizardTag        ="pageFromWizard";

    //"Edit" actions
    public static final String undoTag                  ="undo";
    public static final String redoTag                  ="redo";
    public static final String deleteTag                ="delete";
    public static final String searchDirectoryTag       ="searchDirectory";
    public static final String HTMLDomainsTag           ="HTMLDomains";
    public static final String cardPropertiesTag        ="cardProperties";
    public static final String preferencesTag           ="preferences";

    //"View" actions
    public static final String hideMessageToolbarTag    ="hideMessageToolbar";
    public static final String byTypeTag                ="byType";
    public static final String byNameTag                ="byName";
    public static final String byEmailAddressTag        ="byEmailAddress";
    public static final String byComapanyTag            ="byComapany";
    public static final String byCityTag                ="byCity";
    public static final String byNicknameTag            ="byNickname";
    public static final String sortAscendingTag         ="sortAscending";
    public static final String sortDescendingTag        ="sortDescending";
    public static final String myAddressBookCardTag     ="myAddressBookCard";

    // --- action implementations -----------------------------------
    private Action[] defaultActions = {
        //"File" actions
        new NewCard(),
//        new NewList(),
//        new Import(),
        new SaveAs(),
//        new Call(),
        new CloseWindow()

        // "file->new" actions
//        new NavigatorWindow(),
//        new Message(),
//        new BlankPage(),
//        new PageFromTemplate(),
//        new PageFromWizard(),

        //"Edit" actions
//        new Undo(),
//        new Redo(),
//        new Delete(),
//        new SearchDirectory(),
//        new HTMLDomains(),
//        new CardProperties(),
//        new Preferences(),

        //"View" actions
//        new HideMessageToolbar(),
//        new ByType(),
//        new ByName(),
//        new ByEmailAddress(),
//        new ByCompany(),
//        new ByCity(),
//        new ByNickname(),
//        new SortAscending(),
//        new SortDescending(),
//        new MyAddressBookCard(),
//        new WrapLongLines()
    };

    //-----------------------
    //"File" actions
    //-----------------------
    /**
     */
    class NewCard extends AbstractAction {
        NewCard() {
            super(newCardTag);
            setEnabled(true);
        }

        public void actionPerformed(ActionEvent e) {
            NewCardDialog aDialog = new NewCardDialog(getParentFrame());

            //display the new card dialog
            aDialog.show ();
            aDialog.dispose();
        }
    }

    class SaveAs extends AbstractAction {
        SaveAs() {
            super(saveAsTag);
            setEnabled(true);
        }
        public void actionPerformed(ActionEvent ae) {
            NewCardDialog aDialog = new NewCardDialog(getParentFrame());

            //display the new card dialog
            aDialog.show ();
            aDialog.dispose();
        }
    }

    class CloseWindow extends AbstractAction {
        CloseWindow() {
            super(closeWindowTag);
            setEnabled(true);
        }

        public void actionPerformed(ActionEvent e) {
            //hide after send.
            hideThisFrame();
        }
    }

    //-----------------------
    // "file->new" actions
    //-----------------------

    //-----------------------
    //"Edit" actions
    //-----------------------
    class Undo extends AbstractAction {
        Undo() {
            super(undoTag);
            setEnabled(true);
        }
        public void actionPerformed(ActionEvent e) {}
    }

    //-----------------------
    //"View" actions
    //-----------------------
    class HideMessageToolbar extends AbstractAction {
        HideMessageToolbar() {
            super(hideMessageToolbarTag);
            setEnabled(true);
        }

        public void actionPerformed(ActionEvent e) {}
    }

    //-----------------------
    //-----------------------
    class Search extends AbstractAction {
        Search() {
            super(newListTag);
            setEnabled(true);
        }

        public void actionPerformed(ActionEvent e) {
            String readableName = (String) mSearchSource.getSelectedItem();
            int index = mSearchSource.getSelectedIndex();

            DataSource ds = mDataSourceList.find (readableName);
            if (null != ds) {

                //get the text to search for.
                String textToSearchFor = mSearchField.getText();

                if (!textToSearchFor.trim().equals ("")) {
                    DataModel dm = (DataModel) mTable.getModel ();
                    dm.reloadData (ds.getDomainName(), ds.getPort(), textToSearchFor);

                    //repaint the table with results.
                    mTable.repaint();
                }
            }
        }
    }

    /**
     * Create a Toolbar
     * @see addToolbarButton
     */
    private NSToolbar createToolbar() {

        NSToolbar toolBar = new NSToolbar();
        addToolbarButton(toolBar, null,     "images/newcard.gif",       "Create a new card");
        addToolbarButton(toolBar, null,     "images/newlist.gif",       "Create a new list");
        addToolbarButton(toolBar, null,     "images/properties.gif",    "Edit the selected card");
        addToolbarButton(toolBar, null,     "images/newmsg.gif",        "New Message (Ctrl+M)");
        addToolbarButton(toolBar, null,     "images/directory.gif",     "Look up an address");
        addToolbarButton(toolBar, null,     "images/call.gif",          "Start Netscape Conference");
        addToolbarButton(toolBar, null,     "images/delete.gif",        "Delete selected cards <Del>");

        return toolBar;
    }

    /**
     * create a toolbar button
     * @param aToolBar The parent toolbar to add this button to.
     * @param aActionListener Who you want to be notified when the button is pressed.
     * @param aImageName The image name for the button. like "save.gif"
     * @param aToolTip The buttons tool tip. like "Save the current file".
     * @see createToolbar
     */
    public void addToolbarButton(NSToolbar aToolBar, AbstractAction aActionListener, String aImageName, String aToolTip) {
        NSButton b = new NSButton();

        b.setHorizontalTextPosition(JButton.CENTER);
        b.setVerticalTextPosition(JButton.BOTTOM);
        b.setToolTipText(aToolTip);

//        URL iconUrl = getClass().getResource("images/" + gifName + ".gif");
        b.setIcon(new ImageIcon(getClass().getResource(aImageName)));

//    iconUrl = getClass().getResource("images/" + gifName + "-disabled.gif");
//    button.setDisabledIcon(ImageIcon.createImageIcon(iconUrl));

//    iconUrl = getClass().getResource("images/" + gifName + "-depressed.gif");
//    button.setPressedIcon(ImageIcon.createImageIcon(iconUrl));

//    iconUrl = getClass().getResource("images/" + gifName + "-rollover.gif");
//    button.setRolloverIcon(ImageIcon.createImageIcon(iconUrl));


//        JButton b = new JButton(new ImageIcon(aImageName));
//      b.setToolTipText(aToolTip);
//      b.setPad(new Insets(3,3,3,3));
//        b.addActionListener(aActionListener);

        aToolBar.addItem(b);
    }

    private String getFirstEnum (Enumeration enumVals) {
            if (enumVals.hasMoreElements())
                    return (String) enumVals.nextElement();

                return null;
        }

    public Vector queryLDAP (String aServerName, int aPort, String aSearchString) {

        Vector retVecVec = new Vector();   //return vector of vectors.

//              try {
            //open a connection to the LDAP server
System.out.println ("Opening server");
            ICardSource Four11AddressBook = new LDAP_Server (aServerName);

            //create the query
            ITerm query = new TermEqual (new AC_Attribute ("sn", aSearchString));

            String[] attributes = {"sn", "cn", "o", "mail", "city"};

            //query the LDAP server.
System.out.println ("Send query");
            ICardSet cardSet = Four11AddressBook.getCardSet (query, attributes);

            //Sort the list.
//            String[] sortOrder = {"givenname", "surname"};
//            cardSet.sort (sortOrder);

            //hack. I've put the for loop in a try block to catch the exception
            //thrown when cardEnum.hasMoreElements() incorrectly returns true.
            try {
                //enumerate thru the cards.
                for (Enumeration cardEnum = cardSet.getEnumeration(); cardEnum.hasMoreElements(); ) {
System.out.println ("got card");
                    ICard card = (ICard) cardEnum.nextElement();              //get the addres card
                    IAttributeSet attrSet = card.getAttributeSet ();  //get the attributes for this card
                    Vector thisRow = new Vector(6);                     //create a simple vector to hold the attributes values for this card.

                                String commonName = "";
                                String organization = "";
                                String mail = "";
                                String phone = "";
                                String city = "";
                                String nickName = "";

                                // enumerate thru the card attributes.
                    for (Enumeration attEnum = attrSet.getEnumeration(); attEnum.hasMoreElements(); ) {
                        IAttribute attr = (IAttribute) attEnum.nextElement();
                                        String attrName = attr.getName();

                                        if (attrName.equals ("cn")) {
                                            commonName = attr.getValue();
                                        }

                                        else if (attrName.equals ("o")) {
                                            organization = attr.getValue();
                                        }

                                        else if (attrName.equals ("mail")) {
                                            mail = attr.getValue();
                                        }
                                }

                    //create this row for the table.
                    thisRow.addElement (commonName);
                    thisRow.addElement (mail);
                    thisRow.addElement (organization);
                    thisRow.addElement (phone);
                    thisRow.addElement (city);
                    thisRow.addElement (nickName);

                    //add this row to the table
                    retVecVec.addElement (thisRow);
                }
            }
            catch (Exception e) {
            }
//              }
//              catch( LDAPException e ) {
//                      System.out.println( "Error: " + e.toString() );
//              }

System.out.println ("Done.");
            return retVecVec;
        }

    public class DataModel extends AbstractTableModel {
        private Vector mVecVec;
        private String[] mColumnNames;

        public DataModel (String[] aNames) {
            super();
            mVecVec = new Vector ();
            mColumnNames = aNames;
        }

        public int      getRowCount()                       { return mVecVec.size(); }
        public int      getColumnCount ()                   { return mColumnNames.length; }
        public String   getColumnName(int column)           { return mColumnNames[column]; }
        public Class    getColumnClass(int col)             { return String.class; }
        public boolean  isCellEditable(int row, int col)    { return false;}

        public void     setValueAt(Object aValue, int row, int column) {
            ((Vector)mVecVec.elementAt(row)).setElementAt(aValue, column);
        }

        public void     reloadData (String aServerName, int aPort, String aSearchString) {
            //reload the data from LDAP.
            mVecVec = queryLDAP (aServerName, aPort, aSearchString);
        }

        public Object getValueAt(int row, int column) {
            return (((Vector)mVecVec.elementAt(row)).elementAt(column));
        }
    }

    /**
     * Create a status bar
     * @return
     * @see
     */
//    protected Component createStatusbar() {
//      // need to do something reasonable here
//          StatusBar status = new StatusBar();
//      return status;
//    }

    //***************************
    /**
     *
     */
    class StatusBar extends JPanel {
        public StatusBar() {
            super();
                setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
        }

        public void paint(Graphics g) {
                super.paint(g);
        }
    }

    //***************************
    /**
     */
    class AddressPanel extends JPanel {

        public AddressPanel(DataSourceList aDataSourceList) {
                //super(true);

            setBorder (new EmptyBorder(10,10,10,10));
                setLayout (new BorderLayout(10, 5));

            add(createSearchPane(aDataSourceList), BorderLayout.NORTH);
            add(createTable(),      BorderLayout.CENTER);
        }

        private Box createSearchPane (DataSourceList aDataSourceList) {
            //explaination
            JLabel explaination = new JLabel ("Type in the name you're looking for:");
            explaination.setAlignmentX((float)0.0);        //align left

            //text field
            mSearchField = new JTextField (20);
            mSearchField.setAlignmentX((float)0.0);        //align left

            //box for explain and text field
            Box innerBoxPane = new Box (BoxLayout.Y_AXIS);
//            innerBoxPane.setAlignmentY((float)0.0);        //align to bottom
            innerBoxPane.add (explaination);
            innerBoxPane.add (mSearchField);

            //drop down combo box
            mSearchSource = new JComboBox();
            mSearchSource.setAlignmentY((float)0.0);        //align to bottom
            for (Enumeration e = aDataSourceList.getEnumeration() ; e.hasMoreElements() ;) {
                DataSource ds = (DataSource) e.nextElement();
                mSearchSource.addItem(ds.getReadableName());
            }

            //label
            JLabel lbl = new JLabel ("in:");
            lbl.setAlignmentY((float)0.0);        //align to bottom

            //search button
            mSearchButton = new JButton ("Search");
            mSearchButton.addActionListener(new Search());
            mSearchButton.setAlignmentY((float)0.0);         //align to bottom

            Dimension spacer = new Dimension (10, 10);

            //assemble all the pieces together.
            Box boxPane = new Box (BoxLayout.X_AXIS);

            boxPane.add (innerBoxPane);                 //explaination and text field
            boxPane.add (Box.createRigidArea(spacer));  //spacer
            boxPane.add (lbl);                          //"in:" label
            boxPane.add (Box.createRigidArea(spacer));  //spacer
            boxPane.add (mSearchSource);                //drop down combo box
            boxPane.add (Box.createRigidArea(spacer));  //spacer
            boxPane.add (mSearchButton);                //search buttton

            return boxPane;
        }

        private JScrollPane createTable () {
            String[] columnNames = {
                        "Name",
                        "Email Address",
                        "Organization",
                        "Phone",
                        "City",
                        "Nickname"};

            //create the data model.
            DataModel dm = new DataModel (columnNames);

            //create the table.
            mTable = new JTable(dm);

//              mTable.setAutoCreateColumnsFromModel(false);

            // Add our columns into the  column model
//            for (int columnIndex = 0; columnIndex < columnNames.length; columnIndex++){
                // Create a column object for each column of data
//                TableColumn newColumn = new TableColumn(columnNames[columnIndex]);

                // Set a tool tip for the column header cell
//              TableCellRenderer renderer2 = newColumn.getHeaderRenderer();
//              if (renderer2 instanceof DefaultCellRenderer)
//                    ((DefaultCellRenderer)renderer2).setToolTipText(columnNames[columnIndex]);

//                newColumn.setWidth(200);
//                mTable.addColumn(newColumn);
//            }

            //no selection, no grid.
            mTable.setColumnSelectionAllowed(false);
            mTable.setShowGrid(false);

            // Put the table and header into a scrollPane
                JScrollPane scrollpane = JTable.createScrollPaneForTable(mTable);
//            JTableHeader tableHeader = mTable.getTableHeader();

            // create and add the column heading to the scrollpane's
            // column header viewport
//            JViewport headerViewport = new JViewport();
//            headerViewport.setLayout(new BoxLayout(headerViewport, BoxLayout.X_AXIS));
//            headerViewport.add(tableHeader);
//            scrollpane.setColumnHeader(headerViewport);

            // add the table to the viewport
///            JViewport mainViewPort = scrollpane.getViewport();
//            mainViewPort.add(mTable);
//            mainViewPort.setBackground (Color.white);

            // speed up resizing repaints by turning off live cell updates
//            tableHeader.setUpdateTableInRealTime(false);

            //return the JScrollPane with the table in it.
            return scrollpane;
        }
    }
}
