/*
Copyright (c) 2016 Red Hat, Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package org.ovirt.sdk.go;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Formatter;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.inject.Inject;

import org.apache.commons.io.FileUtils;

/**
 * This class is a buffer intended to simplify generation of Go source code. It stores the name of the module, the
 * list of imports and the rest of the source separately, so that imports can be added on demand while generating the
 * rest of the source.
 */
public class GoBuffer {
    // Reference to the object used to generate names:
    @Inject private GoNames goNames;

    // The name of the file:
    private String fileName;

    // The name of the package:
    private String packageName;

    // The imports:
    private Set<String> imports = new HashSet<>();

    // The source lines:
    private List<String> lines = new ArrayList<>();

    // The current indentation level:
    private int level;

    /**
     * Sets the file name.
     */
    public void setFileName(String newFileName) {
        fileName = newFileName;
    }

    /**
     * Sets the module name:
     */
    public void setPackageName(String newPackageName) {
        packageName = newPackageName;
    }

    /**
     * Starts an indented block.
     */
    public void startBlock() {
        level++;
    }

    /**
     * Ends an indented block.
     */
    public void endBlock() {
        if (level > 0) {
            level--;
        }
    }

    /**
     * Adds one import.
     */
    public void addImport(String format, Object ... args) {
        // Format the line:
        StringBuilder buffer = new StringBuilder();
        Formatter formatter = new Formatter(buffer);
        formatter.format(format, args);

        // Add the line to the set:
        imports.add(buffer.toString());
    }

    /**
     * Adds multiple imports.
     */
    public void addImports(List<String> imports) {
        imports.forEach(this::addImport);
    }

    /**
     * Adds an empty line to the body of the class.
     */
    public void addLine() {
        lines.add("");
    }

    /**
     * Adds a formatted line to the body of the class. The given {@code args} are formatted using the
     * provided {@code format} using the {@link String#format(String, Object...)} method.
     */
    public void addLine(String format, Object ... args) {
        // Format the line:
        StringBuilder buffer = new StringBuilder();
        Formatter formatter = new Formatter(buffer);
        formatter.format(format, args);

        // Indent the line:
        for (int i = 0; i < level; i++) {
            buffer.insert(0, "    ");
        }

        // Add the line to the list:
        lines.add(buffer.toString());
    }

    /**
     * Adds a non-formatted line to the body of the class.
     */
    public void addRawLine(String line) {
        StringBuilder buffer = new StringBuilder(line);
        // Indent the line:
        for (int i = 0; i < level; i++) {
            buffer.insert(0, "    ");
        }

        // Add the line to the list:
        lines.add(buffer.toString());
    }

    /**
     * Starts a multi line comment.
     */
    public void startComment() {
        addLine("\"\"\"");
    }

    /**
     * Ends a multi line comment.
     */
    public void endComment() {
        addLine("\"\"\"");
    }

    /**
     * Generates the complete source code of the class.
     */
    public String toString() {
        StringBuilder buffer = new StringBuilder();

        // License:
        buffer.append("//\n");
        buffer.append("// Copyright (c) 2017 Red Hat, Inc.\n");
        buffer.append("//\n");
        buffer.append("// Licensed under the Apache License, Version 2.0 (the \"License\");\n");
        buffer.append("// you may not use this file except in compliance with the License.\n");
        buffer.append("// You may obtain a copy of the License at\n");
        buffer.append("//\n");
        buffer.append("//   http://www.apache.org/licenses/LICENSE-2.0\n");
        buffer.append("//\n");
        buffer.append("// Unless required by applicable law or agreed to in writing, software\n");
        buffer.append("// distributed under the License is distributed on an \"AS IS\" BASIS,\n");
        buffer.append("// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n");
        buffer.append("// See the License for the specific language governing permissions and\n");
        buffer.append("// limitations under the License.\n");
        buffer.append("//\n");

        // Package:
        buffer.append("package ovirtsdk4");
        buffer.append("\n");
        buffer.append("\n");

        // Add the imports:
        if (!imports.isEmpty()) {
            buffer.append("import (\n");
            imports.stream().sorted().forEach(line -> {
                // four blanks
                buffer.append("    ");
                // "fmt"
                buffer.append("\"" + line + "\"");
                buffer.append("\n");
            });
            buffer.append(")\n");
            buffer.append("\n");
        }

        // Body:
        for (String line : lines) {
            buffer.append(line);
            buffer.append("\n");
        }

        return buffer.toString();
    }

    /**
     * Creates a {@code .go} source file and writes the source. The required intermediate directories will be created
     * if they don't exist.
     *
     * @param dir the base directory for the source code
     * @throws IOException if something fails while creating or writing the file
     */
    public void write(File dir) throws IOException {
        // Calculate the complete file name:
        if (fileName == null) {
            fileName = packageName;
        }
        File file = new File(dir, fileName + ".go");

        // Create the directory and all its parent if needed:
        File parent = file.getParentFile();
        FileUtils.forceMkdir(parent);

        // Write the file:
        System.out.println("Writing file \"" + file.getAbsolutePath() + "\".");
        try (Writer writer = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8)) {
            writer.write(toString());
        }
    }
}

@@ -3836,32 +3836,176 @@
 ring());%0A    %7D%0A%0A
+    /**%0A     * Adds a non-formatted comment line%0A     */%0A    public void addCommentLine(String line) %7B%0A        addRawLine(%22// %22 + line);%0A    %7D%0A%0A
     /**%0A     * S
@@ -4024,32 +4024,32 @@
 i line comment.%0A
-
      */%0A    publ
@@ -4082,38 +4082,34 @@
        addLine(%22
-%5C%22%5C%22%5C%22
+//
 %22);%0A    %7D%0A%0A    /
@@ -4205,14 +4205,10 @@
 ne(%22
-%5C%22%5C%22%5C%22
+//
 %22);%0A

make comment as Go style