/*
 * This code is free software; you can redistribute it and/or modify it under
 * the terms of the new BSD License.
 *
 * Copyright (c) 2018, Sebastian Staudt
 */

package com.github.koraktor.mavanagaiata.mojo;

import java.io.PrintStream;
import java.text.SimpleDateFormat;
import java.util.Optional;

import com.github.koraktor.mavanagaiata.git.GitCommit;
import com.github.koraktor.mavanagaiata.git.GitTag;

import static com.github.koraktor.mavanagaiata.mojo.AbstractGitOutputMojo.unescapeFormatNewlines;
import static org.apache.commons.text.StringEscapeUtils.escapeHtml4;

/**
 * Base class for formatting changelog output
 * <p>
 * Individual properties can be overridden in the configuration of the
 * {@code changelog} mojo.
 *
 * @author Sebastian Staudt
 * @see ChangelogMojo
 * @see ChangelogDefaultFormat
 * @see ChangelogMarkdownFormat
 */
public class ChangelogFormat {

    public enum Formats {
        DEFAULT(new ChangelogDefaultFormat()),
        MARKDOWN(new ChangelogMarkdownFormat());

        private ChangelogFormat format;

        Formats(ChangelogFormat format) {
            this.format = format;
        }

        ChangelogFormat getFormat() {
            return new ChangelogFormat().apply(format);
        }
    }

    String baseUrl;

    /**
     * The format for the branch line
     */
    String branch;

    /**
     * The format for the link to the history from the last tag to the current
     * branch on GitHub
     */
    String branchLink;

    /**
     * The format for the link to the branch history on GitHub
     */
    String branchOnlyLink;

    SimpleDateFormat dateFormatter;

    String dateFormat;

    /**
     * The string to prepend to every commit message
     */
    String commitPrefix;

    private Boolean createLinks = false;

    /**
     * Whether to escape HTML
     */
    Boolean escapeHtml = false;

    /**
     * The header to print above the changelog
     */
    String header;

    PrintStream printStream;

    /**
     * THe separator to print between different sections of the changelog
     */
    String separator;

    /**
     * The format for a tag line
     */
    String tag;

    /**
     * The format for the link to the tag history on GitHub
     */
    String tagLink;

    /**
     * Create a new format instance using this instance as base and override
     * with (non-{@code null}) properties of the given format
     *
     * @param format Format to apply settings from
     * @return A new format with applied settings
     */
    ChangelogFormat apply(ChangelogFormat format) {
        branch = Optional.ofNullable(format.branch).
            orElse(branch);
        branchLink = Optional.ofNullable(format.branchLink).
            orElse(branchLink);
        branchOnlyLink = Optional.ofNullable(format.branchOnlyLink).
            orElse(branchOnlyLink);
        commitPrefix = Optional.ofNullable(format.commitPrefix).
            orElse(commitPrefix);
        createLinks = Optional.ofNullable(format.createLinks).
            orElse(createLinks);
        escapeHtml = Optional.ofNullable(format.escapeHtml).
            orElse(escapeHtml);
        header = Optional.ofNullable(format.header).
            orElse(header);
        separator = Optional.ofNullable(format.separator).
            orElse(separator);
        tag = Optional.ofNullable(format.tag).
            orElse(tag);
        tagLink = Optional.ofNullable(format.tagLink).
            orElse(tagLink);

        return this;
    }

    /**
     * Enable creation of links using the given base URL
     *
     * @param baseUrl The base URL to link to
     */
    void enableCreateLinks(String baseUrl) {
        this.baseUrl = baseUrl;
    }

    /**
     * Prepare the format strings for use
     */
    void prepare() {
        branch = unescapeFormatNewlines(branch);
        branchLink = unescapeFormatNewlines(branchLink);
        branchOnlyLink = unescapeFormatNewlines(branchOnlyLink);
        commitPrefix = unescapeFormatNewlines(commitPrefix);
        header = unescapeFormatNewlines(header);
        separator = unescapeFormatNewlines(separator);
        tag = unescapeFormatNewlines(tag);
        tagLink = unescapeFormatNewlines(tagLink);

        dateFormatter = new SimpleDateFormat(dateFormat);
    }

    /**
     * Print a section header for a branch
     *
     * @param branchName The name of the branch
     */
    void printBranch(String branchName) {
        printStream.println(separator + String.format(branch, branchName) + separator);
    }

    /**
     * Print a single line for a commit
     *
     * @param currentCommit The commit to print
     */
    void printCommit(GitCommit currentCommit) {
        String commitMessage = escapeHtml ?
            escapeHtml4(currentCommit.getMessageSubject()) :
            currentCommit.getMessageSubject();

        printStream.println(commitPrefix + commitMessage);
    }

    /**
     * Generates a link to the GitHub compare / commits view and inserts it
     * into the changelog
     * <p>
     * If no current ref is provided, the generated text will link to the
     * commits view, listing all commits of the latest tag or the whole branch.
     * Otherwise the text will link to the compare view, listing all commits
     * that are in the current ref, but not in the last one.
     *
     * @param currentRef The current tag or branch in the changelog
     * @param lastRef The last tag or branch in the changelog
     * @param isBranch Whether the current ref is a branch
     */
    void printCompareLink(String currentRef, String lastRef, boolean isBranch) {
        if (baseUrl == null) {
            return;
        }

        String url = baseUrl;
        if (lastRef == null) {
            url += String.format("/commits/%s", currentRef);
        } else {
            url += String.format("/compare/%s...%s", currentRef, lastRef);
        }

        String linkText;
        if (isBranch) {
            if (lastRef == null) {
                linkText = String.format(branchOnlyLink, currentRef, url);
            } else {
                linkText = String.format(branchLink, lastRef, currentRef, url);
            }
        } else {
            String tagName = (lastRef == null) ? currentRef : lastRef;
            linkText = String.format(tagLink, tagName, url);
        }

        printStream.println(linkText + separator);
    }

    /**
     * Print a header for the changelog
     */
    void printHeader() {
        printStream.println(header);
    }

    /**
     * Print a separator between sections
     */
    void printSeparator() {
        printStream.print(separator);
    }

    /**
     * Print a section header for a tag
     *
     * @param currentTag The tag
     */
    void printTag(GitTag currentTag) {
        dateFormatter.setTimeZone(currentTag.getTimeZone());
        String date = dateFormatter.format(currentTag.getDate());

        printStream.println(String.format(tag, currentTag.getName(), date) + separator);
    }
}

@@ -1748,24 +1748,16 @@
 x;%0A%0A    
-private 
 Boolean 
@@ -1767,24 +1767,16 @@
 ateLinks
- = false
 ;%0A%0A    /
@@ -1842,16 +1842,8 @@
 Html
- = false
 ;%0A%0A 
@@ -3981,32 +3981,170 @@
 (commitPrefix);%0A
+        createLinks = Optional.ofNullable(createLinks).orElse(false);%0A        escapeHtml = Optional.ofNullable(escapeHtml).orElse(false);%0A
         header =

Fix defaults for changelog formats