/*
 * Copyright 2018 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.template.soy.base.internal.IdGenerator;
import com.google.template.soy.error.ErrorReporter;
import com.google.template.soy.error.SoyErrorKind;
import com.google.template.soy.logging.ValidatedLoggingConfig;
import com.google.template.soy.shared.SoyGeneralOptions;
import com.google.template.soy.soytree.AliasDeclaration;
import com.google.template.soy.soytree.SoyFileNode;
import com.google.template.soy.types.SoyType;
import com.google.template.soy.types.SoyTypeRegistry;

/**
 * Checks that aliases don't conflict with things that can be aliased (or their namespace prefixes).
 */
final class ValidateAliasesPass implements CompilerFilePass {
  private static final SoyErrorKind ALIAS_CONFLICTS_WITH_GLOBAL =
      SoyErrorKind.of("Alias ''{0}'' conflicts with a global of the same name.");
  private static final SoyErrorKind ALIAS_CONFLICTS_WITH_GLOBAL_PREFIX =
      SoyErrorKind.of("Alias ''{0}'' conflicts with namespace for global ''{1}''.");

  private static final SoyErrorKind ALIAS_CONFLICTS_WITH_TYPE_NAME =
      SoyErrorKind.of("Alias ''{0}'' conflicts with a type of the same name.");
  private static final SoyErrorKind ALIAS_CONFLICTS_WITH_TYPE_PREFIX =
      SoyErrorKind.of("Alias ''{0}'' conflicts with namespace for type ''{1}''.");

  private static final SoyErrorKind ALIAS_CONFLICTS_WITH_VE =
      SoyErrorKind.of("Alias ''{0}'' conflicts with a VE of the same name.");

  private final ErrorReporter errorReporter;
  private final SoyGeneralOptions options;
  private final ValidatedLoggingConfig loggingConfig;

  ValidateAliasesPass(
      ErrorReporter errorReporter,
      SoyGeneralOptions options,
      ValidatedLoggingConfig loggingConfig) {
    this.errorReporter = errorReporter;
    this.options = options;
    this.loggingConfig = loggingConfig;
  }

  @Override
  public void run(SoyFileNode file, IdGenerator nodeIdGen) {
    SoyTypeRegistry registry = file.getSoyTypeRegistry();
    for (AliasDeclaration alias : file.getAliasDeclarations()) {
      if (options.getCompileTimeGlobals().containsKey(alias.alias().identifier())) {
        errorReporter.report(alias.alias().location(), ALIAS_CONFLICTS_WITH_GLOBAL, alias.alias());
      }
      SoyType type = registry.getType(alias.alias().identifier());
      // When running with a dummy type provider that parses all types as unknown, ignore that.
      if (type != null && type.getKind() != SoyType.Kind.UNKNOWN) {
        errorReporter.report(
            alias.alias().location(), ALIAS_CONFLICTS_WITH_TYPE_NAME, alias.alias());
      }
      String conflictingNamespacedType =
          registry.findTypeWithMatchingNamespace(alias.alias().identifier());
      if (conflictingNamespacedType != null) {
        errorReporter.report(
            alias.alias().location(),
            ALIAS_CONFLICTS_WITH_TYPE_PREFIX,
            alias.alias(),
            conflictingNamespacedType);
      }
      String prefix = alias.alias().identifier() + ".";
      for (String global : options.getCompileTimeGlobals().keySet()) {
        if (global.startsWith(prefix)) {
          errorReporter.report(
              alias.alias().location(), ALIAS_CONFLICTS_WITH_GLOBAL_PREFIX, alias.alias(), global);
        }
      }
      if (loggingConfig.getElement(alias.alias().identifier()) != null) {
        errorReporter.report(alias.alias().location(), ALIAS_CONFLICTS_WITH_VE, alias.alias());
      }
    }
  }
}

@@ -1020,16 +1020,122 @@
 leNode;%0A
+import com.google.template.soy.types.SoyProtoEnumType;%0Aimport com.google.template.soy.types.SoyProtoType;%0A
 import c
@@ -3159,24 +3159,295 @@
 .UNKNOWN) %7B%0A
+        // Temporarily, while we migrate all proto aliases to imports, ignore aliases that are%0A        // identical to proto imports. They don't conflict and Tricorder will remove the aliases.%0A        if (!alias.namespace().identifier().equals(getFqProtoName(type))) %7B%0A  
         erro
@@ -3456,32 +3456,34 @@
 eporter.report(%0A
+  
             alia
@@ -3544,32 +3544,42 @@
 alias.alias());%0A
+        %7D%0A
       %7D%0A      St
@@ -4430,11 +4430,336 @@
    %7D%0A  %7D
+%0A%0A  private static String getFqProtoName(SoyType type) %7B%0A    if (type instanceof SoyProtoType) %7B%0A      return ((SoyProtoType) type).getDescriptor().getFullName();%0A    %7D else if (type instanceof SoyProtoEnumType) %7B%0A      return ((SoyProtoEnumType) type).getDescriptor().getFullName();%0A    %7D else %7B%0A      return null;%0A    %7D%0A  %7D
 %0A%7D%0A

Allow aliases collisions when identical to proto imports. Makes multipass proto tricorder more resilient.