package jetbrains.mps.ide.modelchecker.platform.actions;

/*Generated by MPS */

import jetbrains.mps.annotations.GeneratedClass;
import javax.swing.JPanel;
import com.intellij.openapi.project.Project;
import jetbrains.mps.ide.findusages.view.UsagesView;
import javax.swing.JButton;
import jetbrains.mps.ide.project.ProjectHelper;
import java.awt.BorderLayout;
import jetbrains.mps.ide.findusages.view.treeholder.treeview.ViewOptions;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.icons.AllIcons;
import org.jetbrains.annotations.NotNull;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.IdeActions;
import java.awt.FlowLayout;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import jetbrains.mps.baseLanguage.closures.runtime.Wrappers;
import java.util.List;
import jetbrains.mps.errors.item.IssueKindReportItem;
import jetbrains.mps.smodel.ModelAccessHelper;
import jetbrains.mps.internal.collections.runtime.ListSequence;
import com.intellij.openapi.ui.Messages;
import java.util.ArrayList;
import jetbrains.mps.errors.item.QuickFixBase;
import jetbrains.mps.errors.item.QuickFixReportItem;
import java.util.Set;
import org.jetbrains.mps.openapi.model.SNodeReference;
import jetbrains.mps.internal.collections.runtime.SetSequence;
import java.util.HashSet;
import org.jetbrains.mps.openapi.model.SModel;
import jetbrains.mps.ide.findusages.model.SearchResult;
import jetbrains.mps.internal.collections.runtime.IWhereFilter;
import jetbrains.mps.errors.item.NodeFlavouredItem;
import jetbrains.mps.errors.item.ModelFlavouredItem;
import jetbrains.mps.internal.collections.runtime.ISelector;
import org.jetbrains.mps.openapi.module.SModule;
import jetbrains.mps.ide.findusages.model.IResultProvider;
import jetbrains.mps.ide.findusages.model.SearchQuery;
import jetbrains.mps.ide.findusages.model.SearchTask;
import com.intellij.openapi.actionSystem.ActionPlaces;
import org.jetbrains.annotations.Nullable;
import jetbrains.mps.ide.findusages.model.SearchResults;
import jetbrains.mps.ide.findusages.view.treeholder.treeview.NodeRepresentatorBase;
import jetbrains.mps.ide.findusages.view.treeholder.tree.TextOptions;
import jetbrains.mps.util.NameUtil;
import javax.swing.Icon;
import jetbrains.mps.ide.icons.IdeIcons;
import jetbrains.mps.ide.messages.Icons;
import jetbrains.mps.ide.findusages.model.CategoryKind;
import java.util.Arrays;
import org.jdom.Element;
import jetbrains.mps.ide.findusages.CantSaveSomethingException;
import jetbrains.mps.ide.findusages.CantLoadSomethingException;

@GeneratedClass(node = "r:5754bb7d-f802-4a0f-bd3d-0764f0d71413(jetbrains.mps.ide.modelchecker.platform.actions)/3719390199795365573", model = "r:5754bb7d-f802-4a0f-bd3d-0764f0d71413(jetbrains.mps.ide.modelchecker.platform.actions)")
public class ModelCheckerViewer extends JPanel {
  private final Project myIdeaProject;
  private final jetbrains.mps.project.Project myProject;
  private UsagesView myUsagesView;
  private JButton myFixButton;
  private UsagesView.RerunAction myCheckAction;
  public ModelCheckerViewer(Project project) {
    this(project, true);
  }

  public ModelCheckerViewer(Project project, boolean canFix) {
    myIdeaProject = project;
    myProject = ProjectHelper.toMPSProject(project);

    setLayout(new BorderLayout());
    ViewOptions viewOptions = new ViewOptions(true, false, false, false, false);
    viewOptions.myCategories = new boolean[]{true, false};

    myUsagesView = new UsagesView(project, viewOptions);
    myCheckAction = new UsagesView.RerunAction(myUsagesView, "Check again");
    myUsagesView.setActions(myCheckAction, new UsagesView.RebuildAction(myUsagesView), new AnAction("Close", "", AllIcons.Actions.Cancel) {
      public void actionPerformed(@NotNull AnActionEvent p0) {
        ModelCheckerViewer.this.close();
      }
    }, ActionManager.getInstance().getAction(IdeActions.ACTION_PIN_ACTIVE_TAB));
    myUsagesView.setCustomNodeRepresentator(new MyNodeRepresentator());
    add(myUsagesView.getComponent(), BorderLayout.CENTER);

    JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
    // XXX fix button might be an action along with others above (i.e. button in the left pane)
    if (canFix) {
      myFixButton = new JButton("Perform Quick Fixes");
      myFixButton.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent event) {
          performQuickFixes();
        }
      });
      buttonPanel.add(myFixButton);
    }
    add(buttonPanel, BorderLayout.SOUTH);
  }
  protected void close() {
    // no-op, override to react on view close action
  }
  public void performQuickFixes() {
    // Ask if need to fix

    // Perform quick fixes
    final Wrappers._int fixedTotal = new Wrappers._int(0);
    // Select all fixable issues
    final List<IssueKindReportItem> issuesToFix = new ModelAccessHelper(myProject.getModelAccess()).runReadAction(() -> getIssuesToFix());
    if (ListSequence.fromList(issuesToFix).isEmpty()) {
      Messages.showInfoMessage("There are no quick fixes for current problems", "No Quick Fixes");
      return;
    }
    int dialogAnswer = Messages.showYesNoDialog(myIdeaProject, "You are going to remove undeclared properties, children from nodes and resolve references. " + "You may not be able to undo it. Are you sure?", "Warning", null);
    if (dialogAnswer != 0) {
      return;
    }

    myProject.getModelAccess().executeCommandInEDT(() -> {
      while (true) {
        int fixedBefore = fixedTotal.value;
        for (IssueKindReportItem issue : ListSequence.fromListWithValues(new ArrayList<IssueKindReportItem>(), issuesToFix)) {
          QuickFixBase quickFix = QuickFixReportItem.FLAVOUR_QUICKFIX.getAutoApplicable(issue);
          boolean alive = IssueKindReportItem.PATH_OBJECT.get(issue).resolve(myProject.getRepository()) != null && quickFix.isAlive(myProject.getRepository());
          if (alive) {
            quickFix.execute(myProject.getRepository());
            fixedTotal.value++;
            ListSequence.fromList(issuesToFix).removeElement(issue);
          }
        }
        if (fixedBefore == fixedTotal.value) {
          break;
        }
      }
    });

    // Perform recheck if needed
    if (fixedTotal.value != 0) {
      int dialogAnswer1 = Messages.showYesNoDialog(myIdeaProject, "Model checker fixed " + fixedTotal.value + " issues. Do you wish to recheck?", "Recheck", null);
      if (dialogAnswer1 != 0) {
        return;
      }

      doReCheck();
    }
  }
  private List<IssueKindReportItem> getIssuesToFix() {
    final Set<SNodeReference> includedResultNodes = SetSequence.fromSetWithValues(new HashSet<SNodeReference>(), myUsagesView.getIncludedResultNodes());
    final Set<SModel> includedResultModels = SetSequence.fromSetWithValues(new HashSet<SModel>(), myUsagesView.getIncludedModels());
    List<SearchResult<IssueKindReportItem>> searchResults = getSearchResults().getSearchResults();
    return ListSequence.fromList(searchResults).where(new IWhereFilter<SearchResult<IssueKindReportItem>>() {
      public boolean accept(SearchResult<IssueKindReportItem> sr) {
        IssueKindReportItem reportItem = sr.getObject();
        QuickFixBase quickFix = QuickFixReportItem.FLAVOUR_QUICKFIX.getAutoApplicable(reportItem);
        boolean fixable = quickFix != null;
        boolean isNodeIssueAndFixable = NodeFlavouredItem.FLAVOUR_NODE.canGet(reportItem) && SetSequence.fromSet(includedResultNodes).contains(NodeFlavouredItem.FLAVOUR_NODE.tryToGet(reportItem)) && fixable;
        boolean isModelIssueAndFixable = ModelFlavouredItem.FLAVOUR_MODEL.canGet(reportItem) && SetSequence.fromSet(includedResultModels).contains((SModel) sr.getPathObject()) && fixable;
        return isNodeIssueAndFixable || isModelIssueAndFixable;
      }
    }).select(new ISelector<SearchResult<IssueKindReportItem>, IssueKindReportItem>() {
      public IssueKindReportItem select(SearchResult<IssueKindReportItem> it) {
        return it.getObject();
      }
    }).toListSequence();

  }
  /*package*/ void checkModules(List<SModule> modules, String taskTargetTitle) {
    ModelCheckerIssueFinder f = newModelChecker();
    f.addModuleScope(modules);
    runCheck(f, taskTargetTitle);
  }
  /*package*/ void checkModels(List<SModel> models, String taskTargetTitle) {
    ModelCheckerIssueFinder f = newModelChecker();
    f.addModelScope(models);
    runCheck(f, taskTargetTitle);
  }
  /*package*/ void runCheck(IResultProvider resultProvider, SearchQuery searchQuery, String taskTargetTitle) {
    myCheckAction.setProgressText(String.format("Checking %s", taskTargetTitle));
    myCheckAction.setRunOptions(resultProvider, searchQuery);
    doReCheck();
  }
  /*package*/ void runCheck(SearchTask searchTask, String taskTargetTitle) {
    myCheckAction.setProgressText(String.format("Checking %s", taskTargetTitle));
    myCheckAction.setRunOptions(searchTask);
    doReCheck();
  }

  private void doReCheck() {
    myCheckAction.actionPerformed(AnActionEvent.createFromInputEvent(myCheckAction, null, ActionPlaces.UNKNOWN));
  }
  public void dispose() {
    myUsagesView.dispose();
  }
  @Nullable
  public SearchResults<IssueKindReportItem> getSearchResults() {
    return myUsagesView.getSearchResults();
  }
  public void setSearchResults(SearchResults<IssueKindReportItem> issues) {
    myUsagesView.setContents(issues);
  }
  private ModelCheckerIssueFinder newModelChecker() {
    return new ModelCheckerIssueFinder(myProject.getRepository(), ModelCheckerSettings.getInstance().getSpecificCheckers(myProject));
  }
  public static class MyNodeRepresentator extends NodeRepresentatorBase<IssueKindReportItem> {
    public MyNodeRepresentator() {
    }
    @Override
    public String getResultsText(TextOptions options) {
      int size = options.mySubresultsCount;
      return NameUtil.formatNumericalString(size, "issue") + " found";
    }
    @Override
    public Icon getResultsIcon() {
      return IdeIcons.CLOSED_FOLDER;
    }
    @Override
    public String getCategoryText(TextOptions options, String category, boolean isResultsSection) {
      String counter = "";
      if (options.myCounters && isResultsSection) {
        counter = " (" + NameUtil.formatNumericalString(options.mySubresultsCount, "issue") + ")";
      }
      return category + counter;
    }
    @Override
    public Icon getCategoryIcon(String category) {
      if ((category != null && category.length() > 0)) {
        if (category.startsWith(ModelCheckerIssueFinder.SEVERITY_ERROR)) {
          return Icons.ERROR_ICON;
        } else if (category.startsWith(ModelCheckerIssueFinder.SEVERITY_WARNING)) {
          return Icons.WARNING_ICON;
        } else if (category.startsWith(ModelCheckerIssueFinder.SEVERITY_INFO)) {
          return Icons.INFORMATION_ICON;
        }
      }
      return IdeIcons.CLOSED_FOLDER;
    }
    @NotNull
    @Override
    public String getPresentation(IssueKindReportItem issue) {
      return issue.getMessage();
    }
    @Override
    public List<CategoryKind> getCategoryKinds() {
      return Arrays.asList(ModelCheckerIssueFinder.CATEGORY_KIND_SEVERITY, ModelCheckerIssueFinder.CATEGORY_KIND_ISSUE_TYPE);
    }
    @Override
    public void write(Element element, jetbrains.mps.project.Project project) throws CantSaveSomethingException {
    }
    @Override
    public void read(Element element, jetbrains.mps.project.Project project) throws CantLoadSomethingException {
    }
  }
}
