package net.zephyrizing.http_server;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import net.zephyrizing.http_server.content.ContentProvider;

public class HttpResponse {

    private static final Map<Integer, String> RESPONSE_DESCRIPTIONS;
    static {
        Map<Integer, String> descriptions = new HashMap<Integer, String>();

        descriptions.put(200, "OK");
        descriptions.put(404, "Not Found");

        RESPONSE_DESCRIPTIONS = Collections.unmodifiableMap(descriptions);
    }

    private int status;
    private Map<String, List<String>> headers;
    private ContentProvider provider;

    public HttpResponse() {
        this(200);
    }

    public HttpResponse(int status) {
        this.status = status;
        this.headers = new HashMap<String, List<String>>();
    }

    public int status() {
        return this.status;
    }

    public Map<String, List<String>> headers() {
        return this.headers;
    }

    public HttpResponse setStatus(int status) {
        this.status = status;
        return this;
    }

    public HttpResponse addHeader(String key, String... values) {
        if (values.length != 0) {
            List<String> headerValues = this.headers.getOrDefault(key, new ArrayList<String>());
            Collections.addAll(headerValues,
                               values);
            this.headers.putIfAbsent(key, headerValues);
        }
        return this;
    }

    public HttpResponse setContent(ContentProvider provider) {
        this.provider = provider;
        return this;
    }

    public String getStatusLine() {
        return String.format("HTTP/%s %d %s",
                             protocolVersion(),
                             this.status,
                             RESPONSE_DESCRIPTIONS.get(this.status));
    }

    public Stream<String> getStatusLineStream() {
        return Stream.of(getStatusLine());
    }

    public Stream<String> getHeaderStream() {
        return this.headers.entrySet().stream().map(HttpResponse::renderHeader);
    }

    public InputStream getData() {
        if (this.provider != null && this.provider.contentExists()) {
            return this.provider.getContent();
        }
        return new ByteArrayInputStream("".getBytes());
    }

    private static String protocolVersion() {
        return String.format("%s.%s",
                             HttpProtocol.MAJOR_VERSION,
                             HttpProtocol.MINOR_VERSION);
    }

    private static String renderHeader(Map.Entry<String, List<String>> entry) {
        String prefix = String.format("%s: ", entry.getKey());
        return entry.getValue().stream().collect(Collectors.joining(",", prefix, ""));
    }

    private static Stream.Builder<String> emptyStringStreamBuilder() {
        return Stream.builder();
    }
}

@@ -441,36 +441,411 @@
 er;%0A
-%0Apublic class HttpResponse %7B
+import net.zephyrizing.http_server.content.StringContentProvider;%0A%0Apublic class HttpResponse %7B%0A%0A    private static final String ERROR_MESSAGE_FMT =%0A        %22%3C!DOCTYPE html%3E%5Cn%22+%0A        %22%3Chtml%3E%3Chead%3E%22+%0A        %22%3Ctitle%3E%25s - %25s%3C/title%3E%22+%0A        %22%3C/head%3E%3Cbody%3E%22+%0A        %22%3Ch1%3E%25s%3C/h1%3E%22+%0A        %22%3Carticle%3E%3Cp%3EAn error occurred while processing.%3C/p%3E%22+%0A        %22%3Cp%3E%25s%3C/p%3E%3C/article%3E%22+%0A        %22%3C/body%3E%3C/html%3E%22;;
 %0A%0A  
@@ -2085,32 +2085,537 @@
 rn this;%0A    %7D%0A%0A
+    public HttpResponse setContent(Throwable error) %7B%0A        return setContent(String.format(ERROR_MESSAGE_FMT,%0A                                        this.status,%0A                                        RESPONSE_DESCRIPTIONS.get(this.status),%0A                                        error.toString(),%0A                                        error.getLocalizedMessage()));%0A    %7D%0A%0A    public HttpResponse setContent(String content) %7B%0A        return setContent(new StringContentProvider(content));%0A    %7D%0A%0A
     public HttpR

Create convenience setContent methods in Response