package org.inferred.freebuilder.processor.excerpt;

import static org.inferred.freebuilder.processor.util.feature.FunctionPackage.FUNCTION_PACKAGE;

import com.google.common.collect.ImmutableList;

import org.inferred.freebuilder.processor.util.Excerpt;
import org.inferred.freebuilder.processor.util.ParameterizedType;
import org.inferred.freebuilder.processor.util.SourceBuilder;

import java.util.AbstractList;
import java.util.List;
import java.util.RandomAccess;

/**
 * Excerpts defining a list implementation that delegates to a provided add method to perform
 * element validation and insertion into a random-access backing list.
 */
public class CheckedList {

  public static List<Excerpt> excerpts() {
    return ImmutableList.<Excerpt>of(CHECKED_LIST);
  }

  private static final Excerpt CHECKED_LIST = new Excerpt() {
    @Override
    public void addTo(SourceBuilder code) {
      ParameterizedType consumer = code.feature(FUNCTION_PACKAGE).consumer().orNull();
      if (consumer == null) {
        return;
      }
      code.addLine("")
          .addLine("/**")
          .addLine(" * A list implementation that delegates to a provided add method to perform")
          .addLine(" * element validation and insertion into a random-access backing list.")
          .addLine(" */")
          .addLine("private static class CheckedList<E> extends %s<E> implements %s {",
              AbstractList.class, RandomAccess.class)
          .addLine("")
          .addLine("  private final %s<E> list;", List.class)
          .addLine("  private final %s<E> add;", consumer.getQualifiedName())
          .addLine("")
          .addLine("  CheckedList(%s<E> list, %s<E> add) {",
              List.class, consumer.getQualifiedName())
          .addLine("    this.list = list;")
          .addLine("    this.add = add;")
          .addLine("  }")
          .addLine("")
          .addLine("  @Override public int size() {")
          .addLine("    return list.size();")
          .addLine("  }")
          .addLine("")
          .addLine("  @Override public E get(int index) {")
          .addLine("    return list.get(index);")
          .addLine("  }")
          .addLine("")
          .addLine("  @Override public E set(int index, E element) {")
          .addLine("    add.accept(element);")
          .addLine("    return list.set(index, list.remove(list.size() - 1));")
          .addLine("  }")
          .addLine("")
          .addLine("  @Override public void add(int index, E element) {")
          .addLine("    int endIndex = list.size();")
          .addLine("    add.accept(element);")
          .addLine("    if (index != endIndex) {")
          .addLine("      list.add(index, list.remove(endIndex));")
          .addLine("    }")
          .addLine("  }")
          .addLine("")
          .addLine("  @Override public E remove(int index) {")
          .addLine("    return list.remove(index);")
          .addLine("  }")
          .addLine("")
          .addLine("  @Override public void clear() {")
          .addLine("    list.clear();")
          .addLine("  }")
          .addLine("")
          .addLine("  @Override protected void removeRange(int fromIndex, int toIndex) {")
          .addLine("    list.subList(fromIndex, toIndex).clear();")
          .addLine("  }")
          .addLine("}");
    }
  };

  private CheckedList() {}
}
@@ -2491,32 +2491,186 @@
  E element) %7B%22)%0A
+          .addLine(%22    // Append to the end of the list with add, then move the inserted element%22)%0A          .addLine(%22    // to the desired location.%22)%0A
           .addLi

Add impl comment to CheckedList.add