/*
 * Copyright 2000-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.
 */

/*
 * User: anna
 * Date: 09-Jan-2007
 */
package com.intellij.codeInspection.actions;

import com.intellij.analysis.AnalysisScope;
import com.intellij.analysis.PerformAnalysisInBackgroundOption;
import com.intellij.codeHighlighting.HighlightDisplayLevel;
import com.intellij.codeInsight.daemon.HighlightDisplayKey;
import com.intellij.codeInspection.InspectionApplication;
import com.intellij.codeInspection.InspectionManager;
import com.intellij.codeInspection.InspectionsBundle;
import com.intellij.codeInspection.ex.GlobalInspectionContextImpl;
import com.intellij.codeInspection.ex.InspectionManagerEx;
import com.intellij.codeInspection.ex.InspectionProfileImpl;
import com.intellij.codeInspection.offline.OfflineProblemDescriptor;
import com.intellij.codeInspection.offlineViewer.OfflineInspectionRVContentProvider;
import com.intellij.codeInspection.offlineViewer.OfflineViewParseUtil;
import com.intellij.codeInspection.reference.RefManagerImpl;
import com.intellij.codeInspection.ui.InspectionResultsView;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileChooser.FileChooser;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.fileEditor.impl.LoadTextUtil;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.profile.codeInspection.InspectionProfileManager;
import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
import com.intellij.psi.PsiElement;
import com.intellij.util.PlatformUtils;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class ViewOfflineResultsAction extends AnAction {
  private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.actions.ViewOfflineResultsAction");
  @NonNls private static final String XML_EXTENSION = "xml";

  @Override
  public void update(AnActionEvent event) {
    final Presentation presentation = event.getPresentation();
    final Project project = event.getData(CommonDataKeys.PROJECT);
    presentation.setEnabled(project != null);
    presentation.setVisible(ActionPlaces.isMainMenuOrActionSearch(event.getPlace()) && !PlatformUtils.isCidr());
  }

  @Override
  public void actionPerformed(AnActionEvent event) {
    final Project project = event.getData(CommonDataKeys.PROJECT);

    LOG.assertTrue(project != null);

    final FileChooserDescriptor descriptor = new FileChooserDescriptor(false, true, false, false, false, false){
      @Override
      public Icon getIcon(VirtualFile file) {
        if (file.isDirectory()) {
          if (file.findChild(InspectionApplication.DESCRIPTIONS + "." + StdFileTypes.XML.getDefaultExtension()) != null) {
            return AllIcons.Nodes.InspectionResults;
          }
        }
        return super.getIcon(file);
      }
    };
    descriptor.setTitle("Select Path");
    descriptor.setDescription("Select directory which contains exported inspections results");
    final VirtualFile virtualFile = FileChooser.chooseFile(descriptor, project, null);
    if (virtualFile == null || !virtualFile.isDirectory()) return;

    final Map<String, Map<String, Set<OfflineProblemDescriptor>>> resMap =
      new HashMap<>();
    final String [] profileName = new String[1];
    final Runnable process = () -> {
      final VirtualFile[] files = virtualFile.getChildren();
      try {
        for (final VirtualFile inspectionFile : files) {
          if (inspectionFile.isDirectory()) continue;
          final String shortName = inspectionFile.getNameWithoutExtension();
          final String extension = inspectionFile.getExtension();
          if (shortName.equals(InspectionApplication.DESCRIPTIONS)) {
            profileName[0] = ReadAction.compute(() -> OfflineViewParseUtil.parseProfileName(LoadTextUtil.loadText(inspectionFile).toString()));
          }
          else if (XML_EXTENSION.equals(extension)) {
            resMap.put(shortName, ApplicationManager.getApplication().runReadAction(
                new Computable<Map<String, Set<OfflineProblemDescriptor>>>() {
                  @Override
                  public Map<String, Set<OfflineProblemDescriptor>> compute() {
                    return OfflineViewParseUtil.parse(LoadTextUtil.loadText(inspectionFile).toString());
                  }
                }
            ));
          }
        }
      }
      catch (final Exception e) {  //all parse exceptions
        SwingUtilities.invokeLater(
          () -> Messages.showInfoMessage(e.getMessage(), InspectionsBundle.message("offline.view.parse.exception.title")));
        throw new ProcessCanceledException(); //cancel process
      }
    };
    ProgressManager.getInstance().runProcessWithProgressAsynchronously(project, InspectionsBundle.message("parsing.inspections.dump.progress.title"), process,
                                                                       () -> SwingUtilities.invokeLater(() -> {
                                                                         final String name = profileName[0];
                                                                         showOfflineView(project, name, resMap,
                                                                                         InspectionsBundle.message("offline.view.title") +
                                                                                         " (" + (name != null ? name : InspectionsBundle.message("offline.view.editor.settings.title")) +")");
                                                                       }), null, new PerformAnalysisInBackgroundOption(project));
  }

  @SuppressWarnings({"WeakerAccess", "UnusedReturnValue"}) //used in TeamCity
  public static InspectionResultsView showOfflineView(@NotNull Project project,
                                                      @Nullable
                                                      final String profileName,
                                                      @NotNull final Map<String, Map<String, Set<OfflineProblemDescriptor>>> resMap,
                                                      @NotNull String title) {
    InspectionProfileImpl profile;
    if (profileName != null) {
      profile = InspectionProjectProfileManager.getInstance(project).getProfile(profileName, false);
      if (profile == null) {
        profile = InspectionProfileManager.getInstance().getProfile(profileName, false);
      }
    }
    else {
      profile = null;
    }
    final InspectionProfileImpl inspectionProfile;
    if (profile != null) {
      inspectionProfile = profile;
    }
    else {
      inspectionProfile = new InspectionProfileImpl(profileName != null ? profileName : "Server Side") {
        @Override
        public HighlightDisplayLevel getErrorLevel(@NotNull final HighlightDisplayKey key, PsiElement element) {
          return InspectionProfileManager.getInstance().getCurrentProfile().getErrorLevel(key, element);
        }
      };
      for (String id : resMap.keySet()) {
        inspectionProfile.enableTool(id, project);
      }
    }
    return showOfflineView(project, resMap, inspectionProfile, title);
  }

  @NotNull
  public static InspectionResultsView showOfflineView(@NotNull Project project,
                                                      @NotNull Map<String, Map<String, Set<OfflineProblemDescriptor>>> resMap,
                                                      @NotNull InspectionProfileImpl inspectionProfile,
                                                      @NotNull String title) {
    final AnalysisScope scope = new AnalysisScope(project);
    final InspectionManagerEx managerEx = (InspectionManagerEx)InspectionManager.getInstance(project);
    final GlobalInspectionContextImpl context = managerEx.createNewGlobalContext(false);
    context.setExternalProfile(inspectionProfile);
    context.setCurrentScope(scope);
    context.initializeTools(new ArrayList<>(), new ArrayList<>(), new ArrayList<>());
    final InspectionResultsView view = new InspectionResultsView(context,
                                                                 new OfflineInspectionRVContentProvider(resMap, project));
    ((RefManagerImpl)context.getRefManager()).startOfflineView();
    context.addView(view, title, true);
    view.update();
    return view;
  }
}
