package com.tradehero.route.internal;

import java.util.LinkedHashSet;
import java.util.Set;

final class RouteInjector {
  private final String classPackage;
  private final String targetClass;
  private final String bundleKey;
  private final String className;
  private final Set<FieldBinding> fieldBinding = new LinkedHashSet<FieldBinding>();
  private String parentInjector;

  RouteInjector(String classPackage, String className, String targetClass, String bundleKey) {
    this.classPackage = classPackage;
    this.className = className;
    this.targetClass = targetClass;
    this.bundleKey = bundleKey;
  }

  String getFqcn() {
    return Utils.isNullOrEmpty(classPackage) ? className : classPackage + "." + className;
  }

  String brewJava() {
    StringBuilder builder = new StringBuilder();
    builder.append("// Generated code by Route. Do not modify!\n");
    if (!Utils.isNullOrEmpty(classPackage)) {
      builder.append("package ").append(classPackage).append(";\n\n");
    }
    builder.append("import android.os.Bundle;\n");
    builder.append("import com.tradehero.route.Router;\n\n");
    builder.append("public final class ").append(className).append(" {\n");

    emitInject(builder);
    builder.append("\n\n");

    emitSaver(builder);
    builder.append("\n");

    builder.append("}\n");

    Utils.debug(builder.toString());
    return builder.toString();
  }

  private void emitSaver(StringBuilder builder) {
    builder.append("  ")
        .append("public static void save(final ")
        .append(targetClass)
        .append(" source, Bundle dest, boolean flat) {\n");

    builder.append("    ")
        .append("Bundle toWrite = null;\n");

    for (FieldBinding binding: fieldBinding) {
      if (binding instanceof RoutePropertyBinding)
      emitSaveBinding(builder, (RoutePropertyBinding) binding);
    }

    builder.append("\n    ")
        .append("if (!flat) dest.putBundle(\"")
        .append(bundleKey)
        .append("\", toWrite);\n");
    builder.append("  ")
        .append("}\n");
  }

  private void emitInject(StringBuilder builder) {
    builder.append("  ")
        .append("public static void inject(final ")
        .append(targetClass)
        .append(" target, Bundle source) {\n");
    // Emit a call to the superclass injector, if any.
    if (parentInjector != null) {
      builder.append("    ")
          .append(parentInjector)
          .append(".inject(target, source);\n\n");
    }

    builder.append("    ")
        .append("Bundle subBundle = source.getBundle(\"")
        .append(bundleKey)
        .append("\");\n");
    builder.append("    ")
        .append("if (subBundle != null) {\n");

    builder.append("      ")
        .append("inject(target, subBundle);\n");
    builder.append("    ")
        .append("}\n");

    for (FieldBinding binding: fieldBinding) {
      if (binding instanceof RoutePropertyBinding) {
        emitInjectBinding(builder, (RoutePropertyBinding) binding);
      } else {
        emitRedirectBinding(builder, binding);
      }
    }

    builder.append("  }");
  }

  private void emitRedirectBinding(StringBuilder builder, FieldBinding binding) {
    String fieldPath = "target." + binding.getName();

    builder.append("    ")
        .append("if (")
        .append(fieldPath)
        .append(" == null) ")
        .append(fieldPath)
        .append(" = new ")
        .append(binding.getBundleMethod())
        .append("();\n");

    builder.append("    ")
        .append("Router.getInstance().inject(target.")
        .append(binding.getName())
        .append(", source);\n");
  }

  private void emitInjectBinding(StringBuilder builder, RoutePropertyBinding binding) {
    if (binding.isMethod() && !binding.getName().startsWith("set")) {
      return;
    }
    builder.append("    ")
        .append("if (source.containsKey(\"")
        .append(binding.getBundleKey())
        .append("\")) {\n");

    builder.append("      ")
        .append("target.")
        .append(binding.getName())
        .append(binding.isMethod() ? "(" : " = ")

        .append("source.get")
        .append(binding.getBundleMethod())
        .append("(\"")
        .append(binding.getBundleKey())
        .append("\")")

        .append(binding.isMethod() ? ")" : "")
        .append(";\n");
    builder.append("    ")
        .append("}\n");
  }

  private void emitSaveBinding(StringBuilder builder, RoutePropertyBinding binding) {
    if (binding.isMethod() && !binding.getName().startsWith("get")) {
      return;
    }

    builder.append("    ")
        .append("toWrite = flat ? dest : new Bundle();\n");
    builder.append("    ")
        .append("toWrite.put")
        .append(binding.getBundleMethod())
        .append("(\"")
        .append(binding.getBundleKey())
        .append("\", source.")
        .append(binding.getName())
        .append(binding.isMethod() ? "()" : "")
        .append(")")
        .append(";\n");
  }

  public void addBinding(FieldBinding binding) {
    fieldBinding.add(binding);
  }

  public void setParentInjector(String parentInjector) {
    this.parentInjector = parentInjector;
  }
}

@@ -1668,24 +1668,107 @@
  = null;%5Cn%22)
+%0A        .append(%22    %22)%0A        .append(%22toWrite = flat ? dest : new Bundle();%5Cn%22)
 ;%0A%0A    for (
@@ -1855,17 +1855,21 @@
 Binding)
-%0A
+ %7B%0A  
       em
@@ -1920,24 +1920,98 @@
 ) binding);%0A
+      %7D else %7B%0A        emitRedirectSaveBinding(builder, binding);%0A      %7D%0A
     %7D%0A%0A    b
@@ -2180,34 +2180,32 @@
       .append(%22%7D
-%5Cn
 %22);%0A  %7D%0A%0A  priva
@@ -4685,96 +4685,8 @@
    %7D
-%0A%0A    builder.append(%22    %22)%0A        .append(%22toWrite = flat ? dest : new Bundle();%5Cn%22);
 %0A   
@@ -5002,32 +5002,304 @@
 nd(%22;%5Cn%22);%0A  %7D%0A%0A
+  private void emitRedirectSaveBinding(StringBuilder builder, FieldBinding binding) %7B%0A    builder.append(%22    %22)%0A        .append(%22Router.getInstance().saveSingle(toWrite, source.%22)%0A        .append(binding.getName())%0A        .append(%22, flat)%22)%0A        .append(%22;%5Cn%22);%0A  %7D%0A%0A
   public void ad

Fix generate `save` method