/*
 * The contents of this file are subject to the terms of the Common Development
 * and Distribution License (the License). You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
 * or http://www.netbeans.org/cddl.txt.
 * 
 * When distributing Covered Code, include this CDDL Header Notice in each file
 * and include the License file at http://www.netbeans.org/cddl.txt.
 * If applicable, add the following below the CDDL Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyrighted [year] [name of copyright owner]"
 *
 * The Original Software is NetBeans. The Initial Developer of the Original
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
 * Microsystems, Inc. All Rights Reserved.
 */


package org.netbeans.modules.autoupdate;

import java.awt.Font;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;

import java.util.Date;
import java.util.HashSet;
import javax.swing.DefaultListModel;
import javax.swing.JComponent;
import javax.swing.UIManager;
import javax.swing.event.ListSelectionListener;
import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.StyleSheet;
import org.openide.DialogDisplayer;

import org.openide.NotifyDescriptor;
import org.openide.awt.HtmlBrowser;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.explorer.ExplorerManager;
import org.openide.explorer.view.BeanTreeView;
import org.openide.modules.SpecificationVersion;
import org.openide.util.NbBundle;
import org.openide.util.RequestProcessor;


class UpdatePanel extends javax.swing.JPanel {

    private static final String EMPTY_STRING = ""; // NOI18N
    
    private static final java.awt.Dimension PREFERRED_SIZE = new java.awt.Dimension( 600, 500 );

    private static final String SPACE = " "; //NOI18N
    
    transient private static final HashSet acceptedLicencies = new HashSet (10);

    /** Explorer tree */
    private ExplorerView explorerView;
    /** List model for listBox with selected modules */
    private DefaultListModel selectedListModel;
    /** Summation of lengths of all selected modules */
    private long totalSize;

    private Wizard.Validator validator;
    
    private UpdateNode.AllServers allNodes;
    
    private int duplicateStatus = 0;
    
    private PropertyChangeListener nodeListener;

    static final long serialVersionUID =897622109141801200L;
    
    static final String LINE_SEPARATOR = "<br>"; //NOI18N
    
    /**
     * Creates new form UpdatePanel
     * @param validator controls if next button should be enabled or disbled
     */
    public UpdatePanel( Wizard.Validator validator ) {
        putClientProperty("WizardPanel_autoWizardStyle", Boolean.TRUE);   // NOI18N
        putClientProperty("WizardPanel_contentDisplayed", Boolean.TRUE);  // NOI18N
        putClientProperty("WizardPanel_contentNumbered", Boolean.TRUE);   // NOI18N
        putClientProperty("WizardPanel_contentSelectedIndex", new Integer(1)); // NOI18N
        putClientProperty("WizardPanel_contentData", new String[] {            // NOI18N
            getBundle("WIZ_First"),            
            getBundle("WIZ_Select"),
            getBundle("WIZ_Download"),
            getBundle("WIZ_View")
        });
        initComponents();
        
        HTMLEditorKit htmlkit = new HTMLEditorKit ();
        // override the Swing default CSS to make the HTMLEditorKit use the
        // same font as the rest of the UI.

        // XXX the style sheet is shared by all HTMLEditorKits.  We must
        // detect if it has been tweaked by ourselves or someone else
        // (code completion javadoc popup for example) and avoid doing the
        // same thing again

        StyleSheet css = htmlkit.getStyleSheet ();

        if (css.getStyleSheets() == null) {
            StyleSheet css2 = new StyleSheet();
            Font f = jList1.getFont ();
            int size = jList1.getFont ().getSize ();
            css2.addRule(new StringBuffer("body { font-size: ").append(size) // NOI18N
                        .append("; font-family: ").append(f.getName()).append("; }").toString()); // NOI18N
            css2.addStyleSheet(css);
            htmlkit.setStyleSheet(css2);
        }

        this.description.setEditorKit (htmlkit);
        
        setName(getBundle("LAB_Select2"));
        
        this.validator = validator;

        // Add Explorer view
        explorerView = new ExplorerView();
        explorerView.getExplorerManager().addPropertyChangeListener(
            new PropertyChangeListener() {
                public void propertyChange( PropertyChangeEvent evt ) {
                    if ( evt.getPropertyName() == ExplorerManager.PROP_SELECTED_NODES ) {
                        selectionChange( true );
                    }
                }
            } );
        
        jList1.addFocusListener(
            new FocusListener() {
                public void focusGained( FocusEvent evt ) {
                    unselectLeft();
                    if ( jList1.getSelectedIndex() == -1 && selectedListModel.getSize() > 0 )
                        jList1.setSelectedIndex( 0 );
                }
            
                public void focusLost( FocusEvent evt ) {
                }
            } );

        java.awt.GridBagConstraints gridBagConstraints1 = new java.awt.GridBagConstraints ();
        gridBagConstraints1.gridwidth = 0;
        gridBagConstraints1.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints1.weightx = 1.0;
        gridBagConstraints1.weighty = 1.0;
        gridBagConstraints1.insets = new java.awt.Insets (0, 3, 0, 3);
        jPanel1.add (explorerView, gridBagConstraints1);

        // Customize jList1

        jList1.setCellRenderer( new SelectedListCellRenderer() );
        selectedListModel = new DefaultListModel();
        jList1.setModel( selectedListModel );
        jList1.getSelectionModel().setSelectionMode( javax.swing.ListSelectionModel.MULTIPLE_INTERVAL_SELECTION );
        jList1.getSelectionModel().addListSelectionListener(
            new ListSelectionListener() {
                public void valueChanged( javax.swing.event.ListSelectionEvent evt ) {
                    selectionChange( false );
                }
            }
        );
                
        panel1Label.setLabelFor(explorerView);

        getAccessibleContext().setAccessibleName(getBundle("ACS_UpdatePanel"));
        getAccessibleContext().setAccessibleDescription(getBundle("ACSD_UpdatePanel") +  getBundle("LAB_227"));
        moreButton.getAccessibleContext().setAccessibleDescription(getBundle("ACS_More"));
        jList1.getAccessibleContext().setAccessibleDescription(getBundle("ACS_InstallList"));
        explorerView.setName (getBundle ("ACSN_ExplorerView"));
    }
    
    /** Returns preferred size of the panel */
    public java.awt.Dimension getPreferredSize() {
        return PREFERRED_SIZE;
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents
    private void initComponents() {
        java.awt.GridBagConstraints gridBagConstraints;

        panel1Label = new javax.swing.JLabel();
        jPanel1 = new javax.swing.JPanel();
        addButton = new javax.swing.JButton();
        removeButton = new javax.swing.JButton();
        addallButton = new javax.swing.JButton();
        removeallButton = new javax.swing.JButton();
        scrollpaneLabel = new javax.swing.JLabel();
        jScrollPane1 = new javax.swing.JScrollPane();
        jList1 = new javax.swing.JList();
        jPanel3 = new javax.swing.JPanel();
        totalsizeLabel = new javax.swing.JLabel();
        totalsizeTextField = new javax.swing.JTextField();
        jPanel2 = new javax.swing.JPanel();
        avLabel = new javax.swing.JLabel();
        avTextField = new javax.swing.JTextField();
        ivLabel = new javax.swing.JLabel();
        ivTextField = new javax.swing.JTextField();
        jScrollPane2 = new javax.swing.JScrollPane();
        description = new javax.swing.JEditorPane();
        moreButton = new javax.swing.JButton();

        setLayout(new java.awt.GridBagLayout());

        org.openide.awt.Mnemonics.setLocalizedText(panel1Label, getBundle("LAB_221"));
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.gridwidth = 2;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
        add(panel1Label, gridBagConstraints);

        jPanel1.setLayout(new java.awt.GridBagLayout());

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.gridwidth = 2;
        gridBagConstraints.gridheight = 5;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.weighty = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(2, 0, 0, 0);
        add(jPanel1, gridBagConstraints);

        org.openide.awt.Mnemonics.setLocalizedText(addButton, getBundle("BTN_Add_2"));
        addButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                addButtonActionPerformed(evt);
            }
        });

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 2;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.insets = new java.awt.Insets(2, 12, 0, 12);
        add(addButton, gridBagConstraints);

        org.openide.awt.Mnemonics.setLocalizedText(removeButton, getBundle("BTN_Remove_2"));
        removeButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                removeButtonActionPerformed(evt);
            }
        });

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 2;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.insets = new java.awt.Insets(6, 12, 0, 12);
        add(removeButton, gridBagConstraints);

        org.openide.awt.Mnemonics.setLocalizedText(addallButton, getBundle("BTN_AddAll_2"));
        addallButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                addallButtonActionPerformed(evt);
            }
        });

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 2;
        gridBagConstraints.gridy = 3;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.insets = new java.awt.Insets(6, 12, 0, 12);
        add(addallButton, gridBagConstraints);

        org.openide.awt.Mnemonics.setLocalizedText(removeallButton, getBundle("BTN_RemoveAll_2"));
        removeallButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                removeallButtonActionPerformed(evt);
            }
        });

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 2;
        gridBagConstraints.gridy = 4;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.insets = new java.awt.Insets(6, 12, 0, 12);
        add(removeallButton, gridBagConstraints);

        scrollpaneLabel.setLabelFor(jList1);
        org.openide.awt.Mnemonics.setLocalizedText(scrollpaneLabel, getBundle("LAB_222"));
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 3;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
        gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 12);
        add(scrollpaneLabel, gridBagConstraints);

        jList1.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                jList1MouseClicked(evt);
            }
        });

        jScrollPane1.setViewportView(jList1);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 3;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.gridwidth = 3;
        gridBagConstraints.gridheight = 5;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.weighty = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(2, 0, 0, 0);
        add(jScrollPane1, gridBagConstraints);

        jPanel3.setLayout(new java.awt.GridLayout(1, 2, 6, 0));

        totalsizeLabel.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
        totalsizeLabel.setLabelFor(totalsizeTextField);
        org.openide.awt.Mnemonics.setLocalizedText(totalsizeLabel, getBundle("LAB_223"));
        jPanel3.add(totalsizeLabel);

        totalsizeTextField.setEditable(false);
        totalsizeTextField.setDisabledTextColor(java.awt.Color.black);
        totalsizeTextField.setEnabled(false);
        totalsizeTextField.setMinimumSize(new java.awt.Dimension(6, 20));
        jPanel3.add(totalsizeTextField);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 3;
        gridBagConstraints.gridy = 6;
        gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(12, 0, 0, 0);
        add(jPanel3, gridBagConstraints);

        jPanel2.setLayout(new java.awt.GridBagLayout());

        jPanel2.setBorder(javax.swing.BorderFactory.createEtchedBorder());
        avLabel.setLabelFor(avTextField);
        org.openide.awt.Mnemonics.setLocalizedText(avLabel, getBundle("LAB_224"));
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.insets = new java.awt.Insets(4, 10, 0, 0);
        jPanel2.add(avLabel, gridBagConstraints);

        avTextField.setEditable(false);
        avTextField.setDisabledTextColor(java.awt.Color.black);
        avTextField.setEnabled(false);
        avTextField.setMinimumSize(new java.awt.Dimension(60, 21));
        avTextField.setPreferredSize(new java.awt.Dimension(60, 21));
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.ipadx = 60;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(4, 6, 0, 0);
        jPanel2.add(avTextField, gridBagConstraints);

        ivLabel.setLabelFor(ivTextField);
        org.openide.awt.Mnemonics.setLocalizedText(ivLabel, getBundle("LAB_225"));
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
        gridBagConstraints.insets = new java.awt.Insets(4, 12, 0, 0);
        jPanel2.add(ivLabel, gridBagConstraints);

        ivTextField.setEditable(false);
        ivTextField.setDisabledTextColor(java.awt.Color.black);
        ivTextField.setEnabled(false);
        ivTextField.setMinimumSize(new java.awt.Dimension(60, 21));
        ivTextField.setPreferredSize(new java.awt.Dimension(63, 21));
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.ipadx = 60;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(4, 6, 0, 0);
        jPanel2.add(ivTextField, gridBagConstraints);

        jScrollPane2.setMinimumSize(new java.awt.Dimension(22, 52));
        description.setEditable(false);
        jScrollPane2.setViewportView(description);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.gridwidth = 5;
        gridBagConstraints.gridheight = 2;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.weighty = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(6, 10, 4, 0);
        jPanel2.add(jScrollPane2, gridBagConstraints);

        org.openide.awt.Mnemonics.setLocalizedText(moreButton, getBundle("BTN_More"));
        moreButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        moreButton.setIconTextGap(0);
        moreButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                moreButtonActionPerformed(evt);
            }
        });

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 5;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.SOUTH;
        gridBagConstraints.insets = new java.awt.Insets(6, 11, 4, 4);
        jPanel2.add(moreButton, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 7;
        gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
        gridBagConstraints.gridheight = java.awt.GridBagConstraints.REMAINDER;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.SOUTH;
        gridBagConstraints.weighty = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(12, 0, 0, 0);
        add(jPanel2, gridBagConstraints);

    }
    // </editor-fold>//GEN-END:initComponents

    private void moreButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_moreButtonActionPerformed
        ModuleUpdate mu = getSingleSelection();

        if (mu != null && mu.getHomePage() != null) {
            HtmlBrowser.URLDisplayer.getDefault().showURL(mu.getHomePage());
        }
    }//GEN-LAST:event_moreButtonActionPerformed

    private void removeallButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_removeallButtonActionPerformed
        for( int i = 0; i < selectedListModel.size(); i++ ) {
            ((ModuleUpdate)selectedListModel.get(i)).setSelected( false );
            ((ModuleUpdate)selectedListModel.get(i)).setDownloadOK( false );
            ((ModuleUpdate)selectedListModel.get(i)).setSecurity( SignVerifier.NOT_CHECKED );
        }
        refreshNodes();

        selectedListModel.clear();
        Downloader.deleteDownload();
        showSize();
        validator.setValid( selectedListModel.size() > 0 );
        
        buttonsCheck();
    }//GEN-LAST:event_removeallButtonActionPerformed

    private void addallButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addallButtonActionPerformed
        Collection modules = Wizard.getAllModules();

        Iterator it = modules.iterator();

        while ( it.hasNext() ) {
            if ( !addModule( (ModuleUpdate)it.next() ) ) {
                // Some licence rejected
                removeallButtonActionPerformed(evt);
                return;
            }
        }
        buttonsCheck();
        
    }//GEN-LAST:event_addallButtonActionPerformed

    private void removeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_removeButtonActionPerformed
        int[] index = jList1.getSelectedIndices();

        if ( index.length == 0 )
            return;
        
        java.util.List toremove = new ArrayList();
        for (int i = index.length -1; i >= 0 ; i-- )
            toremove.add( selectedListModel.get( index[i] ));
        
        Iterator it = toremove.iterator();
        while (it.hasNext())        
            removeModule( (ModuleUpdate)it.next() );

        refreshNodes();

        showSize();
        validator.setValid( selectedListModel.size() > 0 );
        buttonsCheck();
    }//GEN-LAST:event_removeButtonActionPerformed

    private void addButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addButtonActionPerformed
        Node[] nodes = explorerView.getSelectedNodes();
        List modulesToAdd = new ArrayList();
        ModuleUpdate mu = null;

        for ( int i = 0; i < nodes.length; i ++ ) {
            mu = (ModuleUpdate)nodes[i].getCookie( ModuleUpdate.class );
            if ( mu != null ) {
                modulesToAdd.add( mu );
            }
            else {
                // Add whole group - subtree
                modulesToAdd.addAll( getUnselectedChildren( nodes[i] ) );
            }
        }
        
        Iterator iter = modulesToAdd.iterator();
        while ( iter.hasNext() ) {
            mu = (ModuleUpdate)iter.next();
            if ( !addModule( mu , modulesToAdd ) ) {
                return;
            }
        }

        buttonsCheck();
    }//GEN-LAST:event_addButtonActionPerformed

    private void jList1MouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_jList1MouseClicked
        if ( evt.getClickCount() == 2 ) {
            int index = jList1.locationToIndex( evt.getPoint() );

            if ( index >= 0 && index < selectedListModel.size() ) {
                removeModule( (ModuleUpdate)selectedListModel.get( index ) );
                showSize();
                validator.setValid( selectedListModel.size() > 0 );
                refreshNodes();
            }
            buttonsCheck();
        }
    }//GEN-LAST:event_jList1MouseClicked
     
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton addButton;
    private javax.swing.JButton addallButton;
    private javax.swing.JLabel avLabel;
    private javax.swing.JTextField avTextField;
    private javax.swing.JEditorPane description;
    private javax.swing.JLabel ivLabel;
    private javax.swing.JTextField ivTextField;
    private javax.swing.JList jList1;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JPanel jPanel2;
    private javax.swing.JPanel jPanel3;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JScrollPane jScrollPane2;
    private javax.swing.JButton moreButton;
    private javax.swing.JLabel panel1Label;
    private javax.swing.JButton removeButton;
    private javax.swing.JButton removeallButton;
    private javax.swing.JLabel scrollpaneLabel;
    private javax.swing.JLabel totalsizeLabel;
    private javax.swing.JTextField totalsizeTextField;
    // End of variables declaration//GEN-END:variables
    
    void setUpdates( int wizardType ) {

        boolean firstTime = true;
        
        allNodes = null;
        selectedListModel.clear();
        totalSize = 0;
        Iterator it = Wizard.getAllModules().iterator();

        while( it.hasNext() ) {
            ModuleUpdate mu = (ModuleUpdate)it.next();
            
            if ( mu.isSelected() && ( !selectedListModel.contains( mu ) ) ) {
                selectedListModel.addElement( mu );
                totalSize += mu.getDownloadSize();
            }
            
            if ( firstTime ) {
                if ( wizardType == 1 || mu.isPurchased() ) {
                    addModule( mu );
                }
            }
        }
        validator.setValid( selectedListModel.size() > 0 );

        totalsizeTextField.setText( ( totalSize / 1024 ) + SPACE + getBundle("CTL_UpdatePanel_KB") );

        buttonsCheck();
        showSize();
        
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                refreshNodes();
            }
        });
        
        nodeListener = new PropertyChangeListener() {
                            public void propertyChange(PropertyChangeEvent evt) {
                                if ( Settings.NODE_DEFAULT_ACTION.equals( evt.getPropertyName() )) {
                                    Node[] selectedNodes = explorerView.getSelectedNodes();
                                    if ( selectedNodes.length == 1 ) {
                                        ModuleUpdate mu = (ModuleUpdate)selectedNodes[0].getCookie( ModuleUpdate.class );
                                        if ( mu != null )
                                            if ( ! addModule( mu ) )
                                                buttonsCheck();
                                    }
                                }
                            }
                        };
        Settings.getShared().addPropertyChangeListener( nodeListener );
    }

    // INNERCLAS ------------------------------------------------------
    static class ExplorerView extends JComponent implements ExplorerManager.Provider  {

        private ExplorerManager em;
        private BeanTreeView btv;

        static final long serialVersionUID =-5811911993587966912L;
        ExplorerView( ) {
            Node waitNode = new UpdateNode.Wait();
            createContent( waitNode );
        }

        private void createContent ( Node node ) {

            btv = new BeanTreeView ();
            btv.setPopupAllowed( false );
            btv.setDefaultActionAllowed( true );
            btv.setBorder (UIManager.getBorder ("ScrollPane.border")); // NOI18N

            //sp.add (new org.openide.explorer.view.ListView (), SplittedPanel.ADD_LEFT);
            setLayout (new java.awt.BorderLayout());
            add ( java.awt.BorderLayout.CENTER, btv );

            getExplorerManager ().setRootContext ( node );

            btv.setRootVisible( false );            
        }

        public java.awt.Dimension getPreferredSize () {
            java.awt.Dimension sup = super.getPreferredSize ();
            return new java.awt.Dimension ( Math.max (sup.width, 450), Math.max (sup.height, 300 ));
        }

        void expandAll() {
            expandChildren(getExplorerManager().getRootContext());
        }
        
        public void addNotify () {
            super.addNotify ();
            btv.requestFocus ();
        }
        
        private void expandChildren (Node n) {
            Node[] kids = n.getChildren().getNodes();
            for (int i=0; i < kids.length; i++) {
                if (!kids[i].getDisplayName().startsWith("L10N")) { //NOI18N
                    btv.expandNode(kids[i]);
                    if (kids[i].getChildren() != Children.LEAF) {
                        expandChildren(kids[i]);
                    }
                }
            }
        }

        Node[] getSelectedNodes() {
            return getExplorerManager ().getSelectedNodes();
        }

        public ExplorerManager getExplorerManager () {
            if (em == null) {
                em = new ExplorerManager ();
                
            }
            return em;
        }

    }

    private ModuleUpdate getSingleSelection() {
        if ( jList1.getSelectedIndex() >= 0 )
            // modules from right panel
            return ((ModuleUpdate)selectedListModel.get(jList1.getSelectedIndex()));
        else {
            // modules from left panel
            Node[] selectedNodes = explorerView.getSelectedNodes();
            if ( selectedNodes.length != 1 ) {
                return null;
            }
            else {
                return (ModuleUpdate)selectedNodes[0].getCookie( ModuleUpdate.class );
            }
        }
    }

    private void unselectLeft() {
        try {
            explorerView.getExplorerManager().setSelectedNodes( new Node[0]);
        } catch (java.beans.PropertyVetoException e) {
        }
    }
    
    private String formatDescription (ModuleUpdate mu) {
        Date date = mu.getReleaseDate ();
        String release = date != null ? ModuleUpdate.DATE_FORMAT.format (date) : ""; // NOI18N
        String author = mu.getModuleAuthor () == null ? "" : mu.getModuleAuthor (); //NOI18N
        
        // Module size
        String desc = getBundle ("LAB_226") + SPACE + ( mu.getDownloadSize() / 1024 ) //NOI18N
                        + SPACE + getBundle("CTL_UpdatePanel_KB") + LINE_SEPARATOR +  //NOI18N
        // Author
                ((author.length () == 0) ? "" : getBundle ("LAB_AuthorName") + SPACE + author + LINE_SEPARATOR) + //NOI18N
        // Release Date
                ((release.length () == 0) ? "" : getBundle ("LAB_ReleaseDate") + SPACE + release + LINE_SEPARATOR) + //NOI18N 
        // Description
                mu.getDescription ();
        return desc;
    }
        
    /** Called when the selection in jList1 or explorerView changes */
    private void selectionChange( boolean onLeft ) {
        ModuleUpdate mu;
        
        if ( onLeft && explorerView.getSelectedNodes().length > 0) {
            jList1.clearSelection();
        }
        
        mu = getSingleSelection();

        if ( mu == null ) {
            ivTextField.setText( EMPTY_STRING );
            avTextField.setText( EMPTY_STRING );
            description.setText( EMPTY_STRING );
        }
        else {
            ivTextField.setText( mu.getLocalModule() == null ?
                                       getBundle( "CTL_UpdatePanel_ModuleNotInstalled" ) :
                                       (mu.getLocalModule().getSpecificationVersion() == null ?
                                        "" :
                                        mu.getLocalModule().getSpecificationVersion().toString()));
            SpecificationVersion sv = mu.getRemoteModule().getSpecificationVersion();
            avTextField.setText( sv == null ? "" : sv.toString() );
            description.setText( formatDescription ( mu ) );
            description.setCaretPosition (0);
        }
        
        buttonsCheck();
    }

    private void buttonsCheck() {
        if ( selectedListModel.getSize() > 0 ) {
            removeallButton.setEnabled(true);
            if ( jList1.getSelectedIndex() != -1 )
                removeButton.setEnabled(true);
            else
                removeButton.setEnabled(false);
        } else {
            removeButton.setEnabled(false);
            removeallButton.setEnabled(false);
        }
        
        if ( existUnselectedModule() ) {
            addallButton.setEnabled(true);
            if ( isLeftSelection() )
                addButton.setEnabled(true);
            else
                addButton.setEnabled(false);
        } else {
            addButton.setEnabled(false);
            addallButton.setEnabled(false);
        }
        
        ModuleUpdate mu = getSingleSelection();
        moreButton.setEnabled(mu != null && mu.getHomePage() != null);      
    }
    
    private boolean isLeftSelection() {
        // modules from left panel
        Node[] selectedNodes = explorerView.getSelectedNodes();
        for (int i=0; i<selectedNodes.length; i++)
            if (selectedNodes[i].getCookie( ModuleUpdate.class ) != null)
                return true;
            else {
                // To add whole group - subtree
                List lst = getUnselectedChildren( selectedNodes[i] );
                if ( lst.size() > 0 )
                    return true;
            }
        return false;
    }
    
    /** List of all unselected modules in current group - subtree */
    private List getUnselectedChildren(Node group) {
        Node[] nodes = group.getChildren().getNodes();
        List modlist = new ArrayList();
        for (int i = 0; i < nodes.length; i++) {
            if (nodes[i].getCookie( ModuleUpdate.class ) != null) {
                ModuleUpdate mu = (ModuleUpdate)nodes[i].getCookie( ModuleUpdate.class );
                if ( !mu.isSelected() )
                    modlist.add( mu );
            }
            else {
                List lst = getUnselectedChildren( nodes[i] );
                if ( lst.size() > 0 )
                    modlist.addAll( lst );
            }
        }
        return modlist;
    }

    private boolean addModule( final ModuleUpdate mu ) {
        return addModule( mu, null );
    }
    
    private boolean addModule( final ModuleUpdate mu, final List group ) {
        return addModule( mu, group, true );
    }
    
    /** Adds module to selected list. Shows LicenceAgreement if needed.
     * Increases total size of download. Checks dependencies and adds 
     * all modules needed to satisfy all dependencies.
     *@return False if the licence was rejected
     */
    private boolean addModule( final ModuleUpdate mu, final List group, boolean askForDuplicates ) {

        if ( selectedListModel.contains( mu ) )
            return true; // Already in download list
        
        String prepSpecVersion = PreparedModules.getPrepared().getPreparedVersion( mu.getCodeNameBase() );
        // is this module prepared
        if ( prepSpecVersion != null ) {
            NotifyDescriptor.Confirmation nd = 
                new NotifyDescriptor.Confirmation(
                    NbBundle.getMessage( UpdatePanel.class, "MSG_Prepared_Module",
                                         mu.getName(), prepSpecVersion ),
                    getBundle( "CTL_Prepared_Title" ),
                    NotifyDescriptor.YES_NO_OPTION );
            if ( DialogDisplayer.getDefault().notify( nd ).equals( NotifyDescriptor.YES_OPTION ) ) {                
                PreparedModules.getPrepared().removeModule( mu.getCodeNameBase() );                
            }
            else
                return true;
        }

        // already selected from other server
        boolean remove = false;
        for( int j = 0; j < selectedListModel.size(); j++ ) {
            ModuleUpdate mu2 = (ModuleUpdate)selectedListModel.get(j);
            // bugfix #31764, allow install multiple languages at one time
            String loc = null;
            if (mu instanceof L10NUpdate) {
                loc = ((L10NUpdate) mu).getLangcode ();
            }
            loc = loc == null ? "" : loc; // NOI18N
            String loc2 = null;
            if (mu2 instanceof L10NUpdate) {
                loc2 = ((L10NUpdate) mu2).getLangcode ();
            }
            loc2 = loc2 == null ? "" : loc2; // NOI18N
            if ( mu2.getCodeNameBase().equals( mu.getCodeNameBase() )
                    && loc2.equals( loc ) ) {  // l10n update isn't duplicate
                if ( askForDuplicates ) {
                    if ( duplicateStatus == DuplicateWarningDialog.YES_ALL )
                        remove = true;
                    else if ( duplicateStatus == DuplicateWarningDialog.NO_ALL )
                        return true;
                    else {
                        final java.awt.Dialog warnDialog = DuplicateWarningDialog.getDialog(mu.getName());
                        warnDialog.setVisible (true);
                        duplicateStatus = DuplicateWarningDialog.getResult();
                        if ( duplicateStatus == DuplicateWarningDialog.YES ||                    
                                duplicateStatus == DuplicateWarningDialog.YES_ALL )
                            remove = true;
                        else
                            return true;
                    }
                }
                else
                    remove = true;
            }
            if ( remove ) {
                removeModule( mu2, mu );
                break;
            }
        }

        // View module notification if exists
        if ( mu.getNotification() != null && mu.getNotification().length() > 0 
                && !mu.getNotificationAccepted() ) {
            if ( Notification.performModuleNotification( mu.getNotification() ) ) {
                mu.setNotificationAccepted( true );
            } else {
                return false;
            }
        }
        
        List ext_list = mu.getExternals();
        if ( ext_list != null ) {
            Iterator it = ext_list.iterator();
            while ( it.hasNext() )
                ExternalDialog.showExternal( mu.getName(), (ModuleUpdate.External)it.next() );
        }

        // Module not selected yet and licence accepted

        DependencyChecker dc = new DependencyChecker();
        StringBuffer dontAddModuleName = new StringBuffer();
        Collection modulesToAdd = dc.modulesToAdd( mu, selectedListModel.elements(), group, dontAddModuleName );
        if ( dontAddModuleName.length() > 0 )
            return false;
        
        selectedListModel.add( findPosition( mu ), mu );

        Iterator it = modulesToAdd.iterator();
        while( it.hasNext() ) {
            ModuleUpdate addMu = (ModuleUpdate)it.next();
            if ( !addModule( addMu, null, false ) ) {
                selectedListModel.removeElement( mu );
                totalSize += mu.getDownloadSize();
                return false;
            }
        }

        showSize();
        validator.setValid( selectedListModel.size() > 0 );
        jList1.revalidate();
        
        mu.setSelected( true );
        refreshNodes();
        
        return true;
    }

    /** Shows LicenceAgreement if needed.
     *@return False if the licence was rejected
     */
    boolean checkLicencies() {
        ModuleUpdate mu;
        for( int i = 0; i < selectedListModel.size(); i++ ) {
            mu = (ModuleUpdate)selectedListModel.get(i);

            if ( mu.getLicenceText() != null && mu.getLicenceID() != null ) {

                // Test if the licence was already accepted
                if (!acceptedLicencies.contains (mu.getLicenceID ())) {
                    if (LicenceDialog.acceptLicence (mu.getLicenceText ())) {
                        acceptedLicencies.add (mu.getLicenceID ());
                    } else {
                        return false;
                    }
                }
            }
        }
        return true;
    }
    
    /** Finds the right position of the module in the modules list */
    private int findPosition( ModuleUpdate mu ) {
        int pos = 0;

        Collection modules = Wizard.getAllModules();
        Iterator it = modules.iterator();
        while ( it.hasNext() ) {

            ModuleUpdate imu = ((ModuleUpdate)it.next());

            if ( mu == imu ) {
                return pos;
            }
            if ( selectedListModel.contains( imu ) ) {
                pos++;
            }
        }

        return pos;
    }

    private void removeModule( final ModuleUpdate mu ) {
        removeModule( mu, null );
    }
    
    /** Removes module at position <CODE>index</CODE>. Decreases total
     * size of download. Checks all dependencies and removes all dependent
     * modules.
     */   
    private void removeModule( final ModuleUpdate mu, ModuleUpdate toReplace ) {

        if ( !selectedListModel.contains( mu ) )
            return; // is not selected

        final int index = selectedListModel.indexOf ( mu );
        totalSize -= mu.getDownloadSize();

        selectedListModel.remove( index );
        if ( mu.isDownloadStarted() )
            Downloader.deleteModuleNBM( mu );        

        DependencyChecker dc = new DependencyChecker();
        Collection modulesToRemove = dc.modulesToRemove( mu, toReplace );
        Iterator it = modulesToRemove.iterator();
        while( it.hasNext() ) {
            ModuleUpdate removeMu = (ModuleUpdate)it.next();
            removeModule( removeMu );
        }

        mu.setSelected( false );
        mu.setDownloadOK( false );
        mu.setSecurity( SignVerifier.NOT_CHECKED );
    }

    void showSize() {
        totalSize = 0;

        for( int i = 0; i < selectedListModel.size(); i++ ) {
            if ( !((ModuleUpdate)selectedListModel.get(i)).isDownloadOK() ) {
                totalSize += ((ModuleUpdate)selectedListModel.get(i)).getDownloadSize();
            }
        }

        totalsizeTextField.setText( ( totalSize / 1024 ) + SPACE + getBundle("CTL_UpdatePanel_KB") );
    }

    int modulesToDownload() {
        int result = 0;

        for( int i = 0; i < selectedListModel.size(); i++ ) {
            if ( !((ModuleUpdate)selectedListModel.get(i)).isDownloadOK()
                  ||((ModuleUpdate)selectedListModel.get(i)).isNotChecked()) {
                result++;
            }
        }

        return result;
    }

    private boolean existUnselectedModule() {    
        Collection modules = Wizard.getAllModules();
        Iterator it = modules.iterator();
        while ( it.hasNext() )
            if ( ! ((ModuleUpdate)it.next()).isSelected() )
                return true;
        return false;
    }
    
    void markSelectedModules() {

        // We have firts to unmark all modules
        Collection modules = Wizard.getAllModules();
        Iterator it = modules.iterator();
        while ( it.hasNext() ) {
            ((ModuleUpdate)it.next()).setSelected( false );
        }

        // Then mark the selected

        for( int i = 0; i < selectedListModel.size(); i++ ) {
            ((ModuleUpdate)selectedListModel.get(i)).setSelected( true );
        }
    }
    
    private void refreshNodes() {
        if ( allNodes == null ) {
            allNodes = UpdateNode.getAllServers();
            explorerView.getExplorerManager ().setRootContext (allNodes);
            explorerView.expandAll();
            Runnable makeVisible = new Runnable () {
                public void run () {
                    if (! allNodes.isLeaf ()) {
                        explorerView.getExplorerManager ().setExploredContext (allNodes.getChildren ().getNodes ()[0]);
                    }
                }
            };
            RequestProcessor.getDefault ().post (makeVisible, 300);
        }
        else
            allNodes.refreshContent( true );
    }
    
    void removeListeners() {
        if ( nodeListener != null )
            Settings.getShared().removePropertyChangeListener( nodeListener );
    }
    
    private static String getBundle( String key ) {
        return NbBundle.getMessage( UpdatePanel.class, key );
    }
}
