/*
 * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package jdk.test.lib.format;

/**
 * A collection of formatting utilities
 */
public class Format {

    /**
     * Formats character as literal, using C-style escaping for unprintable symbols
     *
     * @param c character to format
     * @return formatted string representation of the character
     */
    public static String asLiteral(char c) {
        StringBuilder sb = new StringBuilder();
        appendCharToSb(c, sb);
        return sb.toString();
    }

    /**
     * Escapes String in C-style
     *
     * @param src source string
     * @return C-style escaped source string
     */
    public static String escapeString(String src) {
        StringBuilder sb = new StringBuilder();
        src.chars().forEachOrdered(
                (c) -> appendCharToSb((char) c, sb));
        return sb.toString();
    }

    /**
     * Formats Object as literal, using its String representation C-style escaped.
     *
     * @param o object to format
     * @return C-style escaped String representation of the object
     */
    public static String asLiteral(Object o) {
        if (o instanceof String) {
            return '"' + escapeString((String)o) + '"';
        } else if (o instanceof Character) {
            return asLiteral((char) o);
        } else if (o instanceof Byte) {
            return String.valueOf(o);
        } else {
            return escapeString(String.valueOf(o));
        }
    }

    /**
     * Formats a difference between two arrays with index of the first mismatch element,
     * and slices of arrays necessary to understand the problem, along with a failure mark.
     *
     * @param first first array to compare
     * @param second second array to compare
     * @return the difference, generated by the {@link ArrayDiff ArrayDiff}
     */
    public static String arrayDiff(Object first, Object second) {
        return ArrayDiff.of(first, second).format();
    }

    /**
     * Formats a difference between two arrays with index of the first mismatch element,
     * and slices of arrays necessary to understand the problem, along with a failure mark.
     * Takes into account maximum allowed width and context (in elements) before the mismatch.
     *
     * @param first first array to compare
     * @param second second array to compare
     * @param width the maximum allowed width in characters for the formatting
     * @param contextBefore maximum number of elements to print before those that differ
     * @return the difference, generated by the {@link ArrayDiff ArrayDiff}
     */
    public static String arrayDiff(Object first, Object second, int width, int contextBefore) {
        return ArrayDiff.of(first, second, width, contextBefore).format();
    }


    /**
     * Returns a string of spaces with length specified.
     *
     * @param width number of spaces in the resulting string
     * @return Padding string of spaces
     */
    public static String paddingForWidth(int width) {
        return " ".repeat(width);
    }

    private static void appendCharToSb(char c, StringBuilder sb) {
        if (c == 10) {
            sb.append("\\n");
        } else if (c == 13) {
            sb.append("\\r");
        } else if (c == 92) {
            sb.append("\\\\");
        } else if (c == 34) {
            sb.append("\\\"");
        } else if (c < 32 || c > 126) {
            sb.append("\\u" + String.format("%04X", (int) c));
        } else {
            sb.append(c);
        }
    }
}
