/*******************************************************************************
 * Copyright (c) 2013, 2018 Red Hat.
 *
 * 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:
 *     Red Hat - Andrew Ferrazzutti
 *******************************************************************************/

package org.eclipse.linuxtools.internal.systemtap.ui.ide;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

/**
 * A helper class for removing comments from a SystemTap script.
 * Note: if an entire line is a comment, it is replaced with a blank line.
 */
public final class CommentRemover {

    private CommentRemover() {}

    /**
     * Remove comments from a .stp file in the filesystem.
     * @param filename The filename of the script to remove comments from.
     * @return The copy of the script with comments removed.
     */
    public static String execWithFile(String filename) {
        try (BufferedReader br = new BufferedReader(new FileReader(filename))) {
			StringBuilder buffer = new StringBuilder();
            String line;
            while ((line = br.readLine()) != null) {
                buffer.append(line.concat("\n")); //$NON-NLS-1$
            }
            // No need to format line breaks, since this did it already with newlines.
            return exec(buffer.toString(), false);
        } catch (IOException e) {
            return ""; //$NON-NLS-1$
        }
    }

    /**
     * Remove comments from a .stp script. Also, line breaks will be formatted to newlines.
     * @param contents A complete .stp script.
     * @return A copy of the script with comments removed.
     */
    public static String exec(String contents) {
        return exec(contents, true);
    }

    private static String exec(String contents, boolean standardizeLineBreaks) {
        if (contents == null || contents.isEmpty()) {
            return ""; //$NON-NLS-1$
        }
        if (standardizeLineBreaks) {
            contents = doStandardizeLineBreaks(contents);
        }

        char curchar, nxtchar;
        boolean inQuotes = false;
        boolean inComment = false;

        int c = 0;
		StringBuilder buffer = new StringBuilder();

        do {
            curchar = contents.charAt(c++);
            nxtchar = c < contents.length() ? contents.charAt(c) : '\0';

            // Comment tags don't count if they are in a string.
            if (!inQuotes) {
                if (!inComment) {
                    if (curchar == '#' || (curchar == '/' && nxtchar == '/')) {
                        buffer.append('\n'); // Replace the rest of this line with a newline.
                        c = contents.indexOf('\n', c); // Skip past the next newline, if one exists.
                        if (c == -1) {
                            break;
                        }
                        c++; // Skip the newline character on the next character scan.
                        continue;
                    }
                    if (curchar == '/' && nxtchar == '*') {
                        inComment = true;
                        c++; // Skip the * on the next character scan.
                        continue;
                    }
                } else if (curchar == '*' && nxtchar == '/') {
                    inComment = false;
                    c++; // Skip the / on the next character scan.
                    continue;
                }
            }

            // Quotes only count if they aren't commented out.
            if (!inComment) {
                if (curchar == '\"') {
                    inQuotes = !inQuotes;
                }
                else if (curchar == '\n' && inQuotes) {
                    inQuotes = false;
                }
                buffer.append(curchar);
            }
            else if (curchar == '\n') {
                // Print the line breaks of multiline comments.
                buffer.append(curchar);
            }

        } while (c < contents.length());

        return buffer.toString();
    }

    private static String doStandardizeLineBreaks(String contents) {
        return contents.replaceAll("(\\r\\n)|(\\n)", "\n"); //$NON-NLS-1$ //$NON-NLS-2$
    }
}