package page.config;

import java.io.IOException;
import java.util.Map;
import java.util.HashMap;

import org.wikiwebserver.core.WareHouse;
import org.wikiwebserver.distribute.interfaces.WorkerNode;
import org.wikiwebserver.distribute.server.WorkerNodeManager;
import org.wikiwebserver.handler.http.HTTPException;
import org.wikiwebserver.handler.http.HTTPHandler;
import org.wikiwebserver.handler.http.HTTPRequest;
import org.wikiwebserver.handler.http.interfaces.*;
import org.wikiwebserver.handler.http.responder.FileResponder;
import org.wikiwebserver.util.connector.WikiMapConnector;

import page.distribute.AudioPlaylist;
import page.distribute.Display;
import page.distribute.FileBrowser;
import page.distribute.FileDownload;
import page.distribute.ImageBrowser;
import page.distribute.ImageDownload;
import page.distribute.ImagePreview;
import page.tools.entity.NodeData;
import page.tools.stats.NodeList;

public class SiteObjectFinder implements ObjectFinder {
    
    public Object findObject(HTTPHandler conn) throws Exception {
        
        HTTPRequest request = conn.getRequest();
        //System.out.println(request);
        
        if (!request.getUrl().startsWith("/"))
            throw new HTTPException(300, "Bad request");
        

        // Process host header
        String reqHostHeader = request.getHeaders().getFirst("Host");
        if (reqHostHeader != null) {
            
            // Strip off port
            int portIdx = reqHostHeader.indexOf(':');
            if (portIdx != -1) {
                reqHostHeader = reqHostHeader.substring(0, portIdx);
            }
            
            // Go secure?
            if (reqHostHeader.startsWith("secure.") && 
                conn.getProtocol().equals("http")) {
                
                throw new HTTPException(301,
                        "Secure connection preferred", 
                        "https://" + reqHostHeader + 
                        request.getUri());  
            }
            
            // Exit secure!
            if (reqHostHeader.startsWith("www.wikiwebserver.org") && 
                conn.getProtocol().equals("https")) {
                
                throw new HTTPException(301,
                        "Secure connection not required", 
                        "http://www.wikiwebserver.org" + 
                        request.getUri());  
            }            
            
            // Go mobile?
            String userAgent = request.getHeaders().getFirst("User-Agent");
            if (request.getUrl().equals("/") && userAgent != null && 
            		WareHouse.isMobileUserAgent(userAgent) &&
            		!reqHostHeader.startsWith("m.")) {
                
            	throw new HTTPException(301,
                        "Optimised version for mobile browsers", 
                        conn.getProtocol() + "://m." + reqHostHeader + 
                        request.getUri());  
            }

            if (reqHostHeader.contains(".")            
                && !reqHostHeader.startsWith("192.168")
                && !reqHostHeader.startsWith("10.0")
                && !reqHostHeader.startsWith("169.254")
                && !reqHostHeader.startsWith("127.0")
                && !reqHostHeader.startsWith("[")) {
            
                
                // Prefer www.wikiwebserver.org over com and net
                if (reqHostHeader.endsWith(".wikiwebserver.com") ||
                    reqHostHeader.endsWith(".wikiwebserver.net")) {  
                    
                    throw new HTTPException(301,
                            "www.wikiwebserver.org preferred", 
                            "http://www.wikiwebserver.org" + 
                            request.getUri());    
                }      
                
                // Prefer www.mydisk.co.uk (prefixed www)
                if (reqHostHeader.equals("mydisk.co.uk")) {  
                    
                    throw new HTTPException(301,
                            "www.mydisk.co.uk preferred", 
                            "http://www.mydisk.co.uk" + 
                            request.getUri());    
                }                 
                
                
                // Only allow specific sub domains
                if (   !reqHostHeader.startsWith("www.")
                    && !reqHostHeader.startsWith("m.")
                    && !reqHostHeader.startsWith("secure.")
                    && !reqHostHeader.startsWith("audio.")
                    && !reqHostHeader.startsWith("home.")    
                    && !reqHostHeader.startsWith("beta.")) {
                    
                    throw new HTTPException(301,
                            "www.wikiwebserver.org preferred", 
                            "http://www.wikiwebserver.org" + 
                            request.getUri());                        
                }
                
                // If there is a referencing index to the domain, jump to it

                
                // Remove special sub domains
                if (reqHostHeader.startsWith("www."))         reqHostHeader = reqHostHeader.substring(4);              
                else if (reqHostHeader.startsWith("audio."))  reqHostHeader = reqHostHeader.substring(6);
                else if (reqHostHeader.startsWith("secure.")) reqHostHeader = reqHostHeader.substring(7);               
                else if (reqHostHeader.startsWith("m."))      reqHostHeader = reqHostHeader.substring(2);
                
                if (reqHostHeader.startsWith("beta.")) reqHostHeader = reqHostHeader.substring(5);               
                    
                
                // Additional domain support
                if (!reqHostHeader.endsWith("wikiwebserver.org")) {  
                    
                    String[] domParts = reqHostHeader.split("\\.");    
                    
                    // Construct backwards domain reference
                    StringBuilder jumper = new StringBuilder();
                    for (int i=domParts.length-1; i>=0; i--) {
                        jumper.append(domParts[i] + "/");
                    }
                    
                    // Jump to index for domain
                    try {
                        if (request.getUrl().equals("/")) {
                            String jumpStart = "/page/" + jumper.toString() + "Index.class"; 
                            HTTPResponder responder = getHTTPResponderFromUri(jumpStart);
                            if (responder != null) return responder;
                        }
                    } catch (IOException ex) {
                        // Unknown host name
                        ex.printStackTrace();
                    }      
                }
            }
        }
                          

        String url = request.getUrl();       
        
        // Is this an indirect mapping via path re-mapping
        String reMappedPath = urlrewrite.get(url);
        if (reMappedPath != null) url = reMappedPath;
        else {
            // A final slash might have been missed
            reMappedPath = urlrewrite.get(url + "/");
            if (reMappedPath != null) {
                throw new HTTPException(301, "You missed the slash", url + "/");
            }
        }
        
        // Request to get WikiMap data
        if (url.startsWith("/WikiMap/")) return new WikiMapConnector();  
        
      
        // Request to link to node
        
    	if (url.equals("/nodes")) {
    		throw new HTTPException(301, "You missed the slash", "/nodes/");
        }  
    	
        String nodeRoot = "/nodes/";
        if (url.startsWith(nodeRoot)) {
        	if (url.length() == nodeRoot.length()) {
        		return new NodeList();
        	}
        	
        	int idx = url.indexOf("/", nodeRoot.length());
        	if (idx == -1) {
        		throw new HTTPException(301, "You missed the slash", url + "/");
            }
        	
        	WorkerNode node = null;
        	
        	String encNodeName = url.substring(nodeRoot.length(), idx);  
        	String nodeName = WareHouse.urlDecode(encNodeName);
        	NodeData nodeData = NodeData.getNodeDataByName(nodeName);  
            if (nodeData == null) {
                // This might be a node ID not a name
                node = WorkerNodeManager.getNode(nodeName); 
                if (node == null) {
                    throw new HTTPException(404, "Node not found");
                }
            }        	
            else {
                node = WorkerNodeManager.getNode(nodeData.getId()); 
            }
            
            if (node == null) {
            	throw new HTTPException(404, "Node id not found");
            }
            
            String path = url.substring(nodeRoot.length() + encNodeName.length());
            
            if (path.equals("/SCREENSHOT")) {
            	return new Display(node);
            }
            
            String mode = null;
            int offset = 0;
            int maximumImageDimension = -1;
            if (request.getFormData() != null) {
                mode = request.getFormData().getFirst("mode");                       
                String offsetString = request.getFormData().getFirst("offset");
                if (offsetString != null) {
                    offset = Integer.parseInt(offsetString);
                }                
                String maxDimString = request.getFormData().getFirst("max_dim");
                if (maxDimString != null) {
                    maximumImageDimension = Integer.parseInt(maxDimString);
                }
            }            
    	    	
        	if (path.endsWith("/")) {
        	    if ("playlist".equals(mode)) {
        	        return new AudioPlaylist(node, path);
        	    }
        	    else if ("image_index".equals(mode)) {
                    return new ImageBrowser(node, path, offset);
                }
                else if ("image".equals(mode)) {
                    return new ImagePreview(node, path, offset);
                }
                else return new FileBrowser(node, mode, path);
        	}
        	else {
        	    if (maximumImageDimension > 0) {
        	        return new ImageDownload(node, path, maximumImageDimension);
        	    }
        	    else return new FileDownload(node, path);
        	}
        }   
        
        // Return class files in this location rather than assume they are responders
        // for creating dynamic content.
        if (url.startsWith("/org/wikiwebserver/distribute/se/worker/")) {
        	return new FileResponder(url);
        }
        if (url.startsWith("/org/wikiwebserver/distribute/util/")) {
        	return new FileResponder(url);
        }        

        
        String userAgent = request.getHeaders().getFirst("User-Agent");
        
        // If this class request came from another WikiWebServer 
        // and the file exists, return the raw class file.                
        if (userAgent != null && userAgent.startsWith("WikiWebServer")) {
            return new FileResponder(url);
        }

        if (url.endsWith(".class")) {
            return getHTTPResponderFromUri(url);          
        }
                
        return new FileResponder(url);
    }
    
    private HTTPResponder getHTTPResponderFromUri(String url) throws IOException {
    	if (url.startsWith("/")) url = url.substring(1);
        // Construct class name from path
        String className = url.substring(0, url.length()-6); // remove .class
        className = className.replace('\\', '.'); // Java packages use dots not back slashes              
        className = className.replace('/', '.');  // Java packages use dots not slashes  
        return getHTTPResponder(className);
    }
    
    private HTTPResponder getHTTPResponder(String className) throws IOException {
        try {
            Class<?> cla = Class.forName(className);
            Class<?>[] faces = cla.getInterfaces();
            String httpResponderClass = HTTPResponder.class.getName();
            for (int i=0; i<faces.length; i++) {
                // If this class implements HTTPResponder, create a new instance
                // and return it ready for execution
                String faceName = faces[i].getName();
                if (faceName.equals(httpResponderClass)) {
                    return (HTTPResponder) cla.newInstance();
                }
            }
            
            throw new Exception("The requested class does not implement " + httpResponderClass);
            
        } catch (ClassNotFoundException ex) {
            HTTPException httpe = new HTTPException(404, "Class not found");
            httpe.initCause(ex);
            throw httpe;
        } catch (Exception ex) {
            HTTPException httpe = new HTTPException(500, "Class can not respond to HTTP requests");
            httpe.initCause(ex);
            throw httpe;
        }
    }
    
    
    private static Map<String, String> urlrewrite = null;
    static {
        urlrewrite = new HashMap<String, String>();
        
        addRewriteRule("/",                  page.Index.class);
        addRewriteRule("/mobilise/",         page.example.Mobiliser.class);
        addRewriteRule("/examples/",         page.example.Index.class);
        addRewriteRule("/user/",             page.tools.stats.UserList.class);
        addRewriteRule("/changes/",          page.tools.stats.ChangeView.class);
        addRewriteRule("/files/",            page.tools.management.FileTreeView.class);
        addRewriteRule("/contribute/",       page.misc.Contribute.class);
        addRewriteRule("/status/",           page.tools.stats.Status.class);
        addRewriteRule("/nav/",              page.tools.html.NavigationMenu.class);
        addRewriteRule("/edit",              page.tools.admin.FileEditor.class);
        addRewriteRule("/michaelgardiner/",  page.info.michaelgardiner.Index.class);
        
        addRewriteRule("/WikiWebServerCore.jar", page.tools.jar.CoreJarResponder.class);
        
        addRewriteRule("/JFileRecovery.jnlp",    page.com.jfilerecovery.JNLP.class);
        addRewriteRule("/JFileRecovery.pad.xml", page.com.jfilerecovery.PAD.class);
        
        addRewriteRule("/JRSSTray.jnlp",         page.com.jrsstray.JNLP.class); 
        addRewriteRule("/JRSSTray.pad.xml",      page.com.jrsstray.PAD.class);
    }
    
    private static void addRewriteRule(String url, Class<? extends HTTPResponder> target) {
        addRewriteRule(url, WareHouse.getUrlPathForClass(target));
    }
    
    private static void addRewriteRule(String url, String target) {
        urlrewrite.put(url, target);
    }
}
