/*
 * Copyright 2013 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.common.css.compiler.passes;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Maps;
import com.google.common.css.compiler.ast.CssCompilerPass;
import com.google.common.css.compiler.ast.CssDefinitionNode;
import com.google.common.css.compiler.ast.CssMixinDefinitionNode;
import com.google.common.css.compiler.ast.CssProvideNode;
import com.google.common.css.compiler.ast.CssRequireNode;
import com.google.common.css.compiler.ast.DefaultTreeVisitor;
import com.google.common.css.compiler.ast.VisitController;

import java.util.Map;

/**
 * A compiler pass to help find missing {@code @require} lines for def constant references
 * and mixins.
 * This pass simply collects namespaces that correpond to constant definitions and mixins.
 * Also see the CheckMissingRequire pass that is used in conjunction with this one.
 *
 */
public final class CollectProvideNamespaces extends DefaultTreeVisitor implements CssCompilerPass {
  private final VisitController visitController;

  // Key: filename; Value: provide namespace
  private final Map<String, String> filenameProvideMap = Maps.newHashMap();
  // Key: filename; Value: require namespace
  private final ListMultimap<String, String> filenameRequireMap = LinkedListMultimap.create();

  // Multiple namespaces can contain the same defs due to duplicate defs (or mods).
  // Key: def name; Value: provide namespace
  private final ListMultimap<String, String> defProvideMap = LinkedListMultimap.create();
  // Key: defmixin name; Value: provide namespace
  private final ListMultimap<String, String> defmixinProvideMap = LinkedListMultimap.create();

  public Map<String, String> getFilenameProvideMap() {
    return ImmutableMap.copyOf(filenameProvideMap);
  }

  public ListMultimap<String, String> getFilenameRequireMap() {
    return ImmutableListMultimap.copyOf(filenameRequireMap);
  }

  public ListMultimap<String, String> getDefProvideMap() {
    return ImmutableListMultimap.copyOf(defProvideMap);
  }

  public ListMultimap<String, String> getDefmixinProvideMap() {
    return ImmutableListMultimap.copyOf(defmixinProvideMap);
  }

  public CollectProvideNamespaces(VisitController visitController) {
    this.visitController = visitController;
  }

  @Override
  public boolean enterProvideNode(CssProvideNode node) {
    Preconditions.checkState(node.getSourceCodeLocation() != null);
    String filename = node.getSourceCodeLocation().getSourceCode().getFileName();
    filenameProvideMap.put(filename, node.getProvide());
    return true;
  }

  @Override
  public boolean enterRequireNode(CssRequireNode node) {
    Preconditions.checkState(node.getSourceCodeLocation() != null);
    String filename = node.getSourceCodeLocation().getSourceCode().getFileName();
    filenameRequireMap.put(filename, node.getRequire());
    return true;
  }

  @Override
  public boolean enterDefinition(CssDefinitionNode node) {
    if (node.getSourceCodeLocation() == null) {
      // Cannot enforce provide / require for generated GSS components.
      return true;
    }
    String filename = node.getSourceCodeLocation().getSourceCode().getFileName();
    String provideNamespace = filenameProvideMap.get(filename);
    // Remove this after switching to the new syntax.
    if (provideNamespace == null) {  // ignore old format @provide
      return true;
    }
    defProvideMap.put(node.getName().getValue(), provideNamespace);
    return true;
  }

  @Override
  public boolean enterMixinDefinition(CssMixinDefinitionNode node) {
    if (node.getSourceCodeLocation() == null) {
      // Cannot enforce provide / require for generated GSS components.
      return true;
    }
    String filename = node.getSourceCodeLocation().getSourceCode().getFileName();
    String provideNamespace = filenameProvideMap.get(filename);
    // Remove this after switching to the new syntax.
    if (provideNamespace == null) {  // ignore old format @provide
      return true;
    }
    Preconditions.checkArgument(provideNamespace != null);
    defmixinProvideMap.put(node.getDefinitionName(), provideNamespace);
    return true;
  }

  @Override
  public void runPass() {
    visitController.startVisit(this);
  }
}

@@ -686,111 +686,8 @@
 ns;%0A
-import com.google.common.collect.ImmutableListMultimap;%0Aimport com.google.common.collect.ImmutableMap;%0A
 impo
@@ -727,32 +727,32 @@
 edListMultimap;%0A
+
 import com.googl
@@ -1556,16 +1556,102 @@
 one.%0A *%0A
+ * NOTE: The maps in this class can only be used within the same set of pass runs.%0A *%0A
  */%0Apubl
@@ -2493,28 +2493,8 @@
 urn 
-ImmutableMap.copyOf(
 file
@@ -2499,33 +2499,32 @@
 lenameProvideMap
-)
 ;%0A  %7D%0A%0A  public 
@@ -2593,37 +2593,8 @@
 urn 
-ImmutableListMultimap.copyOf(
 file
@@ -2603,25 +2603,24 @@
 meRequireMap
-)
 ;%0A  %7D%0A%0A  pub
@@ -2688,37 +2688,8 @@
 urn 
-ImmutableListMultimap.copyOf(
 defP
@@ -2689,33 +2689,32 @@
 rn defProvideMap
-)
 ;%0A  %7D%0A%0A  public 
@@ -2783,37 +2783,8 @@
 urn 
-ImmutableListMultimap.copyOf(
 defm
@@ -2797,17 +2797,16 @@
 ovideMap
-)
 ;%0A  %7D%0A%0A 
@@ -4791,32 +4791,32 @@
  %7D%0A%0A  @Override%0A
-
   public void ru
@@ -4821,24 +4821,147 @@
 runPass() %7B%0A
+    filenameProvideMap.clear();%0A    filenameRequireMap.clear();%0A    defProvideMap.clear();%0A    defmixinProvideMap.clear();%0A
     visitCon

Change the specificity conflict error message to also mention the @overrideSelector suppression, including the appropriate namespace.