package com.wizzardo.http;

import com.wizzardo.http.request.Request;
import com.wizzardo.http.response.RangeResponseHelper;
import com.wizzardo.http.response.Response;
import com.wizzardo.http.response.Status;
import com.wizzardo.tools.misc.DateIso8601;
import com.wizzardo.tools.misc.WrappedException;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.Date;

/**
 * @author: wizzardo
 * Date: 19.09.14
 */
public class FileTreeHandler implements Handler {
    private String prefix;
    private File workDir;

    public FileTreeHandler(File workDir, String prefix) {
        if (prefix.equals("/"))
            prefix = "";
        this.workDir = workDir;
        this.prefix = prefix;
    }

    public FileTreeHandler(String workDir, String prefix) {
        this(new File(workDir), prefix);
    }

    @Override
    public Response handle(Request request, Response response) {
        String path = request.path();

        if (!path.startsWith(prefix))
            return response.setStatus(Status._400).setBody("path must starts with prefix '" + prefix + "'");

        path = path.substring(prefix.length(), path.length());

        File file = new File(workDir, decodePath(path));
        if (file.getAbsolutePath().length() < workDir.getAbsolutePath().length())
            return response.setStatus(Status._403).setBody(path + " is forbidden");

        if (!file.exists())
            return response.setStatus(Status._404).setBody(path + " not found");

        if (!file.canRead())
            return response.setStatus(Status._403).setBody(path + " is forbidden");

        if (file.isDirectory())
            return response.setBody(renderDirectory(file));

        return RangeResponseHelper.makeRangeResponse(request, response, file);
    }

    private String renderDirectory(File dir) {
        StringBuilder sb = new StringBuilder();
        sb.append("<HTML><HEAD><TITLE>Directory: ");
        String path = prefix + dir.getAbsolutePath().substring(workDir.getAbsolutePath().length());
        sb.append(path);
        sb.append("</TITLE></HEAD><BODY>");

        sb.append("<H1>Directory: ").append(path).append("</H1>\n");
        sb.append("<TABLE BORDER=0>\n");

        File[] files = dir.listFiles();

        Arrays.sort(files, (o1, o2) -> {
            if (o1.isDirectory() && o2.isDirectory())
                return o1.getName().compareTo(o2.getName());

            if (o1.isDirectory())
                return -1;

            if (o2.isDirectory())
                return 1;

            return o1.getName().compareTo(o2.getName());
        });

        if (!path.endsWith("/"))
            path += '/';

        for (File file : files) {
            sb.append("<tr>");

            sb.append("<td>");
            sb.append("<a href=\"").append(path).append(encodeName(file.getName()));
            if (file.isDirectory())
                sb.append('/');
            sb.append("\">");
            sb.append(file.getName());
            sb.append("</a>");
            sb.append("</td>");

            sb.append("<td align=right>");
            sb.append(file.length()).append(" bytes");
            sb.append("</td>");

            sb.append("<td>");
            sb.append(DateIso8601.format(new Date(file.lastModified())));
            sb.append("</td>");

            sb.append("<tr>\n");
        }

        sb.append("</TABLE>\n</BODY></HTML>");
        return sb.toString();
    }

    private String encodeName(String name) {
        try {
            return URLEncoder.encode(name, "utf-8").replace("+", "%20");
        } catch (UnsupportedEncodingException e) {
            throw new WrappedException(e);
        }
    }

    private String decodePath(String path) {
        try {
            return URLDecoder.decode(path, "utf-8");
        } catch (UnsupportedEncodingException e) {
            throw new WrappedException(e);
        }
    }
}

@@ -1065,19 +1065,22 @@
 h(prefix
+, 1
 ))%0A
-
         
@@ -1226,16 +1226,20 @@
 length()
+ + 1
 , path.l
@@ -1868,32 +1868,874 @@
 , file);%0A    %7D%0A%0A
+    protected StringBuilder renderFolderPath(File dir, StringBuilder sb) %7B%0A        if (dir.equals(workDir)) %7B%0A            StringBuilder s = new StringBuilder(%22/%22);%0A            renderLink(s, %22root%22, sb).append(%22/%22);%0A            s.append(prefix).append(%22/%22);%0A            renderLink(s, prefix, sb).append(%22/%22);%0A            return s;%0A        %7D%0A%0A        if (dir.getPath().endsWith(%22/%22))%0A            return renderFolderPath(dir.getParentFile(), sb);%0A%0A        StringBuilder path = renderFolderPath(dir.getParentFile(), sb).append(dir.getName()).append(%22/%22);%0A        renderLink(path, dir.getName(), sb).append(%22/%22);%0A        return path;%0A    %7D%0A%0A    protected StringBuilder renderLink(StringBuilder href, String text, StringBuilder sb) %7B%0A        sb.append(%22%3Ca href=%5C%22%22).append(href).append(%22%5C%22%3E%22).append(text).append(%22%3C/a%3E%22);%0A        return sb;%0A    %7D%0A%0A
     private Stri
@@ -2887,16 +2887,22 @@
 g path =
+ %22/%22 +
  prefix 
@@ -3054,49 +3054,60 @@
     
-sb.append(%22%3CH1%3EDirectory: %22).append(path)
+renderFolderPath(dir, sb.append(%22%3CH1%3E%22));%0A        sb
 .app

add path breadcrumbs