package de.duehl.basics.logic;

/*
 * Copyright 2016 Christian Dühl. All rights reserved.
 *
 * This program is free software. You can redistribute it and/or
 * modify it under the same terms as perl:
 *
 * general:  http://dev.perl.org/licenses/
 * GPL:      http://dev.perl.org/licenses/gpl1.html
 * artistic: http://dev.perl.org/licenses/artistic.html
 */

import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.regex.Matcher;

/**
 * Diese Klasse ersetzt BBC-Code in Strings in HTML.
 *
 * @version 1.01     2013-12-12
 * @author Christian Dühl
 */

public class BulletinBoardCode {

    /** Verzeichnis für normales HTML. */
    private final Map<String, String> bbMap;

    /** Verzeichnis zur Darstellung auf einer Swing JEditorPane. */
    private final Map<String, String> bbMapForJava;

    /** Konstruktor. */
    public BulletinBoardCode() {
        bbMap = buildBulletinBoardMap();
        bbMapForJava = buildBulletinBoardMapForJava();
    }

    /**
     * Erzeugt das Verzeichnis für normales HTML.
     *
     * @return Erzeugtes Verzeichnis.
     */
    private Map<String, String> buildBulletinBoardMap() {
        Map<String, String> map = new HashMap<>();

        //map.put("(\r\n|\r|\n|\n\r)", "<br/>");
        map.put("\\[b\\](.+?)\\[/b\\]", "<strong>$1</strong>");
        map.put("\\[i\\](.+?)\\[/i\\]",
                "<span style='font-style:italic;'>$1</span>");
        map.put("\\[u\\](.+?)\\[/u\\]",
                "<span style='text-decoration:underline;'>$1</span>");
        map.put("\\[s\\](.+?)\\[/s\\]",
                "<span style=\"text-decoration:line-through\">$1</span>");
        map.put("\\[c\\](.+?)\\[/c\\]",
                "<span style=\"font-family:monospace\">$1</span>");
        map.put("\\[h1\\](.+?)\\[/h1\\]", "<h1>$1</h1>");
        map.put("\\[h2\\](.+?)\\[/h2\\]", "<h2>$1</h2>");
        map.put("\\[h3\\](.+?)\\[/h3\\]", "<h3>$1</h3>");
        map.put("\\[h4\\](.+?)\\[/h4\\]", "<h4>$1</h4>");
        map.put("\\[h5\\](.+?)\\[/h5\\]", "<h5>$1</h5>");
        map.put("\\[h6\\](.+?)\\[/h6\\]", "<h6>$1</h6>");
        map.put("\\[quote\\](.+?)\\[/quote\\]", "<blockquote>$1</blockquote>");
        map.put("\\[p\\](.+?)\\[/p\\]", "<p>$1</p>");
        map.put("\\[p=(.+?),(.+?)\\](.+?)\\[/p\\]",
                "<p style='text-indent:$1px;line-height:$2%;'>$3</p>");
        map.put("\\[center\\](.+?)\\[/center\\]", "<div align='center'>$1</div>"); // hier
        map.put("\\[align=(.+?)\\](.+?)\\[/align\\]", "<div align='$1'>$2</div>"); // hier
        map.put("\\[color=(.+?)\\](.+?)\\[/color\\]",
                "<span style='color:$1;'>$2</span>");
        map.put("\\[size=(.+?)\\](.+?)\\[/size\\]",
                "<span style='font-size:$1;'>$2</span>");
        map.put("\\[img\\](.+?)\\[/img\\]", "<img src='$1' />");
        map.put("\\[img=(.+?),(.+?)\\](.+?)\\[/img\\]",
                "<img width='$1' height='$2' src='$3' />");
        map.put("\\[email\\](.+?)\\[/email\\]", "<a href='mailto:$1'>$1</a>");
        map.put("\\[email=(.+?)\\](.+?)\\[/email\\]",
                "<a href='mailto:$1'>$2</a>");
        map.put("\\[url\\](.+?)\\[/url\\]", "<a href='$1'>$1</a>");
        map.put("\\[url=(.+?)\\](.+?)\\[/url\\]", "<a href='$1'>$2</a>");
        map.put(
                "\\[youtube\\](.+?)\\[/youtube\\]",
                "<object width='640' height='380'><param name='movie' value='http://www.youtube.com/v/$1'></param><embed src='http://www.youtube.com/v/$1' type='application/x-shockwave-flash' width='640' height='380'></embed></object>");
        map.put("\\[video\\](.+?)\\[/video\\]", "<video src='$1' />");
        map.put("\\[pre\\](.+?)\\[/pre\\]", "<pre>$1</pre>");

        return map;
    }

    /**
     * Erzeugt das Verzeichnis zur Darstellung auf einer Swing JEditorPane.
     *
     * @return Erzeugtes Verzeichnis.
     */
    private Map<String, String> buildBulletinBoardMapForJava() {
        Map<String, String> map = new HashMap<>();

        //map.put("(\r\n|\r|\n|\n\r)", "<br/>");
        map.put("\\[b\\](.+?)\\[/b\\]", "<b>$1</b>");
        map.put("\\[i\\](.+?)\\[/i\\]", "<i>$1</i>");
        map.put("\\[u\\](.+?)\\[/u\\]", "<u>$1</u>");
        map.put("\\[s\\](.+?)\\[/s\\]", "<s>$1</s>");
        map.put("\\[c\\](.+?)\\[/c\\]",
                "<span style=\"font-family:monospace\">$1</span>");
        map.put("\\[h1\\](.+?)\\[/h1\\]", "<h1>$1</h1>");
        map.put("\\[h2\\](.+?)\\[/h2\\]", "<h2>$1</h2>");
        map.put("\\[h3\\](.+?)\\[/h3\\]", "<h3>$1</h3>");
        map.put("\\[h4\\](.+?)\\[/h4\\]", "<h4>$1</h4>");
        map.put("\\[h5\\](.+?)\\[/h5\\]", "<h5>$1</h5>");
        map.put("\\[h6\\](.+?)\\[/h6\\]", "<h6>$1</h6>");
        map.put("\\[quote\\](.+?)\\[/quote\\]", "<blockquote>$1</blockquote>");
        map.put("\\[p\\](.+?)\\[/p\\]", "<p>$1</p>");
        map.put("\\[p=(.+?),(.+?)\\](.+?)\\[/p\\]",
                "<p style='text-indent:$1px;line-height:$2%;'>$3</p>");
        map.put("\\[center\\](.+?)\\[/center\\]", "<div align='center'>$1</div>"); // hier
        map.put("\\[align=(.+?)\\](.+?)\\[/align\\]", "<div align='$1'>$2</div>"); // hier
        map.put("\\[color=(.+?)\\](.+?)\\[/color\\]",
                "<span style='color:$1;'>$2</span>");
        map.put("\\[size=(.+?)\\](.+?)\\[/size\\]",
                "<span style='font-size:$1;'>$2</span>");
        //map.put("\\[img\\](.+?)\\[/img\\]", "<img src='$1' />");
        //map.put("\\[img=(.+?),(.+?)\\](.+?)\\[/img\\]",
        //        "<img width='$1' height='$2' src='$3' />");
        map.put("\\[email\\](.+?)\\[/email\\]", "<a href='mailto:$1'>$1</a>");
        map.put("\\[email=(.+?)\\](.+?)\\[/email\\]",
                "<a href='mailto:$1'>$2</a>");
        map.put("\\[url\\](.+?)\\[/url\\]", "<a href='$1'>$1</a>");
        map.put("\\[url=(.+?)\\](.+?)\\[/url\\]", "<a href='$1'>$2</a>");
        //map.put(
        //        "\\[youtube\\](.+?)\\[/youtube\\]",
        //        "<object width='640' height='380'><param name='movie' value='http://www.youtube.com/v/$1'></param><embed src='http://www.youtube.com/v/$1' type='application/x-shockwave-flash' width='640' height='380'></embed></object>");
        //map.put("\\[video\\](.+?)\\[/video\\]", "<video src='$1' />");
        map.put("\\[\\*\\]([^\\[\\]\\<\\>]+)\\]", "<li>$1</li>");
        map.put("\\[pre\\](.+?)\\[/pre\\]", "<pre>$1</pre>");

        return map;
    }

    /**
     * Ersetzt Bulletin Board Code in dem gegebenen String in HTML und gibt das
     * Ergebnis zurück.
     *
     * @param bbCode
     *            Zu bearbeitender Text.
     * @return Umgesetztes Ergebnis.
     */
    public String toHtml(final String bbCode) {
        String html = replace(bbCode, bbMap);
        return html;
    }

    /**
     * Ersetzt Bulletin Board Code in dem gegebenen String in HTML in einer
     * Geschmacksrichtung zur Darstellung auf einer Swing JEditorPane und gibt
     * das Ergebnis zurück.
     *
     * @param bbCode
     *            Zu bearbeitender Text.
     * @return Umgesetztes Ergebnis.
     */
    public String toHtmlForJava(String bbCode) {
        String html = replace(bbCode, bbMapForJava);
        return html;
    }

    /** Pattern, um normale Listen &lt;ul&gt;...&lt;/ul&gt; zu finden. */
    static private Pattern findNormalListPattern = Pattern.compile(
            "\\[list\\](.*?)\\[/list\\]",
            Pattern.DOTALL | Pattern.MULTILINE | Pattern.CASE_INSENSITIVE);

    /** Pattern, um Aufzählungslisten &lt;ol&gt;...&lt;/ol&gt; zu finden. */
    static private Pattern findSpecialListPattern = Pattern.compile(
            "\\[list=(.)\\](.*?)\\[/list\\]",
            Pattern.DOTALL | Pattern.MULTILINE | Pattern.CASE_INSENSITIVE);

    /**
     * Ersetzt Bulletin Board Code in dem gegebenen String in HTML und gibt das
     * Ergebnis zurück.
     *
     * @param bbCode
     *            Zu bearbeitender Text.
     * @param map
     *            Verzeichnis für normales HTML oder für die Java-Variante.
     * @return Umgesetztes Ergebnis.
     */
    private String replace(String bbCode, Map<String, String> map) {
        String html = bbCode;
        for (String key : map.keySet()) {
            String value = map.get(key);
            html = html.replaceAll(key, value);
        }

        /* Für normale Listen (ul): */
        Matcher findNormalListMatcher = findNormalListPattern.matcher(html);
        while (findNormalListMatcher.find()) {
            String listString = findNormalListMatcher.group();
            String innerString = findNormalListMatcher.group(1);
            StringBuilder builder = new StringBuilder();
            innerString = innerString.replaceAll("\\[\\*\\]([^\\[\\]]+)",
                    "<li>$1</li>");
            innerString = innerString.replace("^<br/>", "");
            innerString = innerString.replace("<li> ", "<li>");
            innerString = innerString.replace("<br/>", "");
            innerString = innerString.replaceAll("\n(\\s*)</li>", "</li>\n$1");
            builder.append("<ul>");
            builder.append(innerString);
            builder.append("</ul>");
            html = html.replace(listString, builder.toString());
        }

        /* Für spezielle Listen (ol): */
        Matcher findSpecialListMatcher = findSpecialListPattern.matcher(html);
        while (findSpecialListMatcher.find()) {
            String listString = findSpecialListMatcher.group();
            String specialString = findSpecialListMatcher.group(1);
            String innerString = findSpecialListMatcher.group(2);
            StringBuilder builder = new StringBuilder();
            innerString = innerString.replaceAll("\\[\\*\\]([^\\[\\]]+)",
                    "<li>$1</li>");
            innerString = innerString.replace("<li> ", "<li>");
            innerString = innerString.replace("<br/>", "");
            innerString = innerString.replaceAll("\n(\\s*)</li>", "</li>\n$1");
            if ("i".equals(specialString)) {
                builder.append("<ol type=\"i\">");
            }
            else if ("a".equals(specialString)) {
                builder.append("<ol type=\"a\">");
            }
            else  {
                builder.append("<ol>");
            }
            builder.append(innerString);
            builder.append("</ol>");
            html = html.replace(listString, builder.toString());
        }

        return html;
    }

}
