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

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.eclipse.jface.viewers.AbstractTreeViewer;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.ui.part.ViewPart;
import org.jvnet.lookout.core.adapter.SearchAdapter;
import org.jvnet.lookout.core.model.SearchResultProperty;
import org.jvnet.lookout.core.model.SearchResultSource;
import org.jvnet.lookout.core.model.SearchResultType;
import org.jvnet.lookout.ui.core.Plugin;
import org.jvnet.lookout.ui.core.preference.Preferences;
import org.jvnet.lookout.ui.core.util.Utilities;

public class SearchResultsContentProvider implements ITreeContentProvider {
    
    private TreeParent invisibleRoot;
    
    private ViewPart viewPart;
    
    private AbstractTreeViewer treeViewer;
    
    private org.jvnet.lookout.core.Plugin searchCorePlugin;
    
    public SearchResultsContentProvider(ViewPart viewPart, AbstractTreeViewer treeViewer) {
        
        this.viewPart = viewPart; 
        this.treeViewer = treeViewer;
        
        invisibleRoot = new TreeParent("");
        
        searchCorePlugin = org.jvnet.lookout.core.Plugin.getDefault();    	
    }
    
    public void setContent(List<TreeObject> searchItemsList) {
        
    	if (searchItemsList == null) {
    		return;
    	}
    	
        //CXB would be good to batch this to prevent events from being fired needlessly..

		backupNewList(searchItemsList);

    	if(needsReArranging()) {
    		
    		searchItemsList = reArrangeIfNeeded(searchItemsList);
    	}
    	
    	//Clear current content...
        invisibleRoot.clear();
        
        //Populate with new content...
        for (TreeObject treeObject : searchItemsList) {
            //Populate the content...
            invisibleRoot.addChild(treeObject);
        }
        
        //Update the UI
        treeViewer.refresh(true);        
    }
    
    
    private void backupNewList(List<TreeObject> newList) {
    		
    	if(newList != null) {
    		backup = new ArrayList<TreeObject>(newList.size());
    		
    		for (TreeObject object : newList) {
    			backup.add((TreeObject)Utilities.copy(object));
    		}    
    	}
    }
    
    public void reload() {
    	setContent(backup);
    }
    
    private List<TreeObject> backup;
    
    private boolean needsReArranging() {
    	String groupingMode = getGroupingMode();
    	return 
    	(Preferences.SearchEngineMode.equals(groupingMode)) ||
        (Preferences.ResourceTypeMode.equals(groupingMode));
    }
    private List<TreeObject> reArrangeIfNeeded(List<TreeObject> searchItemsList) {
    	
    	String groupingMode = getGroupingMode();
    	
    	List<TreeObject> reArrangedList = null;    	
    		
    	if(Preferences.SearchEngineMode.equals(groupingMode)) {
    		
    		reArrangedList = bucketObjects(searchItemsList, SearchResultProperty.SOURCE);
    		
    	} else if (Preferences.ResourceTypeMode.equals(groupingMode)) {
    		
    		reArrangedList = bucketObjects(searchItemsList, SearchResultProperty.TYPE);
    		
    	} else {
        	//If this is in alphabetical or not specified, then there is no 
    		//need to rearrange
    		reArrangedList = searchItemsList;
    	}
    	
    	return reArrangedList;
    }
    
    private List<TreeObject> bucketObjects(List<TreeObject> searchItemsList, SearchResultProperty property) {
    	
    	Map<Object, TreeParent> categoryToParentMap = new HashMap<Object, TreeParent>();
    	
    	List<TreeObject> reArrangedList = new ArrayList<TreeObject>();
    	
    	for (Iterator iter = searchItemsList.iterator(); iter.hasNext();) {
    		
			SearchResultItem item = (SearchResultItem) iter.next();
			
			Object categoryValue = item.getPropertyValue(property);
			
			TreeParent newParent = categoryToParentMap.get(categoryValue);
			
			if(newParent == null) {				
				//Find name for it...
				String parentName = getNameForParent(categoryValue);
				
				//Create the new parent...
				newParent = new SearchResultItem(parentName);
				
				//This ensures smooth, label processing... see AbstractTreeLabelProvider
				((SearchResultItem)newParent).setPropertyValue(property, categoryValue, true);

				((SearchResultItem)newParent).setPropertyValue
				(SearchResultProperty.SOURCE, item.getPropertyValue(SearchResultProperty.SOURCE), true);

				//Update map
				categoryToParentMap.put(categoryValue, newParent);
				
				//Add to rearranged list...
				reArrangedList.add(newParent);
			}
			
			//Add to new parent...
			newParent.addChild(item);			
		}
    	
    	return reArrangedList;
    }
    
    private String getNameForParent(Object categoryValue) {
    	
    	String name = "";
    	
    	if(categoryValue instanceof SearchResultSource) {
    		SearchAdapter adapter = searchCorePlugin.getSearchAdapter((SearchResultSource)categoryValue);
    		name = searchCorePlugin.getSearchAdapterName(adapter);
    	} else if (categoryValue instanceof SearchResultType) {
    		
    		if(categoryValue == SearchResultType.EMAIL) {
    			name = SearchResultType.EMAIL.getName() + "s";
    		} else if(categoryValue == SearchResultType.OTHER) {
    			name = "Other files";
    		} else {
    			name = ((SearchResultType)categoryValue).getName() + " files"; 
    		}    		
    	}    	
    	return name;
    }
    
    private String getGroupingMode() {
    	return Plugin.getDefault().getPreferenceStore().getString(Preferences.GroupingMode);
    }
    
    ///////////////////////////////////////////////////////////////////////////
    //IStructuredContentProvider
    ///////////////////////////////////////////////////////////////////////////

    public Object[] getElements(Object parent) {
        
        if (parent.equals(viewPart.getViewSite())) {
            return getChildren(invisibleRoot);
        }
        
        return getChildren(parent);
    }

    ///////////////////////////////////////////////////////////////////////////
    //ITreeContentProvider
    ///////////////////////////////////////////////////////////////////////////

    public Object getParent(Object child) {
        if (child instanceof TreeObject) {
            return ((TreeObject) child).getParent();
        }
        return null;
    }

    public Object[] getChildren(Object parent) {
        if (parent instanceof TreeParent) {
            return ((TreeParent) parent).getChildren();
        }
        return new Object[0];
    }

    public boolean hasChildren(Object parent) {
        if (parent instanceof TreeParent)
            return ((TreeParent) parent).hasChildren();
        return false;
    }

    ///////////////////////////////////////////////////////////////////////////
    //IContentProvider
    ///////////////////////////////////////////////////////////////////////////

    public void inputChanged(Viewer v, Object oldInput, Object newInput) {
        
    }

    public void dispose() {
        
    }
}

