/*
 *  PHEX - The pure-java Gnutella-servent.
 *  Copyright (C) 2001 - 2008 Phex Development Group
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * 
 *  --- SVN Information ---
 *  $Id: LibraryTreePane.java 4412 2009-03-22 13:40:44Z ArneBab $
 */
package phex.gui.tabs.library;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import java.io.IOException;

import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.ToolTipManager;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;

import org.apache.commons.lang.SystemUtils;

import phex.common.Environment;
import phex.common.log.NLogger;
import phex.gui.actions.FWAction;
import phex.gui.actions.GUIActionPerformer;
import phex.gui.common.FWPopupMenu;
import phex.gui.common.FWToolBar;
import phex.gui.common.FileDialogHandler;
import phex.gui.common.GUIRegistry;
import phex.prefs.core.LibraryPrefs;
import phex.servent.Servent;
import phex.share.SharedDirectory;
import phex.share.SharedFilesService;
import phex.utils.DirectoryOnlyFileFilter;
import phex.utils.Localizer;
import phex.utils.SystemShellExecute;

import com.jgoodies.forms.builder.PanelBuilder;
import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;

public class LibraryTreePane extends JPanel
{
    private Component parent;
    private SharingTreeModel sharingTreeModel;
    private JTree mainTree;
    private FWPopupMenu fileTreePopup;
    
    private AddShareFolderAction addShareFolderAction;
    private RemoveShareFolderAction removeShareFolderAction;
    private ExploreFolderAction exploreFolderAction;
    
    public LibraryTreePane( Component parent )
    {
        this.parent = parent;
        prepareComponent();
    }
    
    public void addTreeSelectionListener( TreeSelectionListener listener )
    {
        mainTree.getSelectionModel().addTreeSelectionListener( listener );
    }
    
    public void appendPopupSeparator()
    {
        fileTreePopup.addSeparator();
    }
    
    public void appendPopupAction( FWAction action )
    {
        fileTreePopup.addAction( action );
    }
    
    public LibraryNode getSelectedTreeComponent()
    {
        TreePath path = mainTree.getSelectionPath();
        if (path == null)
        {
            return null;
        }
        LibraryNode node = (LibraryNode) path.getLastPathComponent();
        return node;
    }
    
    public void updateFileSystem()
    {
        sharingTreeModel.updateFileSystem();
    }
    
    /**
     * 
     */
    private void refreshActions()
    {
        addShareFolderAction.refreshActionState();
        removeShareFolderAction.refreshActionState();
        if ( exploreFolderAction != null )
        {
            exploreFolderAction.refreshActionState();
        }
    }
    
    private void prepareComponent()
    {
        CellConstraints cc = new CellConstraints();
        FormLayout layout = new FormLayout("fill:d:grow", // columns
            "fill:d:grow, 1dlu, p"); //rows
        PanelBuilder tabBuilder = new PanelBuilder( layout, this );
        
        sharingTreeModel = new SharingTreeModel();
        mainTree = new JTree( sharingTreeModel );
        mainTree.setMinimumSize( new Dimension( 0, 0 ) );
        mainTree.setRowHeight(0);
        mainTree.setCellRenderer(new SharingTreeRenderer());
        mainTree.addMouseListener( new MouseHandler() );
        
        mainTree.getSelectionModel().addTreeSelectionListener(
            new SelectionHandler());
        ToolTipManager.sharedInstance().registerComponent( mainTree );
        
        // open up first level of nodes
        TreeNode root = (TreeNode) sharingTreeModel.getRoot();
        int count = root.getChildCount();
        for ( int i = 0; i < count; i++ )
        {
            mainTree.expandPath( new TreePath( new Object[] {root, root.getChildAt(i)} ) );
        }

        JScrollPane treeScrollPane = new JScrollPane(mainTree);
        tabBuilder.add(treeScrollPane, cc.xywh(1, 1, 1, 1));
        
        FWToolBar shareToolbar = new FWToolBar(FWToolBar.HORIZONTAL);
        shareToolbar.setBorderPainted(false);
        shareToolbar.setFloatable(false);
        tabBuilder.add(shareToolbar, cc.xy(1, 3));
        
        addShareFolderAction = new AddShareFolderAction();
        shareToolbar.addAction( addShareFolderAction );
        
        removeShareFolderAction = new RemoveShareFolderAction();
        shareToolbar.addAction( removeShareFolderAction );
        
        if ( SystemUtils.IS_OS_WINDOWS || SystemUtils.IS_OS_MAC_OSX )
        {
            exploreFolderAction = new ExploreFolderAction();
        }
        
        fileTreePopup = new FWPopupMenu();
        fileTreePopup.addAction( addShareFolderAction );
        fileTreePopup.addAction( removeShareFolderAction );
        
        if ( SystemUtils.IS_OS_WINDOWS || SystemUtils.IS_OS_MAC_OSX )
        {
            fileTreePopup.addAction( exploreFolderAction );
        }
    }
    
    

    /**
     * Starts a download.
     */
    private class AddShareFolderAction extends FWAction
    {
        AddShareFolderAction()
        {
            super( Localizer.getString("LibraryTab_Share"), 
                GUIRegistry.getInstance().getPlafIconPack().getIcon( "Library.ShareFolder"), 
                Localizer.getString("LibraryTab_TTTShare"));
        }

        public void actionPerformed(ActionEvent e)
        {
            try
            {
                File currentDirectory = null;
                TreePath selectionPath = mainTree.getSelectionPath();
                if ( selectionPath != null )
                {
                    Object lastPathComponent = selectionPath.getLastPathComponent();
                    if ( lastPathComponent instanceof LibraryNode )
                    {
                        currentDirectory = ((LibraryNode)lastPathComponent).getSystemFile();
                    }
                }
                
                final File[] files = FileDialogHandler.openMultipleDirectoryChooser( 
                    parent,
                    Localizer.getString( "LibraryTab_SelectDirectoryToShare" ),
                    Localizer.getString( "LibraryTab_Select" ), 
                    Localizer.getChar( "LibraryTab_SelectMnemonic" ),
                    currentDirectory,
                    Localizer.getString( "LibraryTab_CopyrightWarnTitle" ),
                    Localizer.getString( "LibraryTab_CopyrightWarnMessage" ) );
                if ( files == null )
                {
                    return;
                }
                Runnable runner = new Runnable()
                {
                    public void run()
                    {
                        try
                        {
                            for ( int i = 0; i < files.length; i++ )
                            {
                                if ( files[i] != null )
                                {
                                    shareDirRecursive(files[i]);
                                }
                            }
                            GUIActionPerformer.rescanSharedFiles();
                        }
                        catch ( Throwable th )
                        {
                            NLogger.error( LibraryTreePane.class, th, th );
                        }
                    }
                };
                Environment.getInstance().executeOnThreadPool(runner, "AddShareFolderAction");
            }
            catch ( Throwable th )
            {
                NLogger.error( LibraryTreePane.class, th, th );
            }
        }

        @Override
        public void refreshActionState()
        {
        }
        
        private void shareDirRecursive(File file)
        {
            if (!file.isDirectory())
            {
                return;
            }
            LibraryPrefs.SharedDirectoriesSet.get().add( file.getAbsolutePath() );
            LibraryPrefs.SharedDirectoriesSet.changed();
            File[] dirs = file.listFiles(new DirectoryOnlyFileFilter());
            if ( dirs == null )
            {// not a valid path or an IO error.
                return;
            }
            for (int i = 0; i < dirs.length; i++)
            {
                shareDirRecursive(dirs[i]);
            }
        }
    }
    
    private class RemoveShareFolderAction extends FWAction
    {
        RemoveShareFolderAction()
        {
            super( Localizer.getString("LibraryTab_StopShare"), 
                GUIRegistry.getInstance().getPlafIconPack().getIcon("Library.ShareFolderClear"), 
                Localizer.getString("LibraryTab_TTTStopShare"));
            refreshActionState();
        }

        public void actionPerformed(ActionEvent e)
        {
            TreePath selectionPath = mainTree.getSelectionPath();
            if ( selectionPath == null )
            {
                return;
            }
            Object lastPathComponent = selectionPath.getLastPathComponent();
            if ( !(lastPathComponent instanceof LibraryNode ))
            {
                return;
                
            }
            final File file = ((LibraryNode)lastPathComponent).getSystemFile();
            if ( file == null )
            {
                return;
            }
            Runnable runner = new Runnable()
            {
                public void run()
                {
                    stopShareDirRecursive(file);
                    GUIActionPerformer.rescanSharedFiles();
                }
            };
            Environment.getInstance().executeOnThreadPool(runner, "RemoveShareFolderAction");
            refreshActionState();
        }
        
        private void stopShareDirRecursive(File file)
        {
            if (!file.isDirectory())
            {
                return;
            }
            LibraryPrefs.SharedDirectoriesSet.get().remove( file.getAbsolutePath() );
            LibraryPrefs.SharedDirectoriesSet.changed();
            SharedFilesService sharedFilesService = GUIRegistry.getInstance().getServent().getSharedFilesService();
            SharedDirectory directory = sharedFilesService.getSharedDirectory(file);
            if (directory == null)
            {
                // in case there is no shared directory here..
                // we can assume there is no shared sub-directory available.
                return;
            }
            
            File[] dirs = file.listFiles(new DirectoryOnlyFileFilter());
            for (int i = 0; i < dirs.length; i++)
            {
                stopShareDirRecursive(dirs[i]);
            }
        }

        @Override
        public void refreshActionState()
        {
            TreePath selectionPath = mainTree.getSelectionPath();
            if ( selectionPath == null )
            {
                setEnabled(false);
                return;
            }
            Object lastPathComponent = selectionPath.getLastPathComponent();
            if ( !(lastPathComponent instanceof LibraryNode ))
            {
                setEnabled(false);
                return;
            }
            File file = ((LibraryNode)lastPathComponent).getSystemFile();
            if ( file == null )
            {
                setEnabled(false);
                return;
            }
            setEnabled(true);
        }
    }
    
    private class ExploreFolderAction extends FWAction
    {
        ExploreFolderAction()
        {
            super(Localizer.getString("LibraryTab_Explore"), 
                GUIRegistry.getInstance().getPlafIconPack().getIcon("Library.Explore"),
                Localizer.getString("LibraryTab_TTTExplore"));
        }
        
        public void actionPerformed(ActionEvent e)
        {
            TreePath selectionPath = mainTree.getSelectionPath();
            if ( selectionPath == null )
            {
                return;
            }
            Object lastPathComponent = selectionPath.getLastPathComponent();
            if ( !(lastPathComponent instanceof LibraryNode ))
            {
                return;
                
            }
            final File dir = ((LibraryNode)lastPathComponent).getSystemFile();
            if ( dir == null )
            {
                return;
            }
            
            try
            {
                SystemShellExecute.exploreFolder(dir);
            }
            catch (IOException exp)
            {// ignore and do nothing..
            }
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void refreshActionState()
        {
            TreePath selectionPath = mainTree.getSelectionPath();
            if ( selectionPath == null )
            {
                setEnabled(false);
                return;
            }
            Object lastPathComponent = selectionPath.getLastPathComponent();
            if ( !(lastPathComponent instanceof LibraryNode ))
            {
                setEnabled(false);
                return;
            }
            File file = ((LibraryNode)lastPathComponent).getSystemFile();
            if ( file == null )
            {
                setEnabled(false);
                return;
            }
            setEnabled(true);
        }
    }
    
    private class SelectionHandler implements TreeSelectionListener
    {
        public void valueChanged( TreeSelectionEvent e )
        {
            final Object treeRoot = sharingTreeModel.getRoot();
            final Object lastPathComponent = e.getPath().getLastPathComponent();
            if ( lastPathComponent == treeRoot )
            {
                EventQueue.invokeLater( new Runnable()
                {
                    public void run()
                    {
                        Object[] path = new Object[]
                        { treeRoot, sharingTreeModel.getChild( treeRoot, 0 ) };
                        mainTree.setSelectionPath( new TreePath( path ) );
                    }
                } );
                return;
            }
            refreshActions();
        }
    }

    private class MouseHandler extends MouseAdapter implements MouseListener
    {
        @Override
        public void mouseClicked( MouseEvent e )
        {
        }

        @Override
        public void mouseReleased( MouseEvent e )
        {
            if ( e.isPopupTrigger() )
            {
                popupMenu( (Component) e.getSource(), e.getX(), e.getY() );
            }
        }

        @Override
        public void mousePressed( MouseEvent e )
        {
            if ( e.isPopupTrigger() )
            {
                popupMenu( (Component) e.getSource(), e.getX(), e.getY() );
            }
        }

        private void popupMenu( Component source, int x, int y )
        {
            refreshActions();
            fileTreePopup.show( source, x, y );
        }
    }
}