/*
 * Copyright 2000-2009 JetBrains s.r.o.
 *
 * 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 org.jetbrains.idea.maven.importing;

import com.intellij.ide.highlighter.ModuleFileType;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.maven.model.MavenId;
import org.jetbrains.idea.maven.project.MavenProject;

import java.io.File;
import java.util.*;

public class MavenModuleNameMapper {
  public static void map(Collection<MavenProject> projects,
                         Map<MavenProject, Module> mavenProjectToModule,
                         Map<MavenProject, String> mavenProjectToModuleName,
                         Map<MavenProject, String> mavenProjectToModulePath,
                         String dedicatedModuleDir) {
    resolveModuleNames(projects,
                       mavenProjectToModule,
                       mavenProjectToModuleName);
    resolveModulePaths(projects,
                       mavenProjectToModule,
                       mavenProjectToModuleName,
                       mavenProjectToModulePath,
                       dedicatedModuleDir);
  }

  private static void resolveModuleNames(Collection<MavenProject> projects,
                                         Map<MavenProject, Module> mavenProjectToModule,
                                         Map<MavenProject, String> mavenProjectToModuleName) {
    NameItem[] names = new NameItem[projects.size()];

    int i = 0;
    for (MavenProject each : projects) {
      names[i++] = new NameItem(each, mavenProjectToModule.get(each));
    }

    Arrays.sort(names);

    Map<String, Integer> nameCounters = new HashMap<String, Integer>();

    for ( i = 0; i < names.length; i++) {
      if (names[i].hasDuplicatedGroup) continue;

      for (int k = i + 1; k < names.length; k++) {
        if (names[i].originalName.equals(names[k].originalName)) {
          nameCounters.put(names[i].originalName, 0);

          if (names[i].groupId.equals(names[k].groupId)) {
            names[i].hasDuplicatedGroup = true;
            names[k].hasDuplicatedGroup = true;
          }
        }
      }
    }

    Set<String> existingNames = new HashSet<String>();

    for (NameItem name : names) {
      if (name.module != null) {
        boolean wasAdded = existingNames.add(name.getResultName());
        assert wasAdded : name.getResultName();
      }
    }

    for (NameItem nameItem : names) {
      if (nameItem.module == null) {

        Integer c = nameCounters.get(nameItem.originalName);

        if (c != null) {
          nameItem.number = c;
          nameCounters.put(nameItem.originalName, c + 1);
        }

        do {
          String name = nameItem.getResultName();
          if (existingNames.add(name)) break;

          nameItem.number++;
          nameCounters.put(nameItem.originalName, nameItem.number + 1);
        } while (true);
      }
    }

    for (NameItem each : names) {
      mavenProjectToModuleName.put(each.project, each.getResultName());
    }

    assert new HashSet<String>(mavenProjectToModuleName.values()).size() == mavenProjectToModuleName.size() : new HashMap<MavenProject, String>(mavenProjectToModuleName);
  }

  public static class NameItem implements Comparable<NameItem> {
    public final MavenProject project;
    public final Module module;

    public final String originalName;
    public final String groupId;

    public int number = -1; // has no duplicates
    public boolean hasDuplicatedGroup;

    public NameItem(MavenProject project, @Nullable Module module) {
      this.project = project;
      this.module = module;
      originalName = calcOriginalName();

      String group = project.getMavenId().getGroupId();
      groupId = isValidName(group) ? group : "";
    }

    private String calcOriginalName() {
      if (module != null) return module.getName();

      String name = project.getMavenId().getArtifactId();
      if (!isValidName(name)) name = project.getDirectoryFile().getName();
      return name;
    }

    public String getResultName() {
      if (module != null) return module.getName();

      if (number == -1) return originalName;
      String result = originalName + " (" + (number + 1) + ")";
      if (!hasDuplicatedGroup && groupId.length() != 0) {
        result += " (" + groupId + ")";
      }
      return result;
    }

    @Override
    public int compareTo(NameItem o) {
      return project.getPath().compareToIgnoreCase(o.project.getPath());
    }
  }

  private static boolean isValidName(String name) {
    if (StringUtil.isEmptyOrSpaces(name)) return false;
    if (name.equals(MavenId.UNKNOWN_VALUE)) return false;

    for (int i = 0; i < name.length(); i++) {
      char ch = name.charAt(i);
      if (!(Character.isDigit(ch) || Character.isLetter(ch) || ch == '-' || ch == '_' || ch == '.')) {
        return false;
      }
    }
    return true;
  }

  private static void resolveModulePaths(Collection<MavenProject> projects,
                                         Map<MavenProject, Module> mavenProjectToModule,
                                         Map<MavenProject, String> mavenProjectToModuleName,
                                         Map<MavenProject, String> mavenProjectToModulePath,
                                         String dedicatedModuleDir) {
    for (MavenProject each : projects) {
      Module module = mavenProjectToModule.get(each);
      String path = module != null
                    ? module.getModuleFilePath()
                    : generateModulePath(each,
                                         mavenProjectToModuleName,
                                         dedicatedModuleDir);
      mavenProjectToModulePath.put(each, path);
    }
  }

  private static String generateModulePath(MavenProject project,
                                           Map<MavenProject, String> mavenProjectToModuleName,
                                           String dedicatedModuleDir) {
    String dir = StringUtil.isEmptyOrSpaces(dedicatedModuleDir)
                 ? project.getDirectory()
                 : dedicatedModuleDir;
    String fileName = mavenProjectToModuleName.get(project) + ModuleFileType.DOT_DEFAULT_EXTENSION;
    return new File(dir, fileName).getPath();
  }
}

@@ -2901,24 +2901,26 @@
 ));%0A        
+//
 assert wasAd
@@ -3592,16 +3592,18 @@
  %7D%0A%0A    
+//
 assert n

Remove incorrect assertion