/**
 * Created on Jul 16, 2005
 * $id:  $
 */
package org.jvnet.lookout.google.desktop.adapter;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.jvnet.lookout.core.adapter.CannotSearchException;
import org.jvnet.lookout.core.adapter.SearchExecutor;
import org.jvnet.lookout.core.model.SearchQuery.Parameter;
import org.jvnet.lookout.core.util.Utilities;
import org.jvnet.lookout.google.desktop.Plugin;
import org.xml.sax.SAXException;

public class DesktopSearchExecutor implements SearchExecutor {

    //See this URL for a description of the format:
    //http://desktop.google.com/developerguide.html#results
    
    protected SAXParser saxParser;
    protected SearchResultHandler handler;
    
    public DesktopSearchExecutor() {
        super();
    }

    public List executeSearch(String query, Map<Parameter, String> parameters) throws CannotSearchException {
        List results = new ArrayList();
        
        try {
            
            URL url = new URL(query);
            
            results = readSearchResults(url.openStream());

        } catch (MalformedURLException e) {
        	
            //If this happens it means that either:
        	//1- There is a problem with the plugin logic itself 
        	//   so the URL is not properly created 
        	//2- The desktop server is not running
        	
        	MultiStatus status = Utilities.createPluginStatus(
            		Plugin.getDefault(),
            		//CXB_EXTERNALIZE
            		"Could not execute Google Desktop search",
            		"The following URL is either malformed or cannot be reached: '" + query + "'",
            		IStatus.ERROR,
            		e
            );  
        	
        	//Log it...
            Plugin.getDefault().getLog().log(status);  
            
            throw new CannotSearchException(
            		//CXB_EXTERNALIZE
            		"Could not execute Google Desktop search.\n" +
            		"Make sure that the Google Desktop Search software is installed and started.\n" + 
            		"See error log for more details", 
            		status);
           
        } catch (IOException e) {
        	
        	//If this happens it means that:
        	//1- For some reason we could not read from the opened stream 
        	
        	MultiStatus status = Utilities.createPluginStatus(
            		Plugin.getDefault(),
            		//CXB_EXTERNALIZE
            		"Could not read Google Desktop search results",
            		"Could not read the stream contents of the results for this query: '" + query + "'",
            		IStatus.ERROR,
            		e
            );  
        	
        	//Log it...
            Plugin.getDefault().getLog().log(status);  
            
            throw new CannotSearchException(
            		//CXB_EXTERNALIZE
            		"Could not read Google Desktop search results.\n" +
            		"Make sure that Google Desktop Search software is installed and started.\n" + 
            		"See error log for more details", 
            		status);            
         }
        return results;
    }
    
    protected List readSearchResults(InputStream stream) throws CannotSearchException {
        
    	initXMLMembers();
		
		handler.setup();
		
    	try {
    		saxParser.parse(stream, handler);
    	} catch (Exception e) {
    		MultiStatus status = Utilities.createPluginStatus(
    				Plugin.getDefault(),
    				//CXB_EXTERNALIZE
    				"Could not read Google Desktop search results",
    				"Could not parse XML results with an " + e.getClass() +
    				"\nIt is likely that the XML provided by the Google Desktop Search software is not valid.",
    				IStatus.ERROR,
    				e
    		);  
    		
    		//Log it...
    		Plugin.getDefault().getLog().log(status);  
    		
    		throw new CannotSearchException(
    				//CXB_EXTERNALIZE
    				"Could not read Google Desktop search results.\n" +
    				"See error log for more details", 
    				status);
    	}
    	
    	List results = (handler.getSuccessfullyProcessed()) ? 
        		handler.getSearchResults() : 
        			Collections.emptyList();
        
        handler.cleanup();
        
        return results;        
    }
    
    /**
     * <category> => type
     * <title> => name
     * <url> => url (needs to be processed - if email we need to get the cached URL)
     * <snippet> => snippet (needs tp be processed - remove <>)
     * <icon> => could be reused
     */
    protected void initXMLMembers() throws CannotSearchException {
        
        if (null == saxParser) {
            SAXParserFactory factory = SAXParserFactory.newInstance();
            
            try {
                saxParser = factory.newSAXParser();             
            } catch (ParserConfigurationException e) {
            	
            	MultiStatus status = Utilities.createPluginStatus(
                		Plugin.getDefault(),
                		//CXB_EXTERNALIZE
                		"Could not read Google Desktop search results",
                		"Could not configure underlying XML parser to read the results with an " + ParserConfigurationException.class,
                		IStatus.ERROR,
                		e
                );  
            	
            	//Log it...
                Plugin.getDefault().getLog().log(status);  
                
                throw new CannotSearchException(
                		//CXB_EXTERNALIZE
                		"Could not read Google Desktop search results.\n" +
                		"Make sure you have a JAXP 1.3 implementation (JDK 5.0).\n" + 
                		"See error log for more details", 
                		status);                   
            } catch (SAXException e) {
            	
            	MultiStatus status = Utilities.createPluginStatus(
                		Plugin.getDefault(),
                		//CXB_EXTERNALIZE
                		"Could not read Google Desktop search results",
                		"Could not create XML parser to read the results with an " + SAXException.class,
                		IStatus.ERROR,
                		e
                );  
            	
            	//Log it...
                Plugin.getDefault().getLog().log(status);  
                
                throw new CannotSearchException(
                		//CXB_EXTERNALIZE
                		"Could not read Google Desktop search results.\n" +
                		"Make sure you have a JAXP 1.3 implementation (JDK 5.0).\n" + 
                		"See error log for more details", 
                		status);              
                }
        }
        
        if(null == handler) {
            handler = new SearchResultHandler();            
        }
    }
}
