/*
 * Copyright 2014 Google 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 com.google.template.soy.passes;

import com.google.common.base.CaseFormat;
import com.google.common.collect.Iterables;
import com.google.template.soy.base.internal.IdGenerator;
import com.google.template.soy.base.internal.QuoteStyle;
import com.google.template.soy.error.ErrorReporter;
import com.google.template.soy.error.SoyErrorKind;
import com.google.template.soy.exprtree.ExprNode;
import com.google.template.soy.exprtree.FunctionNode;
import com.google.template.soy.exprtree.StringNode;
import com.google.template.soy.shared.internal.BuiltinFunction;
import com.google.template.soy.soytree.SoyFileNode;
import com.google.template.soy.soytree.SoyTreeUtils;
import com.google.template.soy.soytree.TemplateNode;
import javax.annotation.Nullable;

/** Converts package-relative CSS class names to absolute names. */
final class ResolvePackageRelativeCssNamesPass extends CompilerFilePass {

  private static final String RELATIVE_SELECTOR_PREFIX = "%";

  private static final SoyErrorKind PACKAGE_RELATIVE_CLASS_NAME_USED_WITH_COMPONENT_NAME =
      SoyErrorKind.of(
          "Package-relative class name ''{0}'' cannot be used with component expression.");
  private static final SoyErrorKind NO_CSS_PACKAGE =
      SoyErrorKind.of(
          "No CSS package defined for package-relative class name ''{0}''. "
              + "CSS package prefixes are set via the ''cssbase'' attribute on the template, a "
              + "''cssbase'' attribute on the namespace, or the first ''requirecss'' package on "
              + "the namesapce.{1}.");

  private final ErrorReporter errorReporter;

  ResolvePackageRelativeCssNamesPass(ErrorReporter errorReporter) {
    this.errorReporter = errorReporter;
  }

  @Override
  public void run(SoyFileNode file, IdGenerator nodeIdGen) {
    // Compute the CSS package prefix for this template. The search order is:
    // 1) cssbase on the template
    // 2) cssbase on the namespace
    // 3) first requirecss on the namespace
    String namespacePrefix = null;
    if (file.getCssBaseNamespace() != null) {
      namespacePrefix = toCamelCase(file.getCssBaseNamespace());
    } else if (!file.getRequiredCssNamespaces().isEmpty()) {
      namespacePrefix = toCamelCase(file.getRequiredCssNamespaces().get(0));
    }
    for (TemplateNode template : file.getChildren()) {
      String packagePrefix = namespacePrefix;
      if (template.getCssBaseNamespace() != null) {
        packagePrefix = toCamelCase(template.getCssBaseNamespace());
      }
      for (FunctionNode fn : SoyTreeUtils.getAllNodesOfType(template, FunctionNode.class)) {
        resolveSelector(template, fn, packagePrefix);
      }
    }
  }

  private void resolveSelector(
      TemplateNode template, FunctionNode node, @Nullable String packagePrefix) {
    if (node.getSoyFunction() != BuiltinFunction.CSS) {
      return;
    }

    ExprNode lastChild = Iterables.getLast(node.getChildren());
    if (!(lastChild instanceof StringNode)) {
      // this will generate an error in CheckFunctionCallsVisitor
      return;
    }

    StringNode selector = (StringNode) Iterables.getLast(node.getChildren());
    String selectorText = selector.getValue();
    if (!selectorText.startsWith(RELATIVE_SELECTOR_PREFIX)) {
      return;
    }

    if (node.numChildren() > 1) {
      errorReporter.report(
          selector.getSourceLocation(),
          PACKAGE_RELATIVE_CLASS_NAME_USED_WITH_COMPONENT_NAME,
          selectorText);
    }

    if (packagePrefix == null) {
      errorReporter.report(
          selector.getSourceLocation(),
          NO_CSS_PACKAGE,
          selectorText,
          template.getRequiredCssNamespaces().isEmpty()
              ? ""
              : " NOTE: ''requirecss'' on a template is not used to infer the CSS package.");
    }

    // Replace the selector text with resolved selector text
    String prefixed = packagePrefix + selectorText.substring(RELATIVE_SELECTOR_PREFIX.length());
    StringNode newSelector =
        new StringNode(prefixed, QuoteStyle.SINGLE, selector.getSourceLocation());
    node.replaceChild(selector, newSelector);
  }

  private static String toCamelCase(String packageName) {
    String packageNameWithDashes = packageName.replace('.', '-');
    return CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, packageNameWithDashes);
  }
}

@@ -2133,18 +2133,18 @@
 he names
-a
 p
+a
 ce.%7B1%7D.%22

Fix typo