/*
 * Copyright 2014 Google Inc. All rights reserved.
 *
 * 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.inferred.freebuilder.processor.util;

import com.google.googlejavaformat.java.Formatter;
import com.google.googlejavaformat.java.FormatterException;

import org.inferred.freebuilder.processor.util.feature.EnvironmentFeatureSet;
import org.inferred.freebuilder.processor.util.feature.Feature;
import org.inferred.freebuilder.processor.util.feature.FeatureType;

import java.util.Collection;

import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.ElementFilter;

/** {@code SourceBuilder} which also handles package declaration and imports. */
public class CompilationUnitBuilder implements SourceBuilder {

  private final ImportManager importManager;
  private final SourceBuilder source;
  private final QualifiedName classToWrite;

  /**
   * Returns a {@link CompilationUnitBuilder} for {@code classToWrite}. The file preamble (package
   * and imports) will be generated automatically; the feature set will be taken from {@code env}..
   */
  public CompilationUnitBuilder(
      ProcessingEnvironment env,
      QualifiedName classToWrite,
      Collection<QualifiedName> nestedClasses) {
    this.classToWrite = classToWrite;
    // Write the source code into an intermediate SourceStringBuilder, as the imports need to be
    // written first, but aren't known yet.
    ImportManager.Builder importManagerBuilder = new ImportManager.Builder();
    importManagerBuilder.addImplicitImport(classToWrite);
    PackageElement pkg = env.getElementUtils().getPackageElement(classToWrite.getPackage());
    for (TypeElement sibling : ElementFilter.typesIn(pkg.getEnclosedElements())) {
      importManagerBuilder.addImplicitImport(QualifiedName.of(sibling));
    }
    for (QualifiedName nestedClass : nestedClasses) {
      importManagerBuilder.addImplicitImport(nestedClass);
    }
    importManager = importManagerBuilder.build();
    source = new SourceStringBuilder(importManager, new EnvironmentFeatureSet(env));
  }

  @Override
  public CompilationUnitBuilder add(String fmt, Object... args) {
    source.add(fmt, args);
    return this;
  }

  @Override
  public SourceBuilder add(Excerpt excerpt) {
    source.add(excerpt);
    return this;
  }

  @Override
  public CompilationUnitBuilder addLine(String fmt, Object... args) {
    source.addLine(fmt, args);
    return this;
  }

  @Override
  public SourceStringBuilder subBuilder() {
    return source.subBuilder();
  }

  @Override
  public <T extends Feature<T>> T feature(FeatureType<T> feature) {
    return source.feature(feature);
  }

  @Override
  public String toString() {
    StringBuilder unit = new StringBuilder();
    unit.append("// Autogenerated code. Do not modify.\n")
        .append("package ").append(classToWrite.getPackage()).append(";\n")
        .append("\n");
    if (!importManager.getClassImports().isEmpty()) {
      for (String classImport : importManager.getClassImports()) {
        unit.append("import ").append(classImport).append(";\n");
      }
      unit.append("\n");
    }
    try {
      unit.append(new Formatter().formatSource(source.toString()));
    } catch (FormatterException e) {
      throw new RuntimeException(
          "Formatter failed:\n" + e.getMessage() + "\nGenerated source:\n" + source, e);
    }
    return unit.toString();
  }
}

@@ -709,68 +709,8 @@
 ter;
-%0Aimport com.google.googlejavaformat.java.FormatterException;
 %0A%0Aim
@@ -3666,20 +3666,8 @@
     
-try %7B%0A      
 unit
@@ -3674,16 +3674,160 @@
 .append(
+formatSource(source.toString()));%0A    return unit.toString();%0A  %7D%0A%0A  private static String formatSource(String source) %7B%0A    try %7B%0A      return 
 new Form
@@ -3849,36 +3849,24 @@
 ource(source
-.toString())
 );%0A    %7D cat
@@ -3869,25 +3869,149 @@
  catch (
-Formatter
+UnsupportedClassVersionError e) %7B%0A      // Formatter requires Java 7+; do no formatting in Java 6.%0A      return source;%0A    %7D catch (
 Exceptio
@@ -4144,42 +4144,14 @@
 e);%0A
+
     %7D%0A
-    return unit.toString();%0A
   %7D%0A

Support Java 6 again