/**
 * Created on Jun 18, 2005
 * $id:  $
 */
package org.jvnet.lookout.ui.core.search;

import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.viewers.AbstractTreeViewer;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.TableTreeViewer;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IPartListener;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.part.ViewPart;
import org.jvnet.lookout.core.model.SearchResultProperty;
import org.jvnet.lookout.ui.core.Plugin;
import org.jvnet.lookout.ui.core.controller.SearchController;
import org.jvnet.lookout.ui.core.preference.Preferences;
import org.jvnet.lookout.ui.core.tree.SearchResultDetail;
import org.jvnet.lookout.ui.core.tree.SearchResultItem;
import org.jvnet.lookout.ui.core.tree.SearchResultsContentProvider;
import org.jvnet.lookout.ui.core.tree.filter.detail.PropertyFilter;
import org.jvnet.lookout.ui.core.tree.label.DefaultLabelProvider;
import org.jvnet.lookout.ui.core.tree.sort.SearchResultItemSorter;
import org.jvnet.lookout.ui.core.tree.sort.SearchResultsSorter;
import org.jvnet.lookout.ui.core.tree.sort.detail.DefaultDetailSorter;
import org.jvnet.lookout.ui.core.tree.sort.item.AlphabeticalSorter;
import org.jvnet.lookout.ui.core.tree.sort.item.NoSortingSorter;
import org.jvnet.lookout.ui.core.tree.sort.item.TypeSorter;
import org.jvnet.lookout.ui.core.util.Utilities;

public class SearchResultsView extends ViewPart implements IPropertyChangeListener {
    
    private AbstractTreeViewer searchResultsTreeViewer;
    private Text searchField;
    private Button searchButton;
    
    private Action searchAction;
    private Action openSearchResultAction;
    private Action copySearchResultAction;
    
    private Action clearSearchResultsAction;
    private Action clearKeywordsAction;
    private Action nextResultsAction;
    private Action previousResultsAction;
    
    private Group searchResultsGroup;
    private Group searchGroup;
    
    private SearchResultsSorter sorter;
    private ILabelProvider labelProvider;
    
    public SearchResultsView() {
    	sorter = new SearchResultsSorter();
    	labelProvider = new DefaultLabelProvider();
    	partListener = new PartListener();
    }
    
    /**
     * This is a callback that will allow us
     * to create the searchResultsTreeViewer and initialize it.
     */
    public void createPartControl(Composite parent) {
        
        //Setup the layout...
        parent.setLayout(new GridLayout(2, false));
        
        //Setup controls section...
        createControlsSectionGroup(parent);
        
        //Update the representation of controls section...        
        updateControlsSection();
        
        //Create results section...
        createResultsSectionGroup(parent);        
        
        //Update the representation of results section...        
        updateResultsSection();
        
        Plugin.getDefault().getPreferenceStore().addPropertyChangeListener(this);
        
        //Create actions to be used...
        createActions();
        
        //Install the context menu options...
        hookContextMenu();
        
        //Install 2x click action...
        hookDoubleClickAction();
        
        //Populate action bars...
        fillActionBars();

        //Add part listener that ensures the focus is always given to the
        //keywords field
        getViewSite().getPage().addPartListener(partListener);              
    }    
    
    private PartListener partListener;
    
    private class PartListener implements IPartListener {
    	
    	public void partActivated(IWorkbenchPart part) {
    		if(part instanceof SearchResultsView) {
    			//Give the focus to the text field... 
    			searchField.forceFocus();
    		}
    	}
    
    	public void partBroughtToTop(IWorkbenchPart part) {}
    	
    	public void partClosed(IWorkbenchPart part) {
    		getViewSite().getPage().removePartListener(this);
    	}
    	
    	public void partDeactivated(IWorkbenchPart part) {}
    	
    	public void partOpened(IWorkbenchPart part) {};
    }
    
    private void createControlsSectionGroup(Composite parent) {
        //Create the group for the search controls...
        searchGroup = new Group (parent, SWT.NONE);
        searchGroup.setText (Plugin.getResourceString("search-title"));
        searchGroup.setLayout (new GridLayout(2, false));
        GridData searchGridData = new GridData (GridData.FILL_HORIZONTAL);
        searchGroup.setLayoutData (searchGridData);        
    }

    private void updateControlsSection() {
        
    	//Keywords field
        searchField = new Text(searchGroup, SWT.BORDER | SWT.SINGLE | SWT.V_SCROLL | SWT.H_SCROLL);
        searchField.setLayoutData(new GridData (GridData.FILL_HORIZONTAL));
        
        searchField.addKeyListener(new KeyAdapter() {
        	
        	@Override
            public void keyReleased(KeyEvent e) {
            
        		boolean isNotEmpty = !(searchField.getText().trim().length() == 0);
        		
        		clearKeywordsAction.setEnabled(isNotEmpty);
        		//searchButton.setEnabled(isNotEmpty);
        		
        		if(isNotEmpty) {
        			if(e.keyCode == SWT.Selection) {
        				//Do search...
        				searchAction.run();                   
        			}
        		} 
            }        	
        });
        
        //Search button
        searchButton = new Button(searchGroup, SWT.PUSH);
        searchButton.setImage(Utilities.getImage(Plugin.getResourceString("search-button-icon"), Plugin.getDefault()));
        searchButton.addSelectionListener (new SelectionAdapter() {
        	public void widgetSelected(SelectionEvent e) {
                
                //Do search...
                searchAction.run();
                
                //Release the focus from the search button 
                searchField.setFocus();
            }
        });
    }

    private void createResultsSectionGroup(Composite parent) {
    	
        //Create the group for the Search Results 
        searchResultsGroup = new Group (parent, SWT.NONE);
        searchResultsGroup.setText(Plugin.getResourceString("search-results-title"));
        
        searchResultsGroup.setLayout (new GridLayout ());
        GridData groupGridData = new GridData (GridData.FILL_BOTH);
        groupGridData.horizontalSpan = 2;
        searchResultsGroup.setLayoutData (groupGridData);        
    }

    private void updateResultsSection() {
    	
    	//obtain the viewer...
        searchResultsTreeViewer = obtainViewer(searchResultsGroup);
        
        //Update view config...
        updateViewerConfiguration();        

        //Set the input...
        searchResultsTreeViewer.setInput(getViewSite());
    }
    
    
    private void updateViewerConfiguration() {

    	updateLabelProvider();

    	updateSorter();
    	
    	updateFilters();
    	
    	updateContentProvider();        
    }

    private void updateLabelProvider() {
    }
    
    private void updateSorter() {
    	
    	String groupingMode = getGroupingMode();
    	
    	SearchResultItemSorter itemSorter = null;
    	
    	if(Preferences.AlphabeticalMode.equals(groupingMode)) {
    		itemSorter = new AlphabeticalSorter();    		    		
    	} else if(Preferences.ResourceTypeMode.equals(groupingMode)){    		
    		itemSorter = new TypeSorter();    		
    	} else if(Preferences.SearchEngineMode.equals(groupingMode)) {    		
    		itemSorter = new NoSortingSorter();    		
    	} else {
    		//Default to no sorting for unknown modes...
    		itemSorter = new NoSortingSorter(); 
    	}
    	
    	sorter.setSearchResultItemSorter(itemSorter);
    	
    	if(sorter.getSearchResultDetailSorter() == null) {
    		sorter.setSearchResultDetailSorter(new DefaultDetailSorter());  
    	}
    }
    
    private void updateFilters() {
    	
    	if (searchResultsTreeViewer.getFilters().length == 0) {
    		
    		//Use custom filter for the result details...
    		searchResultsTreeViewer.addFilter(new PropertyFilter());
    		
    		//Use custom filter for the result items...
    		//CXB could be used to hide the visibility of result items...
    		//searchResultsTreeViewer.addFilter(new SourceFilter());
    	}
    }

    private void updateContentProvider() {    	
    	
        if(searchResultsTreeViewer.getContentProvider() == null) {
        	searchResultsTreeViewer.setContentProvider(new SearchResultsContentProvider(this, searchResultsTreeViewer));
        } else {        
        	((SearchResultsContentProvider)searchResultsTreeViewer.getContentProvider()).reload();
        }
    }

    private AbstractTreeViewer obtainViewer(Composite parent) {
    	
    	if(searchResultsTreeViewer == null) {
    		searchResultsTreeViewer = new TreeViewer(parent);	
            searchResultsTreeViewer.setSorter(sorter);
    		searchResultsTreeViewer.setLabelProvider(labelProvider);
    		
            //Setup the layout...
            GridData treeGridData = new GridData (GridData.FILL_BOTH);
            treeGridData.horizontalSpan = 2;
            
            setLayoutData(treeGridData);
    	}
    	return searchResultsTreeViewer;
    }
    
    private void setLayoutData(GridData gridData) {
    	
    	//This is either a TreeViewer or a TableTreeViewer... 
    	if(searchResultsTreeViewer instanceof TreeViewer) {
    		((TreeViewer)searchResultsTreeViewer).getTree().setLayoutData(gridData);
    	} else if (searchResultsTreeViewer instanceof TableTreeViewer) {
    		((TableTreeViewer)searchResultsTreeViewer).getTableTree().setLayoutData(gridData);
    	}
    }
    
    private String getGroupingMode() {
    	return Plugin.getDefault().getPreferenceStore().getString(Preferences.GroupingMode);
    }
    
    public void propertyChange(org.eclipse.jface.util.PropertyChangeEvent event) {
    	String property = event.getProperty();
    	
    	if(Preferences.GroupingMode.equals(property)) {
    		
    		updateResultsSection();
    	}
    }    

    public Text getKeywords() {
        return this.searchField;
    }

    public void setKeywords(Text searchField) {
        this.searchField = searchField;
    }   

    private void hookContextMenu() {
        MenuManager menuMgr = new MenuManager("PopupMenu");
        menuMgr.setRemoveAllWhenShown(true);
        
        menuMgr.addMenuListener(new IMenuListener() {
            public void menuAboutToShow(IMenuManager manager) {
                SearchResultsView.this.fillContextMenu(manager);
            }
        });
        Menu menu = menuMgr.createContextMenu(searchResultsTreeViewer.getControl());
        searchResultsTreeViewer.getControl().setMenu(menu);
        getSite().registerContextMenu(menuMgr, searchResultsTreeViewer);
    }
    
    //Fill the popup menu..
    private void fillContextMenu(IMenuManager manager) {
        
        ISelection selection = searchResultsTreeViewer.getSelection();
        
        Object obj = ((IStructuredSelection) selection).getFirstElement();
        
        //We only add the open or copy search result item if there is a item result selected
        if(obj instanceof SearchResultItem) {                    
            manager.add(openSearchResultAction);
            manager.add(copySearchResultAction);
        }
        
        // Other plug-ins can contribute there actions here
        manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
    }
    
    private void fillActionBars() {
        IActionBars bars = getViewSite().getActionBars();
        fillLocalPullDown(bars.getMenuManager());
        fillLocalToolBar(bars.getToolBarManager());
    }
    
    private void fillLocalPullDown(IMenuManager manager) {        
//        manager.add(openPreferencesPageAction);
//        manager.add(openDocumentationAction);
    }
    
    private void fillLocalToolBar(IToolBarManager manager) {
        manager.add(clearKeywordsAction);
        manager.add(clearSearchResultsAction);

        manager.add(previousResultsAction);
        manager.add(nextResultsAction);
    }
    
    private void hookDoubleClickAction() {
        searchResultsTreeViewer.addDoubleClickListener(new IDoubleClickListener() {
            public void doubleClick(DoubleClickEvent event) {
                openSearchResultAction.run();
            }
        });
    }
    
    /**
     * Passing the focus request to the searchResultsTreeViewer's control.
     */
    public void setFocus() {
        searchResultsTreeViewer.getControl().setFocus();
    }
    
    public AbstractTreeViewer getSearchResultsTreeViewer() {
        return this.searchResultsTreeViewer;
    }
    
    public void setSearchResultsTree(TreeViewer searchResultsTree) {
        this.searchResultsTreeViewer = searchResultsTree;
    }
    
    public void updateSearchResultsControls() {
    	
    	boolean hasFoundResults = SearchController.getHasFoundResults();
		nextResultsAction.setEnabled(hasFoundResults);
		previousResultsAction.setEnabled(hasFoundResults);
		clearSearchResultsAction.setEnabled(hasFoundResults);		
    }
    
    private void createActions() {
        
        //Search action...
        searchAction = new Action() {
            public void run() {
                
            	if(searchField.getText().length()>0) {
            		//Delegate to controller
            		SearchController.triggerAction(SearchController.Action.SEARCH_EXECUTION, SearchResultsView.this);                  
            		
            		//possibly enable these...
            		boolean hasFoundResults = SearchController.getHasFoundResults();
            		nextResultsAction.setEnabled(hasFoundResults);
            		previousResultsAction.setEnabled(hasFoundResults);
            		clearSearchResultsAction.setEnabled(hasFoundResults);
            	}                
            }
        };
		Plugin plugin = Plugin.getDefault();
        
        searchAction.setText(Plugin.getResourceString("search-action-text"));
        searchAction.setToolTipText(Plugin.getResourceString("search-action-tooltip"));
        searchAction.setImageDescriptor(Utilities.getImageDescriptor(Plugin.getResourceString("search-action-icon"), plugin));
        
        searchAction.setEnabled(false);
        
        //Open Search result action...
        openSearchResultAction = new Action() {
            public void run() {
                
                ISelection selection = searchResultsTreeViewer.getSelection();
                Object obj = ((IStructuredSelection) selection).getFirstElement();
                
                //We only process the 2x clicks on result items
                if(!(obj instanceof SearchResultItem) && (obj instanceof SearchResultDetail)) {
                    obj = ((SearchResultDetail)obj).getParent();
                }
                
                //Delegate to controller
                SearchController.triggerAction(SearchController.Action.OPEN_SEARCH_RESULT, SearchResultsView.this, obj);                                    
            }
        };
        
        openSearchResultAction.setText(Plugin.getResourceString("open-action-text"));
        openSearchResultAction.setToolTipText(Plugin.getResourceString("open-action-tooltip"));
        openSearchResultAction.setImageDescriptor(Utilities.getImageDescriptor(Plugin.getResourceString("open-action-icon"), plugin));    
        //openSearchResultAction.setAccelerator(SWT.CTRL|'O');

        clearSearchResultsAction = new Action() {
            public void run() {
                
                //Delegate to controller
                SearchController.triggerAction(SearchController.Action.CLEAR_SEARCH_RESULTS, SearchResultsView.this);
                
                //Disable these...
                nextResultsAction.setEnabled(false);
                previousResultsAction.setEnabled(false);
                this.setEnabled(false);
            }
        };
        
        clearSearchResultsAction.setText(Plugin.getResourceString("clear-results-action-text"));
        clearSearchResultsAction.setToolTipText(Plugin.getResourceString("clear-results-action-tooltip"));
        clearSearchResultsAction.setImageDescriptor(Utilities.getImageDescriptor(Plugin.getResourceString("clear-results-action-icon"), plugin));
        
        clearSearchResultsAction.setEnabled(false);
        
        clearKeywordsAction = new Action() {
            public void run() {
                
                //Delegate to controller
                SearchController.triggerAction(SearchController.Action.CLEAR_KEYWORDS, SearchResultsView.this);
                
                clearKeywordsAction.setEnabled(false);
            }
        };
        
        clearKeywordsAction.setText(Plugin.getResourceString("clear-keywords-action-text"));
        clearKeywordsAction.setToolTipText(Plugin.getResourceString("clear-keywords-action-tooltip"));
        clearKeywordsAction.setImageDescriptor(Utilities.getImageDescriptor(Plugin.getResourceString("clear-keywords-action-icon"), plugin));        
        clearKeywordsAction.setEnabled(false);
        
        //Previous results action...
        previousResultsAction = new Action() {
            public void run() {
                
                //Delegate to controller
                SearchController.triggerAction(SearchController.Action.PREVIOUS_RESULTS, SearchResultsView.this);                  
            }
        };
        
        previousResultsAction.setText(Plugin.getResourceString("previous-results-action-text"));
        previousResultsAction.setToolTipText(Plugin.getResourceString("previous-results-action-tooltip"));
        previousResultsAction.setImageDescriptor(Utilities.getImageDescriptor(Plugin.getResourceString("previous-results-action-icon"), plugin));

        //Next results action...
        nextResultsAction = new Action() {
            public void run() {
                
                //Delegate to controller
                SearchController.triggerAction(SearchController.Action.NEXT_RESULTS, SearchResultsView.this);                  
            }
        };
        
        nextResultsAction.setText(Plugin.getResourceString("next-results-action-text"));
        nextResultsAction.setToolTipText(Plugin.getResourceString("next-results-action-tooltip"));
        nextResultsAction.setImageDescriptor(Utilities.getImageDescriptor(Plugin.getResourceString("next-results-action-icon"), plugin));
        
        //By default this are disabled...
        nextResultsAction.setEnabled(false);
        previousResultsAction.setEnabled(false);
        
        
        //Copy URL action...
        copySearchResultAction = new Action() {
            public void run() {
                
                ISelection selection = searchResultsTreeViewer.getSelection();
                Object obj = ((IStructuredSelection) selection).getFirstElement();
                
                if(!(obj instanceof SearchResultItem) && (obj instanceof SearchResultDetail)) {
                    obj = ((SearchResultDetail)obj).getParent();
                }
                
                SearchResultItem item = (SearchResultItem)obj;
                
                String url = (String)item.getPropertyValue(SearchResultProperty.URL);

                Utilities.copyTextToClipboard(url);
            }
        };
        
        copySearchResultAction.setText(Plugin.getResourceString("copy-action-text"));
        copySearchResultAction.setToolTipText(Plugin.getResourceString("copy-action-tooltip"));
        copySearchResultAction.setImageDescriptor(Utilities.getImageDescriptor(Plugin.getResourceString("copy-action-icon"), plugin));    
        //copySearchResultAction.setAccelerator(SWT.CTRL|'C');
    }
}