package com.limegroup.gnutella.gui.search;

import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.HashSet;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.LineBorder;

import com.limegroup.gnutella.MediaType;
import com.limegroup.gnutella.RouterService;
import com.limegroup.gnutella.gui.AutoCompleteTextField;
import com.limegroup.gnutella.gui.BoxPanel;
import com.limegroup.gnutella.gui.ClearableAutoCompleteTextField;
import com.limegroup.gnutella.gui.GUIMediator;
import com.limegroup.gnutella.gui.GUIUtils;
import com.limegroup.gnutella.gui.KeyProcessingTextField;
import com.limegroup.gnutella.gui.themes.ThemeFileHandler;
import com.limegroup.gnutella.gui.themes.ThemeSettings;
import com.limegroup.gnutella.gui.xml.InputPanel;
import com.limegroup.gnutella.settings.FilterSettings;
import com.limegroup.gnutella.util.NetworkUtils;
import com.limegroup.gnutella.xml.LimeXMLSchema;

/**
 * Inner panel that switches between the various kinds of
 * searching.
 */
class SearchInputPanel extends JPanel {
    
    /**
     * The current search label in what's new.
     */
    private final JLabel WHATSNEW_SEARCH_LABEL = new JLabel();

    /**
     * The current search label in normal search.
     */
    private final JLabel SEARCH_TYPE_LABEL = new JLabel();
    
    /**
     * The sole input text field that is at the top of all searches.
     */
    private final SearchField SEARCH_FIELD = new SearchField(14);
    
    /**
     * The text area that contains the information about direct-connecting
     * to this host.
     */
    private final JTextArea IP_TEXT = new JTextArea();
    
    /**
     * The input field for browse-host searches.
     */
    private final AutoCompleteTextField BROWSE_HOST_FIELD =
        new ClearableAutoCompleteTextField();
    
    /**
     * The What's New search button.
     */
    private final JButton WHATSNEW = new JButton(
            GUIMediator.getStringResource("SEARCH_TYPE_WHATSNEW"));
    
    /**
     * The JTabbedPane that switches between types of searches.
     */
    private final JTabbedPane PANE = new JTabbedPane(JTabbedPane.BOTTOM);
        
    /**
     * The CardLayout that switches between the detailed
     * search input information for each meta-type.
     */
    private final CardLayout META_CARDS = new CardLayout();
    
    /**
     * The panel that the META_CARDS layout uses to layout
     * the detailed search input fields.
     */
    private final JPanel META_PANEL = new JPanel(META_CARDS);
    
    /**
     * The name to use for the default panel that has no meta-data.
     */
    private static final String DEFAULT_PANEL_KEY = "defaultPanel";
    
    /**
     * The box that holds the schemas for searching.
     */
    private final SchemaBox SCHEMA_BOX = new SchemaBox();
    
    /**
     * The ditherer to use for the tab backgrounds.
     */
    private final Ditherer DITHERER =
            new Ditherer(62,
                        ThemeFileHandler.SEARCH_PANEL_BG_1.getValue(), 
                        ThemeFileHandler.SEARCH_PANEL_BG_2.getValue()
                        );
                    
	private JPanel searchEntry;
    
    /**
     * The listener for new searches.
     */
    private final ActionListener SEARCH_LISTENER = new SearchListener();
    
	/**
	 * Holds the keys of the already created input panels.
	 */
	private HashSet inputPanelKeys = null;
    
    SearchInputPanel() {
        super(new BorderLayout(0, 5));

        final ActionListener schemaListener = new SchemaListener();
        SCHEMA_BOX.addSelectionListener(schemaListener);
        add(SCHEMA_BOX, BorderLayout.NORTH);

        searchEntry = createSearchEntryPanel();
		
        JPanel whatsnew = createWhatIsNewPanel();
        JPanel browseHost = createBrowseHostPanel();
        panelize(searchEntry);
        panelize(whatsnew);
        panelize(browseHost);
        PANE.add(GUIMediator.getStringResource("SEARCH_TYPE_KEYWORD"),
                 searchEntry);
        PANE.add(GUIMediator.getStringResource("SEARCH_TYPE_WHATSNEW"),
                 whatsnew);
        PANE.add(GUIMediator.getStringResource("SEARCH_TYPE_BROWSEHOST"),
                 browseHost);
                 
        PANE.setRequestFocusEnabled(false);
        PANE.addMouseListener(new MouseListener() {
            public void mouseClicked(MouseEvent e) {
                requestSearchFocusImmediately();
            }
            public void mouseEntered(MouseEvent e) {}
            public void mouseExited(MouseEvent e) {}
            public void mousePressed(MouseEvent e) {}
            public void mouseReleased(MouseEvent e) {}
        });         

        if(!ThemeSettings.isNativeTheme()) {
            PANE.setBorder(
              new LineBorder(ThemeFileHandler.SEARCH_GRID_COLOR.getValue()) {
                public void paintBorder(Component c, Graphics g,
                                        int x, int y, int width, int height) {
                    try {
                        Component sel = PANE.getSelectedComponent();
                        if(sel != null)
                            height = sel.getBounds().height + 4;                    
                    } catch(ArrayIndexOutOfBoundsException aioobe) {}
                    super.paintBorder(c, g, x, y, width, height);
                }
            });
        }
        add(PANE, BorderLayout.CENTER);

        add(Box.createVerticalStrut(20), BorderLayout.SOUTH);
        
        WHATSNEW_SEARCH_LABEL.setFont(UIManager.getFont("Table.font.bold"));
        Font bold = UIManager.getFont("Table.font.bold");
        Font bolder =
            new Font(bold.getName(), bold.getStyle(), bold.getSize() + 5);
        SEARCH_TYPE_LABEL.setFont(bolder);
        SEARCH_TYPE_LABEL.setPreferredSize(new Dimension(100, 20));
        schemaListener.actionPerformed(null);
    }
    
    /**
     * Gets the KeyProcessingTextField that key events can be forwarded to.
     */
    KeyProcessingTextField getForwardingSearchField() {
        if(isNormalSearchType()) {
            if(SCHEMA_BOX.getSelectedSchema() != null) {
                return getInputPanel().getFirstTextField();
            }
            return SEARCH_FIELD;
        }
		if(isBrowseHostSearchType())
            return BROWSE_HOST_FIELD;
		return null;
    }
    
    /**
     * Determines if a key event can be forwarded to the search.
     */
    boolean isKeyEventForwardable() {
        return isNormalSearchType() ||
               isBrowseHostSearchType();
    }
    
    /**
     * Determines if browse-host is selected.
     */
    boolean isBrowseHostSearchType() {
        return PANE.getSelectedIndex() == 2;
    }
    
    /**
     * Determines if what is new is selected.
     */
    boolean isWhatIsNewSearchType() {
        return PANE.getSelectedIndex() == 1;
    }
    
    /**
     * Determines if keyword is selected.
     */
    boolean isNormalSearchType() {
        return PANE.getSelectedIndex() == 0;
    }
    
    /**
     * Notification that the addr has changed.
     */
    void addressChanged() {
        updateIpText();
        invalidate();
        revalidate();
    }
    
    void requestSearchFocusImmediately() {
        if(isNormalSearchType()) {
            if(SCHEMA_BOX.getSelectedSchema() != null) {
                getInputPanel().requestFirstFocus();
            } else {
                SEARCH_FIELD.requestFocus();
            }
        } else if(isWhatIsNewSearchType()) {
            WHATSNEW.requestFocus();
        } else if(isBrowseHostSearchType()) {
            BROWSE_HOST_FIELD.requestFocus();
        }
    }
    
    void requestSearchFocus() {
        // Workaround for bug manifested on Java 1.3 where FocusEvents
        // are improperly posted, causing BasicTabbedPaneUI to throw an
        // ArrayIndexOutOfBoundsException.
        // See:
        // http://developer.java.sun.com/developer/bugParade/bugs/4523606.html
        // http://developer.java.sun.com/developer/bugParade/bugs/4379600.html
        // http://developer.java.sun.com/developer/bugParade/bugs/4128120.html
        // for related problems.
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                requestSearchFocusImmediately();
            }
        });
    }
    
    /**
     * Sets all components in this component to be not opaque
     * and sets the correct background panel.
     */
    private void panelize(JComponent c) {
        GUIUtils.setOpaque(false, c);
        if(!ThemeSettings.isNativeTheme())
            c.setOpaque(true);
        c.setBackground(ThemeFileHandler.SEARCH_PANEL_BG_2.getValue());
        c.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 0));
    }
    
    /**
     * Creates the 'Direct Connect' browse host input panel.
     */
    private JPanel createBrowseHostPanel() {
        BROWSE_HOST_FIELD.addActionListener(SEARCH_LISTENER);
        
        JPanel panel = new DitherPanel(DITHERER);
        panel.setLayout(new GridBagLayout());
        GridBagConstraints c = new GridBagConstraints();
        
        c.gridwidth = GridBagConstraints.REMAINDER;
        
        JLabel label = new JLabel(GUIMediator.getStringResource("SEARCH_BROWSEHOST_TITLE"));
        label.setFont(UIManager.getFont("Table.font.bold"));
        panel.add(label, c);
        
        JTextArea text1 = new JTextArea(GUIMediator.getStringResource("SEARCH_BROWSEHOST_DESC"));
        text1.setLineWrap(true);
        text1.setWrapStyleWord(true);
        text1.setColumns(15);
        text1.setEditable(false);
        text1.setFont(UIManager.getFont("Table.font"));
        text1.setForeground(label.getForeground());
        c.insets = new Insets(15, 0, 0, 0);
        panel.add(text1, c);
                
        updateIpText();
        IP_TEXT.setLineWrap(true);
        IP_TEXT.setWrapStyleWord(true);
        IP_TEXT.setColumns(15);
        IP_TEXT.setEditable(false);
        IP_TEXT.setFont(UIManager.getFont("Table.font"));
        IP_TEXT.setForeground(label.getForeground());
        panel.add(IP_TEXT, c);
        
        c.fill = GridBagConstraints.HORIZONTAL;
        panel.add(BROWSE_HOST_FIELD, c);
        
        JButton search = new JButton(GUIMediator.getStringResource("SEARCH_BROWSEHOST_BUTTON"));
        search.addActionListener(SEARCH_LISTENER);
        c.fill = GridBagConstraints.NONE;
        panel.add(search, c);
        
        c.weighty = 1;
        panel.add(Box.createVerticalGlue(), c);
        
        return panel;
    }        
        
        
    private void updateIpText() {
        if(RouterService.acceptedIncomingConnection() &&
           !NetworkUtils.isPrivate()) {
            IP_TEXT.setText(
        			GUIMediator.getStringResource("SEARCH_BROWSEHOST_MYIP")+" \""+
					NetworkUtils.ip2string(RouterService.getAddress())+":"
					+RouterService.getPort()+"\""
			       );
        } else {
            IP_TEXT.setText(GUIMediator.getStringResource("SEARCH_BROWSEHOST_FIREWALLED"));
        }
    }
    
    /**
     * Creates the What's New input panel.
     */
    private JPanel createWhatIsNewPanel() {
        JPanel panel = new DitherPanel(DITHERER);
        panel.setLayout(new GridBagLayout());
        GridBagConstraints c = new GridBagConstraints();
        
        c.gridwidth = GridBagConstraints.REMAINDER;
        
        JLabel label = new JLabel(GUIMediator.getStringResource("SEARCH_WHATSNEW_TITLE_ONE"));
        label.setFont(UIManager.getFont("Table.font.bold"));
        panel.add(label, c);
        
        JLabel label2 = new JLabel(GUIMediator.getStringResource("SEARCH_WHATSNEW_TITLE_TWO"));        
        label2.setFont(UIManager.getFont("Table.font.bold"));
        panel.add(label2, c);
        
        JTextArea text = new JTextArea(GUIMediator.getStringResource("SEARCH_WHATSNEW_DESC"));
        text.setLineWrap(true);
        text.setWrapStyleWord(true);
        text.setColumns(15);
        text.setEditable(false);
        text.setFont(UIManager.getFont("Table.font"));
        text.setForeground(label.getForeground());
        c.insets = new Insets(15, 0, 30, 0);
        panel.add(text, c);
        
        JTextArea type = new JTextArea(
            GUIMediator.getStringResource("SEARCH_CURRENT_SEARCH") + 
            "  ");
        type.setFont(UIManager.getFont("Table.font"));
        type.setEditable(false);
        type.setForeground(label.getForeground());
        c.anchor = GridBagConstraints.EAST;
        c.gridwidth = GridBagConstraints.RELATIVE;
        c.insets = new Insets(0, 0, 0, 0);
        panel.add(type, c);
        
        c.anchor = GridBagConstraints.WEST;
        c.gridwidth = GridBagConstraints.REMAINDER;
        panel.add(WHATSNEW_SEARCH_LABEL, c);
        
        WHATSNEW.addActionListener(SEARCH_LISTENER);
        c.insets = new Insets(5, 0, 30, 0);
        c.anchor = GridBagConstraints.CENTER;
        panel.add(WHATSNEW, c);

        final JCheckBox hideAdult = new JCheckBox(
            GUIMediator.getStringResource("SEARCH_WHATSNEW_HIDE_ADULT"),
            FilterSettings.FILTER_WHATS_NEW_ADULT.getValue());
        hideAdult.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                FilterSettings.FILTER_WHATS_NEW_ADULT.
                    setValue(hideAdult.isSelected());
            }
        });
        c.insets = new Insets(0, 0, 0, 0);
        panel.add(hideAdult, c);
        hideAdult.setVisible(!FilterSettings.FILTER_ADULT.getValue());
        
        // Add a blank item with a weighty that'll push the rest to the top.
        c.weighty = 1;
        panel.add(Box.createVerticalGlue(), c);

        return panel;
    }

    private JPanel createSearchEntryPanel() {
        SEARCH_FIELD.addActionListener(SEARCH_LISTENER);

        // add the default search input panel to the meta cards
        META_PANEL.add(createDefaultSearchPanel(), DEFAULT_PANEL_KEY);

		// other mediatype panels are added lazily on demand

        JPanel search = new DitherPanel(DITHERER);
        search.setLayout(new BoxLayout(search, BoxLayout.Y_AXIS));
        search.add(GUIUtils.left(SEARCH_TYPE_LABEL));
        search.add(Box.createVerticalStrut(5));
        search.add(META_PANEL);
        return search;
    }
    
	private void createInputPanelForNamedMediaType(NamedMediaType nmt) {
            String name = nmt.getName();
            LimeXMLSchema schema = nmt.getSchema();
            // If a schema exists, add it as a possible type.
		if(schema == null) {
			throw new NullPointerException("named mediatype has no schema");
        }
		
		InputPanel panel = new InputPanel(schema, SEARCH_LISTENER, 
				SEARCH_FIELD.getDocument(),
				SEARCH_FIELD.getUndoManager());
		panel.addMoreOptionsListener(new MoreOptionsListener());
		JScrollPane pane = new JScrollPane(panel,
				JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
				JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
		pane.setOpaque(false);
		pane.setBorder(BorderFactory.createEmptyBorder());
		pane.getViewport().setBorder(null);
		JPanel outerPanel = new JPanel(new BorderLayout());
		outerPanel.add(pane,"Center");
		outerPanel.add(createSearchButtonPanel(),"South");
		int paneWidth = (int)pane.getPreferredSize().getWidth();
		int paneHeight= (int)pane.getPreferredSize().getHeight();
		outerPanel.setMaximumSize(new Dimension(paneWidth+40,paneHeight+40));
	    JPanel holdingPanel = new JPanel();
	    holdingPanel.setLayout(new BoxLayout(holdingPanel,BoxLayout.Y_AXIS));
	    holdingPanel.add(outerPanel);
		getInputPanelKeys().add(name);
		META_PANEL.add(holdingPanel, name);
		panelize(searchEntry);
	}
        
	private HashSet getInputPanelKeys() {
		if (inputPanelKeys == null) {
			inputPanelKeys = new HashSet();
		}
		return inputPanelKeys;
    }
    
    /**
     * Creates the default search input of:
     *    Filename
     *    [   input box  ]
     */
    private JPanel createDefaultSearchPanel() {
        
        JPanel label = createLabel(
			GUIMediator.getStringResource("SEARCH_DEFAULT_FIELD_TITLE"));
        JPanel fullPanel = new BoxPanel(BoxPanel.Y_AXIS);
        fullPanel.add(label);
        fullPanel.add(Box.createVerticalStrut(3));
        fullPanel.add(GUIUtils.left(SEARCH_FIELD));
        fullPanel.add(Box.createVerticalStrut(5));
        fullPanel.add(createSearchButtonPanel());

        return GUIUtils.left(fullPanel);
    }
    
    private JPanel createLabel(String text) {
        JPanel labelPanel = new BoxPanel(BoxPanel.X_AXIS);
        labelPanel.setOpaque(false);
        labelPanel.add(new JLabel(text));
        labelPanel.add(Box.createHorizontalGlue());		
        return labelPanel;
    }    

    /**
     * Creates the search button & inserts it in a panel.
     */
    private JPanel createSearchButtonPanel() {
        JPanel b = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
        JButton searchButton = new JButton(
            GUIMediator.getStringResource("SEARCH_SEARCH_BUTTON_LABEL"));        
        searchButton.setToolTipText(
            GUIMediator.getStringResource("SEARCH_SEARCH_BUTTON_TIP"));

        searchButton.addActionListener(SEARCH_LISTENER);
      
        b.add(Box.createHorizontalGlue());
        b.add(searchButton);
        return b;
    }
    
    /**
     * Gets the visible component in META_PANEL.
     */
    private JComponent getVisibleComponent() {
        for(int i = 0; i < META_PANEL.getComponentCount(); i++) {
            Component current = META_PANEL.getComponent(i);
            if(current.isVisible())
                return (JComponent)current;
        }
        return null;
    }
    
    /**
     * Gets the visible scrollpane.
     */
    private JScrollPane getVisibleScrollPane() {
        JComponent parent = (JComponent)getVisibleComponent().getComponent(0);
        	for(int i = 0; i < parent.getComponentCount(); i++) {
            Component current = parent.getComponent(i);
            if(current.isVisible() && current instanceof JScrollPane)
                return (JScrollPane)current;
        }
        return null;
    }
    
    /**
     * Retrieves the InputPanel that is currently visible.
     */
    private InputPanel getInputPanel() {
        JScrollPane pane = getVisibleScrollPane();
        if(pane == null)
            return null;
        else
            return (InputPanel)pane.getViewport().getView();
    }
    
	/**
	 * Listener for selecting a new schema.
	 */
	private class SchemaListener implements ActionListener {
	    public void actionPerformed(ActionEvent event) {
	        if(SCHEMA_BOX.getSelectedSchema() != null) {
				String key = SCHEMA_BOX.getSelectedItem();
				if (!getInputPanelKeys().contains(key)) {
					createInputPanelForNamedMediaType
						(SCHEMA_BOX.getSelectedMedia());
				}
	            META_CARDS.show(META_PANEL, key);
            } else {
                META_CARDS.show(META_PANEL, DEFAULT_PANEL_KEY);
            }
            WHATSNEW_SEARCH_LABEL.setText(SCHEMA_BOX.getSelectedItem());
            WHATSNEW_SEARCH_LABEL.setPreferredSize(
                new Dimension(GUIUtils.width(WHATSNEW_SEARCH_LABEL), 20));
            SEARCH_TYPE_LABEL.setText(SCHEMA_BOX.getSelectedItem());
            requestSearchFocus();
        }
    }
    
    /**
     * Listener for starting a new search.
     */
    private class SearchListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            String query = null;
            String xml = null;
            MediaType mt = SCHEMA_BOX.getSelectedMediaType();
            InputPanel panel = null;
            SearchInformation info = null;
            
            if(isWhatIsNewSearchType()) {
                info = SearchInformation.createWhatsNewSearch(
                    SCHEMA_BOX.getSelectedItem(), mt);
            } else if(isNormalSearchType()) {
                if(SCHEMA_BOX.getSelectedSchema() != null) {
                    panel = getInputPanel();
                    query = panel.getStandardQuery();
                    xml = panel.getInput(true);
                } else {
                    query = SEARCH_FIELD.getText();
                }
                info = SearchInformation.createKeywordSearch(query, xml, mt);
            } else if(isBrowseHostSearchType()) {
                String user = BROWSE_HOST_FIELD.getText();
                info = SearchInformation.createBrowseHostSearch(user);
                mt = MediaType.getAnyTypeMediaType(); // always any
            }
            
            // If the search worked, store & clear it.
            if(SearchMediator.triggerSearch(info) != null) {
                if(info.isKeywordSearch()) {
                    // Add the necessary stuff for autocompletion.
                    if(panel != null) {
                        panel.storeInput();
                        panel.clear();
                    } else {
                        SEARCH_FIELD.addToDictionary();
                    }
    
                    // Clear the existing search.
                    SEARCH_FIELD.setText("");
                } else if(info.isBrowseHostSearch()) {
                    BROWSE_HOST_FIELD.addToDictionary();
                    BROWSE_HOST_FIELD.setText("");
                }
            }
        }
    }
    
    /**
     * Listener for 'more options'.
     */
    private class MoreOptionsListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            JCheckBox box = (JCheckBox)e.getSource();
            JComponent c = (JComponent)((JComponent)getVisibleComponent()).getComponent(0);
            JComponent pane = (JComponent)((JComponent)c.getComponent(0)).getComponent(0);
            if(c instanceof JPanel && pane instanceof JScrollPane) {
            	if(box.isSelected()) {
            		c.setMaximumSize(null);
	            }
            	else {
            		int paneWidth = (int)pane.getPreferredSize().getWidth();
            		int paneHeight= (int)pane.getPreferredSize().getHeight();
            		c.setMaximumSize(new Dimension(paneWidth+40,paneHeight+40));
            	}
            }
            invalidate();
            revalidate();
            repaint();
        }
    }
}    
