/*
 * Copyright (c) 2017 XMLmind Software. All rights reserved. 
 *
 * Author: Hussein Shafie
 *
 * This file is part of the XMLmind W2X project.
 * For conditions of distribution and use, see the accompanying legal.txt file.
 */
package com.xmlmind.w2x_servlet;

import java.io.IOException;
import java.io.File;
import java.io.Closeable;
import java.io.InputStream;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import javax.servlet.http.HttpServletResponse;
import com.xmlmind.util.ThrowableUtil;
import com.xmlmind.util.StringUtil;
import com.xmlmind.util.FileUtil;
import com.xmlmind.util.URIComponent;
import com.xmlmind.util.XMLText;

/*package*/ final class RespUtil {
    private RespUtil() {}

    // -----------------------------------------------------------------------

    private static final String[] alertTemplate = new String[1];
    
    public static void sendAlert(String type, String message, Throwable e, 
                                 File baseDir, HttpServletResponse resp) 
        throws IOException {
        String alert;
        synchronized (alertTemplate) {
            if (alertTemplate[0] == null) {
                File alertTemplateFile =
                    new File(baseDir, "forms" + File.separator + "alert.html");
                try {
                    alertTemplate[0] = 
                        FileUtil.loadString(alertTemplateFile, "UTF-8");
                } catch (IOException shouldNotHappen) {
                    alertTemplate[0] = 
                        "<!DOCTYPE html>" +
                        "<html xmlns=\"http://www.w3.org/1999/xhtml\">" +
                        "<head><meta charset=\"UTF-8\"/>" +
                        "<title>%{TITLE}</title></head>" +
                        "<body><h1>%{TITLE}</h1><p>%{MESSAGE}</p></body>" +
                        "</html>";
                }
            }

            alert = alertTemplate[0];
        }

        // ---

        String title = Msg.msg(type + ".title");
        String details = (e == null)? "" : ThrowableUtil.detailedReason(e);
        String[] vars = new String[] {
            "%{TITLE}", title,
            "%{ICON}", type,
            "%{MESSAGE}", message,
            "%{DETAILS}", details
        };

        for (int i = 0; i < vars.length; i += 2) {
            alert = StringUtil.replaceAll(alert, vars[i],
                                          XMLText.escapeXML(vars[i+1]));
        }

        if (e == null) {
            int start = alert.indexOf("<textarea");
            if (start >= 0) {
                int end = alert.indexOf("</textarea>", start+9);
                if (end >= 0) {
                    end += 11;

                    StringBuilder buffer = new StringBuilder(alert);
                    buffer.replace(start, end, "&#xA0;");
                    alert = buffer.toString();
                }
            }
        }

        // ----

        resp.setStatus(HttpServletResponse.SC_OK);

        resp.setContentType("text/html; charset=UTF-8");
        resp.setIntHeader("Expires", -1);

        PrintWriter out = resp.getWriter();
        try {
            out.write(alert);
            out.flush();
        } finally {
            out.close();
        }
    }

    // -----------------------------------------------------------------------

    private static final String[] MIME_TYPES = {
        "zip", "application/zip",
        "epub", "application/epub+zip"
    };

    public static void sendFile(File file, String contentType,
                                HttpServletResponse resp) 
        throws IOException {
        FileInputStream in = null;
        try {
            in = new FileInputStream(file);
        } catch (IOException e) {
            resp.sendError(HttpServletResponse.SC_NOT_FOUND,
                           Msg.msg("cannotReadFile", file, 
                                   ThrowableUtil.reason(e)));
            return;
        }

        resp.setStatus(HttpServletResponse.SC_OK);

        if (contentType == null) {
            String ext = FileUtil.getExtension(file);
            if (ext != null) {
                ext = ext.toLowerCase();

                for (int i = 0; i < MIME_TYPES.length; i += 2) {
                    if (MIME_TYPES[i].equals(ext)) {
                        contentType = MIME_TYPES[i+1];
                        break;
                    }
                }
            }
        }

        if (contentType == null) {
            contentType = "application/octet-stream";
        }
        resp.setContentType(contentType);

        StringBuilder buffer = new StringBuilder("attachment");

        String baseName = file.getName();
        if (FileNameUtil.isASCII(baseName)) {
            buffer.append("; filename=\"");
            buffer.append(FileNameUtil.escapeQuotes(baseName));
            buffer.append('"');
        } else {
            // RFC 6266 -- https://tools.ietf.org/html/rfc6266
            //
            // Example:
            // Content-Disposition: attachment; 
            // filename="EU rates.svg"; filename*=utf-8''%e2%82%ac%20rates.svg
            //
            // According to these tests:
            // http://greenbytes.de/tech/tc2231/
            // filename* seems well supported by modern browsers.

            buffer.append("; filename=\"");
            buffer.append(
                FileNameUtil.escapeQuotes(FileNameUtil.toASCII(baseName)));
            buffer.append('"');

            buffer.append("; filename*=UTF-8''");
            buffer.append(FileNameUtil.encode(baseName, "UTF-8"));
        }

        resp.setHeader("Content-Disposition", buffer.toString());

        resp.setIntHeader("Expires", -1);
        resp.setContentLength((int) file.length());
        resp.setBufferSize(0);

        OutputStream out = resp.getOutputStream();
        copyBytes(in, out);
    }

    private static void copyBytes(InputStream in, OutputStream out)
        throws IOException {
        byte[] buffer = new byte[65536];
        int count;
        try {
            try {
                while ((count = in.read(buffer)) != -1) {
                    out.write(buffer, 0, count);
                }

                out.flush();
            } finally {
                closeStream(out);
            }
        } finally {
            closeStream(in);
        }
    }

    private static void closeStream(Closeable stream) {
        try {
            stream.close();
        } catch (IOException ignored) {}
    }
}
