/*******************************************************************************
 * Copyright (c) 2006, 2018 Red Hat Inc. and others.
 *
 * This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *    Kyu Lee <klee@redhat.com> - initial API and implementation
 *******************************************************************************/
package org.eclipse.linuxtools.internal.changelog.core.editors;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.StringTokenizer;

import org.eclipse.jface.text.formatter.IFormattingStrategy;

public class ChangeLogFormattingStrategy implements IFormattingStrategy {

    private static final String NEW_LINE_CHAR = "\n";
    private static final String WHITE_SPACE_CHAR = " ";
    private static final String TAB_SPACE_CHAR = "\t";
    private static final String DELIMITER_CHARS = NEW_LINE_CHAR + WHITE_SPACE_CHAR + TAB_SPACE_CHAR;

    private static final int MAX_WIDTH = 80;
    private static final SimpleDateFormat isoDate = new SimpleDateFormat("yyyy-MM-dd");

    @Override
    public String format(String content, boolean isLineStart,
            String indentation, int[] positions) {

        String firstLine = "";

        // if first line is not from the start, ignore it
        if (!isLineStart) {
            int eol;
            if ((eol = content.indexOf(NEW_LINE_CHAR)) == content.length() - 1) {
                return content;
            } else {
                firstLine = content.substring(0, eol + 1);
                content = content.substring(eol + 1);
            }
        }

        content.replaceFirst("(\\s+)?\\n(\\s+)?", NEW_LINE_CHAR);
        StringTokenizer candidateWords = new StringTokenizer(content, DELIMITER_CHARS, true);

        String formattedContent = formatContent(candidateWords, indentation.length());
        return firstLine.concat(formattedContent);
    }

    private String formatContent(StringTokenizer candidateWords, int indentationLength) {
        StringBuilder formattedWords = new StringBuilder();
        int currentLineLength = indentationLength;

        while (candidateWords.hasMoreTokens()) {
            String cword = candidateWords.nextToken();

            if (isDelimeter(cword)) {
                continue;
            }

            // if the word is date, start new line and include
            // names, email, then an empty line.
            else if (isDate(cword)) {

                // see if we are in middle of line and
                // if so, start new line, else continue.
                if (!isOnNewLine(formattedWords)) {
                    formattedWords.append(NEW_LINE_CHAR);
                }

                if (formattedWords.length() > 0) {
                    formattedWords.append(NEW_LINE_CHAR);
                }

                // insert date
                formattedWords.append(cword + WHITE_SPACE_CHAR);

                // insert name/email
                while (candidateWords.hasMoreTokens()) {
                    cword = candidateWords.nextToken();
                    if (cword.equals(NEW_LINE_CHAR)) {
                        formattedWords.append(NEW_LINE_CHAR);
                        break;
                    }
                    if (isEmail(cword)) {
                        formattedWords.append(WHITE_SPACE_CHAR)
                                      .append(WHITE_SPACE_CHAR)
                                      .append(cword)
                                      .append(NEW_LINE_CHAR)
                                      .append(NEW_LINE_CHAR);
                        currentLineLength = indentationLength;
                        break;
                    }
                    if (!isDelimeter(cword)) {
                        formattedWords.append(WHITE_SPACE_CHAR).append(cword);
                    }
                }
            }

            // means beginning of file name, so whole filename should be
            // in one line.
            else if (isStar(cword)) {
                // see if we are in middle of line and
                // if so, start new line, else continue.
                if (!isOnNewLine(formattedWords)) {
                    formattedWords.append(NEW_LINE_CHAR);
                    currentLineLength = indentationLength;
                }

                formattedWords.append(TAB_SPACE_CHAR).append(cword);
                currentLineLength += cword.length() + 1;

                // this should be path name
                if (candidateWords.countTokens() >= 2) {
                    candidateWords.nextToken();
                    cword = candidateWords.nextToken();

                    formattedWords.append(WHITE_SPACE_CHAR).append(cword);
                    currentLineLength += cword.length() + 1;
                }
            }

            else if (cword.startsWith("(")) {

                if (formattedWords.length() > 0) {
                    formattedWords.append(NEW_LINE_CHAR);
                }
                formattedWords.append(TAB_SPACE_CHAR);

                currentLineLength = 1;
                // add until closing bracket

                boolean skipMultiWhiteSpace = false;

                while (!cword.endsWith("):") && candidateWords.hasMoreTokens()) {
                    if (cword.equals(NEW_LINE_CHAR)) {
                        break;
                    }
                    if (cword.equals(WHITE_SPACE_CHAR) && !skipMultiWhiteSpace) {
                        formattedWords.append(cword);
                        currentLineLength += cword.length();
                        skipMultiWhiteSpace = true;
                    }
                    if (!isDelimeter(cword)) {
                        formattedWords.append(cword);
                        currentLineLength += cword.length();
                        skipMultiWhiteSpace = false;
                    }
                    cword = candidateWords.nextToken();
                }

                formattedWords.append(cword);
                currentLineLength += cword.length();
            }

            else if (currentLineLength + cword.length() > MAX_WIDTH) {
                formattedWords.append(NEW_LINE_CHAR)
                              .append(TAB_SPACE_CHAR)
                              .append(cword);
                currentLineLength = indentationLength + cword.length();
            } else {
                if (isOnNewLine(formattedWords)) {
                    formattedWords.append(TAB_SPACE_CHAR);
                } else {
                    formattedWords.append(WHITE_SPACE_CHAR);
                }
                formattedWords.append(cword);
                currentLineLength += cword.length() + 1;
            }
        }
        return formattedWords.toString();
    }

    private boolean isDate(String inputStr) {
        try {
            return isoDate.parse(inputStr) != null;
        } catch (ParseException e) {
            // Don't care
        }
        return false;
    }

    private boolean isEmail(String inputStr) {
        return inputStr.startsWith("<") && inputStr.endsWith(">");
    }

    private boolean isStar(String inputStr) {
        return inputStr.equals("*");
    }

    private boolean isDelimeter(String cword) {
        return DELIMITER_CHARS.contains(cword);
    }

    private boolean isOnNewLine(StringBuilder formattedWords) {
        int len = formattedWords.length();
        return len == 0 || formattedWords.charAt(len - 1) == NEW_LINE_CHAR.charAt(0);
    }

    @Override
    public void formatterStarts(String initialIndentation) {

    }

    @Override
    public void formatterStops() {

    }

}
