package org.academiadecodigo.enuminatti.webserver;


import org.academiadecodigo.enuminatti.webserver.exceptions.RedirectToDir;

import javax.activation.MimetypesFileTypeMap;
import java.io.*;
import java.net.URLConnection;
import java.net.URLDecoder;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * MIT License
 * (c) 2017 Ricardo Constantino
 */

public class FileServer {
    private File resourcePath;
    private static final String WEBROOT = "www";
    private MimetypesFileTypeMap mimeMap = null;
    private byte[] fileBytes;

    public FileServer() {
        try {
            mimeMap = new MimetypesFileTypeMap("META-INF/.mime.types");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void parseResourceURL(String resource) throws FileNotFoundException, RedirectToDir {

        File file;
        try {
            file = new File(WEBROOT, URLDecoder.decode(resource, "UTF-8"));
        } catch (UnsupportedEncodingException e) {
            file = new File(WEBROOT, resource);
        }

        if (file.exists() && file.isDirectory()) {
            if (!resource.endsWith("/")) {
                throw new RedirectToDir(resource + "/");
            }
            File indexFile = new File(file, "index.html");
            if (indexFile.isFile()) {
                file = indexFile;
            }
        } else if (!file.exists() && file.getName().equals("ls")) {
            file = file.getParentFile();
        } else if (!file.exists()) {
            throw new FileNotFoundException();
        }

        resourcePath = file;
    }


    public String getContentType() {
        String mimeType = detectMimeType(mimeMap, resourcePath);
        String charset = detectCharset(resourcePath);

        return String.format("Content-Type: %s%s",
                mimeType,
                (charset != null && mimeType.startsWith("text/")) ?
                        "; charset=" + charset : "");
    }

    private static String detectCharset(File resourcePath) {
        // assume utf-8, too hard to chardetect in java
        return "utf-8";
    }

    private static String detectMimeType(MimetypesFileTypeMap mimeMap, File resourcePath) {
        String mimeType = null;

        if (mimeMap != null) {
            mimeType = mimeMap.getContentType(resourcePath);
            WebServer.printDebug("* Guessed mimetype using MimetypesFileTypeMap: " + mimeType);
            return mimeType;
        }

        mimeType = URLConnection.guessContentTypeFromName(resourcePath.getName());
        WebServer.printDebug("* Guessed mimetype from extension: " + mimeType);
        mimeType = mimeType != null ? mimeType : "application/octet-stream";

        return mimeType;
    }

    public long getLength() {
        return resourcePath.length();
    }

    public void writeFile(BufferedOutputStream out) {
        byte[] buffer = new byte[512 * 1024];

        try (InputStream is = (resourcePath.isDirectory()) ?
                new ByteArrayInputStream(fileBytes) :
                new FileInputStream(resourcePath);
                DataInputStream reader = new DataInputStream(is)) {
            int num;
            while ((num = reader.read(buffer)) != -1) {
                out.write(buffer, 0, num);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void generateDirIndex() {
        StringBuilder dirIndexHTML = new StringBuilder("<!doctype html><html>");
        dirIndexHTML.append("<head><link rel=\"stylesheet\" type=\"text/css\" href=\"/index.css\"></head>");
        String relativePath = resourcePath.getAbsolutePath().split("/www/?", 2)[1];
        dirIndexHTML.append("<body><h3>Index of /").append(relativePath).append("</h3><ul>");
        if (relativePath.length() > 0) {
            dirIndexHTML.append("<li><a href=\"..\"><span class=\"filename\">⬆️ Parent Directory</span></a></li>");
        }
        for (File file : resourcePath.listFiles()) {
            if (file.getName().startsWith("."))
                continue;

            dirIndexHTML.append(String.format("<li><a href=\"%s\">", file.isFile() ? file.getName() : file.getName() + "/"));

            String filename = String.format("<span class=\"filename\">%s</span>", file.getName());
            String filesize = String.format("<span class=\"filesize\">%s</span>", file.isFile() ? humanizeSize(file.length()) : "");
            String filedate = String.format("<span class=\"filedate\">%s</span>",
                    new SimpleDateFormat("yyyy-MM-dd HH:mm").format(new Date(file.lastModified())));

            dirIndexHTML.append(filename).append(filesize).append(filedate);
            dirIndexHTML.append("</a></li>");
        }
        dirIndexHTML.append("</ul></body></html>");
        fileBytes = dirIndexHTML.toString().getBytes();
        WebServer.printDebug("* Generated directory index");
    }

    private String humanizeSize(long length) {

        if (length <= 1024) {
            return String.format("%4d %3s", length, "B").replaceAll(" ", "&nbsp;");
        }

        String[] suffixes = new String[] {"KiB", "MiB", "GiB", "TiB"};

        int i = 0;
        double newLength = length/1024.0;
        while (newLength > 1024 && i < suffixes.length-1) {
            newLength /= 1024;
            i++;
        }

        return String.format("%6.1f %s", newLength, suffixes[i]).replaceAll(" ", "&nbsp;");
    }

    public File getFile() {
        return resourcePath;
    }
}
