/*
 * Copyright 2003-2016 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 jetbrains.mps.idea.core.make;

import com.intellij.facet.FacetManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.pom.Navigatable;
import jetbrains.mps.ide.project.ProjectHelper;
import jetbrains.mps.idea.core.facet.MPSFacet;
import jetbrains.mps.idea.core.facet.MPSFacetType;
import jetbrains.mps.openapi.navigation.NavigationSupport;
import jetbrains.mps.smodel.ModuleRepositoryFacade;
import org.jetbrains.mps.openapi.model.SModel;
import org.jetbrains.mps.openapi.model.SNode;
import org.jetbrains.mps.openapi.module.SRepository;
import org.jetbrains.mps.openapi.persistence.PersistenceFacade;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ModelNodeNavigatable implements Navigatable {

  private final String modelName;
  private final String nodeId;
  private final Project project;
  private final Module module;

  public ModelNodeNavigatable(String modelName, String nodeId, Project project, Module module) {
    this.modelName = modelName;
    this.nodeId = nodeId;
    this.project = project;
    this.module = module;
  }

  @Override
  public void navigate(final boolean requestFocus) {
    final jetbrains.mps.project.Project mpsProject = ProjectHelper.fromIdeaProject(project);
    mpsProject.getModelAccess().runWriteInEDT(() -> {
      SModel model = lookupModel();
      if (model == null) return;
      SNode node = model.getNode(PersistenceFacade.getInstance().createNodeId(nodeId));
      if (node != null) {
        Project prj = project;
        if (prj == null) {
          prj = module.getProject();
        }
        NavigationSupport.getInstance().openNode(mpsProject, node, requestFocus, true);
      }
    });
  }

  /**
   * Requires read action.
   *
   * @return model by name or {@code null}
   */
  public SModel lookupModel() {
    SRepository repository = ProjectHelper.getProjectRepository(project);
    assert repository.getModelAccess().canRead();
    SModel model = null;
    if (module != null) {
      MPSFacet facet = FacetManager.getInstance(module).getFacetByType(MPSFacetType.ID);
      for (SModel smd : facet.getSolution().getModels()) {
        if (smd.getName().getLongName().equals(modelName)) {
          model = smd;
        }
      }
    } else {
      model = new ModuleRepositoryFacade(repository).getModelByName(modelName);
    }
    return model;
  }

  @Override
  public boolean canNavigate() {
    return true;
  }

  @Override
  public boolean canNavigateToSource() {
    return true;
  }

  private static final Pattern TRANS_MODEL = Pattern.compile("\\[(\\d+)\\].*\\s([a-zA-Z_][a-zA-Z_0-9.]*)@(\\d+_\\d+)");
  private static final Pattern SOURCE_MODEL = Pattern.compile("\\[(\\d+)\\].*\\sin\\s+([a-zA-Z_][a-zA-Z_0-9.]*)");


  public static ModelNodeNavigatable extractNavigatable(String errorMsg, Project project, Module module) {
    if (errorMsg == null) return null;

    Matcher matcher = TRANS_MODEL.matcher(errorMsg);
    if (!matcher.find()) {
      matcher = SOURCE_MODEL.matcher(errorMsg);
      if (!matcher.find()) return null;
    }

    String nodeId = matcher.group(1);
    String modelName = matcher.group(2);
    return new ModelNodeNavigatable(modelName, nodeId, project, module);
  }

}
