package page.tools.admin;

import java.io.*;

import org.wikiwebserver.core.Privilege;
import org.wikiwebserver.core.WareHouse;
import org.wikiwebserver.core.SecurityMan;
import org.wikiwebserver.handler.http.FormData;
import org.wikiwebserver.handler.http.HTTPException;
import org.wikiwebserver.handler.http.HTTPRequest;
import org.wikiwebserver.handler.http.interfaces.HTTPResponder;
import page.config.SiteTemplatedPage;

import page.tools.entity.User;
import static org.wikiwebserver.html.HTMLHelper.*;

public class FileEditor extends SiteTemplatedPage implements HTTPResponder {
    
    private final String RESOURCE_ROOT = "/templates/default/editor/";
    private final String CODEPRESS_ROOT = "/templates/default/editor/codepress/";
    
    private boolean codePressEnable = true;    

    public void generate() throws HTTPException {
        
        String userAgent = getRequest().getHeaders().getFirst("User-Agent");
        this.codePressEnable = !WareHouse.isMobileUserAgent(userAgent);
		
        // Important variables used for page generator
        String path = "";
        String localPath = null;
        File localFile = null;
        
        StringBuffer body = new StringBuffer(); 
        boolean isJava = false;        
        boolean isExecutable = false;  
        String executeLink = null;
        int error = Integer.MIN_VALUE;
        String errorMessage = null;
        ByteArrayOutputStream compileErrors = null;
		
        // Process form data
        FormData formData = getFormData();
        if (formData != null) {
            path = formData.getFirst("path");
            if (path != null && path.length() > 1) {
                // path string is absolute
                path = path.startsWith("/") ? path : "/" + path;   
                
                // file is relative
                localPath = path.substring(1);
                localFile = new File(localPath);

                isJava = path.endsWith(".java");
            }
            else error = -1;
            
            String method = getRequest().getMethod().toUpperCase();
            String action = formData.getFirst("action");
            if (method.equals("POST") && action != null) {
                try {
                    String text = formData.getFirst("fileEditorArea");
                    
                    saveText(text, localPath, getRequest());
                    compileErrors = new ByteArrayOutputStream();
                    error = isJava ? error = WareHouse.compile(localPath, compileErrors) : 0;
                    if (error == 0) {                       
                        
                        // The user will probably want to execute the new code
                        // next, close this connection to avoid reusing it
                        // because we need a fresh classloader for reloading
                        // the class changes.
                        getResponse().getHeaders().set("Connection", "close");
                    }
                } catch (Exception ex) {
                    error = -1;
                    errorMessage = ex.getMessage();
                }                    
            }       
        };        
        
        if (isJava) {
            String basic = path.substring(1, path.length()-5);
            File classFile = new File(basic + ".class");
            if (classFile.exists()) {
                isExecutable = true;
                executeLink = "/" + basic + ".class";        
            }
        }
       
        if (localPath == null || localPath.length() == 0) {
            throw new HTTPException(404, "Path not specified");
        }
        
        // Load text
        String text = null;
        try {
            text = WareHouse.getResourceAsString(localPath);
        } catch (IOException ex) {
            throw new HTTPException(500, "Failed to read file", ex);
        }
        String contentType = WareHouse.getContentType(localPath);

        if (text == null) text = new String();
        
        String info = null;
        text = protectTextArea(text);

        body.append("<h1 class='pathLabel'>Editing " + path + "</h1>");   
        body.append(getGoogleAdsenseBlock("pub-7253309958196609", "4813634528", 728, 90));
        if (localFile != null && localFile.exists()) {
            body.append("<p>Last saved: " + WareHouse.formatStandardDate(localFile.lastModified()) + "</p>");
            String selPath = WareHouse.getUrlPathForFile(localFile);
            body.append("<p><a href='/files/?selected=" + selPath + "'>Show files in this location</a></p>");
        }
        body.append("<div id='editorFrame'>");
        String action = getUrl();  
        body.append("<form id='f' name='f' method='post' action='" + action + "' charset='UTF-8'>");
                
        
        // Generate information
        if (error == -1 && path.length() == 0) {
            info = "<span class='error'>Path not specified.</span>";
        } else if (error == -1) {
            info = "<span class='error'>" + path + " not saved." + " (" + errorMessage + ")</span>";            
        } else if (isJava && error != 0 && error != Integer.MIN_VALUE) {
            info = "<span class='error'>Compiler response available.</span>";
        } else if (isJava && (error == 0)) {
            String basic = path.substring(1, path.length()-5);
            String className = basic.replace('/', '.');
            info = className + " compiled.";
            if (isExecutable) info += " <a href='" + executeLink + "'>[Execute]</a>";         
        } else if (error == 0) {
            info = path + " saved. <a href='" + path + "'>[Download file]</a>";          
        }  
           
        if (info != null) {
            body.append(" " + info + "<br/>");
        }
        if (compileErrors != null && compileErrors.size() > 0) {
            body.append("<div id='compilerOutput'>");
            body.append("<textarea id='compilerOutputArea' name='javasource' rows='7' cols='80' wrap='off' style='width: 100%'>");
            body.append(protectTextArea(compileErrors.toString()));
            body.append("</textarea><br/>");           
            body.append("</div>"); 
        }
       
    
        body.append("<input type='hidden' name='path' value='" + path + "'/>");   

        
        body.append("<div style='clear:left;'></div>"); 
        body.append("<div id='fileEditor'>");
        
        if (codePressEnable) {
            String config = "generic";
            if (contentType == null) config = "generic";
            else if (contentType.equals("text/java")) config = "java";
            else if (contentType.equals("text/javascript")) config = "javascript";
            else if (contentType.equals("text/html")) config = "html";
            else if (contentType.equals("text/css")) config = "css";
            
            config += " linenumbers-on readonly-off autocomplete-off";
            //int rows = Math.max(countLines(text)+50, 100);
            int rows = 30;
            body.append("<textarea name='fileEditorArea' rows='" + rows + "' style='width: 100%'" +
                        " id='myCpWindow' class='codepress " + config + "'>");            
            
        } else {
            int rows = Math.max(countLines(text)+10, 50);
            body.append("<textarea id='fileEditorAreaNumbers' rows='" + (rows+1) + "' cols='3'>");
            body.append(getLineNumbers(rows));
            body.append("</textarea>");
            
            body.append("<textarea onkeydown='processTextAreaKey()' onscroll='hideLineNumbers()' " +
                    "id='fileEditorArea' name='fileEditorArea' rows='" + (rows+1) + "' style='width: 100%' wrap='off'>");            
        }
        
        body.append(text);
        body.append("</textarea><br/>");
        String buttonText = isJava ? "Save and Compile" : "Save";
       
        
        User u = getUser();
        boolean canSave = u != null && u.getPrivilege().isAbove(Privilege.GUEST);
            
        if (canSave) {
            if (codePressEnable) {
                body.append("<input class='submit' type='submit' name='action' value='" + buttonText + 
                            "' onclick='if (myCpWindow.textarea.disabled) myCpWindow.toggleEditor();'/>");
    
            } else {
                body.append("<input class='submit' type='submit' name='action' " +
                		    "value='" + buttonText + "'/>");
            }
        } 
        
        if (isExecutable) {
        	body.append("<input class='submit' type='button' name='action' value='Execute'" +
        			    " onclick='window.location=\"" + getServiceAddress() + executeLink + "\"'/>");
        }        
        if (!canSave) {
            body.append("<p>Only registered users can modify WikiWebServer.</p>");
        }

        body.append("</div>");
        
        if (codePressEnable) {
            body.append("<div id='codePressCredit'>Syntax highlighting" +
                        " <a href='javascript:myCpWindow.toggleEditor();'>[on/off].</a>" +
                        " Powered by <a href='http://codepress.org'>CodePress</a>.</div>" +          
                        "</form></div>"); // Close editor frame
        }
                
        String title = path == null ? "File Editor" : "Editing " + path;
        
        setTitle(title);
        addResourceRoot(RESOURCE_ROOT);
        addCSSLink("editor.css");
        addJavascriptLink("editor.js");
        
        if (codePressEnable) {
            addJavascriptLink(CODEPRESS_ROOT + "codepress.js");
        }        
        append(body.toString());
    }  
    
    private String protectTextArea(String text) {
        text = text.replace("&", "&amp;");         
        text = text.replace(">", "&gt;");
        return text.replace("<", "&lt;");           
    }
    
    private String getLineNumbers(int num) {
        StringBuilder nos = new StringBuilder();
        for (int i=1; i<=num; i++) {
            nos.append(String.valueOf(i));
            nos.append("\r\n");
        }
        return nos.toString();
    }
    
    private int countLines(String text) {
        int count = 0;
        for (int i=0; i<text.length(); i++) {
            if (text.charAt(i) == '\n') {
                count++;
            }
        }
        return count+1;
    }
   
    
    private void saveText(String utf8, String path, HTTPRequest request) throws IOException {

        User u = User.getUser(request);
        
        try {
            // Establish privilege of current user. Writing must only be performed
            // if the user has sufficient privileges (not the executing class)
            SecurityMan bigNick = (SecurityMan) System.getSecurityManager();
            bigNick.checkWrite(path, u.getPrivilege());
            
        } catch (SecurityException ex) {
            SecurityException se = new SecurityException("You can not save to " + path + " because you do not have sufficient privileges."); 
            se.initCause(ex);
            throw se;
        }               
            
        // Standard users can only write the their personal locations
        if (u.getPrivilege() == Privilege.USER || u.getPrivilege() == Privilege.PREMIUM_USER) {    
            String allowed = WareHouse.USER_ROOT + "u" + u.getId();
            if (!path.startsWith(allowed)) {
                throw new SecurityException("You can not save to " + path + " because it does not belong to you."); 
            }
        }     
        
        // Write the text to the filing system
        File localFile = new File(path);
        localFile.getParentFile().mkdirs();
        Writer out = null;
        try {
            out = new OutputStreamWriter(new FileOutputStream(localFile), "UTF8");
            out.write(utf8);            
        } finally {
            out.close(); 
        }
  
    }    

}
