package jetbrains.mps.workbench.dialogs.project.properties.project;

/*Generated by MPS */

import jetbrains.mps.annotations.GeneratedClass;
import com.intellij.ui.components.JBPanel;
import org.jetbrains.mps.openapi.ui.Modifiable;
import jetbrains.mps.project.MPSProject;
import com.intellij.ui.components.JBList;
import jetbrains.mps.vfs.IFile;
import java.util.List;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
import jetbrains.mps.ide.project.ProjectHelper;
import com.intellij.uiDesigner.core.GridConstraints;
import javax.swing.JComponent;
import javax.swing.ListSelectionModel;
import com.intellij.ui.TreeUIHelper;
import java.util.function.Consumer;
import com.intellij.openapi.vfs.VirtualFile;
import jetbrains.mps.ide.vfs.FileSystemBridge;
import java.util.stream.IntStream;
import java.util.Objects;
import com.intellij.openapi.util.Condition;
import java.util.Set;
import java.util.HashSet;
import java.util.Arrays;
import jetbrains.mps.project.MPSExtentions;
import com.intellij.ui.AnActionButton;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.fileChooser.FileChooser;
import com.intellij.openapi.project.ProjectUtil;
import java.util.ArrayList;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFileVisitor;
import com.intellij.ui.ToolbarDecorator;
import com.intellij.ui.AnActionButtonRunnable;
import java.awt.Dimension;
import javax.swing.JPanel;
import com.intellij.ui.IdeBorderFactory;
import com.intellij.uiDesigner.core.GridLayoutManager;
import jetbrains.mps.internal.collections.runtime.ListSequence;

@GeneratedClass(node = "r:74729267-a5fb-4229-a117-335c34e68536(jetbrains.mps.workbench.dialogs.project.properties.project)/3201642974933583134", model = "r:74729267-a5fb-4229-a117-335c34e68536(jetbrains.mps.workbench.dialogs.project.properties.project)")
public class ProjectPropertiesComponent extends JBPanel implements Modifiable {
  private final MPSProject myProject;
  private final ProjectFilesModel myModuleFiles = new ProjectFilesModel();

  private JBList<IFile> myModulesList;
  private final List<ProjectPrefsExtraPanel> myExtraPanels;

  public ProjectPropertiesComponent(Project ideaProject, @NotNull List<ProjectPrefsExtraPanel> extraPanels) {
    super(true);
    myProject = ProjectHelper.fromIdeaProject(ideaProject);
    myExtraPanels = extraPanels;
    myModuleFiles.loadFrom(myProject);
    init();
  }

  public ProjectPropertiesComponent(Project project) {
    this(project, ProjectPrefsExtraPanel.EP_NAME.getExtensionList(project));
  }

  private static Object getGridConstraints(int row, boolean fill) {
    if (fill) {
      return new GridConstraints(row, 0, 1, 1, GridConstraints.ANCHOR_NORTHWEST, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_GROW | GridConstraints.SIZEPOLICY_CAN_SHRINK, GridConstraints.SIZEPOLICY_CAN_GROW | GridConstraints.SIZEPOLICY_CAN_SHRINK, null, null, null);
    }
    return new GridConstraints(row, 0, 1, 1, GridConstraints.ANCHOR_NORTHWEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_GROW | GridConstraints.SIZEPOLICY_CAN_SHRINK, GridConstraints.SIZEPOLICY_FIXED, null, null, null);
  }

  private JComponent createProjectModulesList() {
    myModulesList = new JBList<>(myModuleFiles);

    myModulesList.setCellRenderer(new PathRenderer());
    myModulesList.setEmptyText("No modules");
    myModulesList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);

    TreeUIHelper.getInstance().installListSpeedSearch(myModulesList, IFile::getPath);

    final Consumer<List<VirtualFile>> filesToModulePathsProcessor = new Consumer<>() {
      /**
       * Process list of files:<br>
       * <ul>
       * <li>Sort input files by path (to add elements in correct order)</li>
       * <li>Filter out files that are already included in project</li>
       * <li>Add rest of the files to the project as module paths</li>
       * <li>Select chosen elements (only new ones) in UI</li>
       * </ul>
       * 
       * @param files to be added to the project as MPS modules
       */
      @Override
      public void accept(List<VirtualFile> files) {
        if (files == null || files.isEmpty()) {
          return;
        }

        FileSystemBridge fsBridge = myProject.getFileSystem();

        IntStream added = myModuleFiles.add(files.stream().map(fsBridge::fromVirtualFile).dropWhile(Objects::isNull));
        myModulesList.setSelectedIndices(added.toArray());
      }
    };

    // Check that file has one of the possible MPS module extensions
    final Condition<VirtualFile> isModuleFile = new Condition<VirtualFile>() {
      private final Set<String> EXTENTIONS = new HashSet<String>(Arrays.asList(MPSExtentions.LANGUAGE, MPSExtentions.SOLUTION, MPSExtentions.DEVKIT, MPSExtentions.GENERATOR));

      public boolean value(VirtualFile file) {
        String fileExtension = file.getExtension();
        return fileExtension != null && !(file.isDirectory()) && EXTENTIONS.contains(fileExtension.toLowerCase());
      }
    };

    AnActionButton importFolder = new AnActionButton("Import", "Add all modules from selected folder", AllIcons.ToolbarDecorator.Import) {
      @Override
      public void actionPerformed(@NotNull AnActionEvent event) {
        FileChooserDescriptor createSingleFolderDescriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor();
        createSingleFolderDescriptor.setDescription("Search for module files (depth is limited to five levels)");
        VirtualFile folder = FileChooser.chooseFile(createSingleFolderDescriptor, myModulesList, myProject.getProject(), ProjectUtil.guessProjectDir(myProject.getProject()));
        if (folder == null) {
          return;
        }

        final List<VirtualFile> moduleFiles = new ArrayList<VirtualFile>();
        VfsUtilCore.visitChildrenRecursively(folder, new VirtualFileVisitor<Void>(VirtualFileVisitor.NO_FOLLOW_SYMLINKS, VirtualFileVisitor.limit(5)) {
          @Override
          public boolean visitFile(@NotNull VirtualFile file) {
            if (isModuleFile.value(file)) {
              moduleFiles.add(file);
            }
            return super.visitFile(file);
          }
        });

        filesToModulePathsProcessor.accept(moduleFiles);
      }
    };

    ToolbarDecorator decorator = ToolbarDecorator.createDecorator(myModulesList);
    decorator.setAddAction(new AnActionButtonRunnable() {
      @Override
      public void run(AnActionButton button) {
        FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createMultipleFilesNoJarsDescriptor().withFileFilter(isModuleFile);

        // Do not use method with consumer, because it uses com.intellij.util.Consumer instead of java.util.function.Consumer
        VirtualFile[] files = FileChooser.chooseFiles(descriptor, myModulesList, myProject.getProject(), ProjectUtil.guessProjectDir(myProject.getProject()));

        filesToModulePathsProcessor.accept(Arrays.asList(files));
      }
    }).setRemoveAction(new AnActionButtonRunnable() {
      @Override
      public void run(AnActionButton button) {
        int[] selectedIndices = myModulesList.getSelectedIndices();
        int minSelectedIndex = myModulesList.getMinSelectionIndex();
        myModuleFiles.remove(selectedIndices);
        if (myModulesList.getItemsCount() > 0) {
          myModulesList.setSelectedIndex((minSelectedIndex < myModulesList.getItemsCount() ? minSelectedIndex : myModulesList.getItemsCount() - 1));
        }
      }
    }).addExtraAction(importFolder).disableUpDownActions();
    decorator.setPreferredSize(new Dimension(500, 150));

    JPanel panel = decorator.createPanel();
    panel.setBorder(IdeBorderFactory.createTitledBorder("Modules", false));
    return panel;
  }

  /*package*/ JComponent getPreferredFocusedComponent() {
    return myModulesList;
  }


  @Override
  public void init() {
    int rowCount = 1 + myExtraPanels.size();
    int rowIndex = 0;
    this.setLayout(new GridLayoutManager(rowCount, 1));
    this.setAutoscrolls(false);

    this.add(createProjectModulesList(), getGridConstraints(rowIndex++, true));
    for (ProjectPrefsExtraPanel extraPanel : ListSequence.fromList(myExtraPanels)) {
      this.add(extraPanel.getComponent(), getGridConstraints(rowIndex++, false));
    }
  }

  @Override
  public boolean isModified() {
    return !(myModuleFiles.isSame(myProject)) || myExtraPanels.stream().anyMatch(ProjectPrefsExtraPanel::isModified);
  }

  @Override
  public void apply() {
    myProject.getModelAccess().runWriteAction(new Runnable() {
      @Override
      public void run() {
        myModuleFiles.saveTo(myProject);
      }
    });
    for (ProjectPrefsExtraPanel ep : ListSequence.fromList(myExtraPanels)) {
      ep.apply();
    }
  }

  public void reset() {
    myModuleFiles.loadFrom(myProject);
    for (ProjectPrefsExtraPanel ep : ListSequence.fromList(myExtraPanels)) {
      ep.reset();
    }
  }
}
