/*
 * Copyright 2017 The Chromium Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
package io.flutter.project;

import com.intellij.execution.ExecutionException;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.projectImport.ProjectOpenProcessor;
import icons.FlutterIcons;
import io.flutter.FlutterBundle;
import io.flutter.FlutterErrors;
import io.flutter.FlutterUtils;
import io.flutter.actions.FlutterPackagesGetAction;
import io.flutter.actions.FlutterSdkAction;
import io.flutter.sdk.FlutterSdk;
import io.flutter.sdk.FlutterSdkUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import java.util.Arrays;
import java.util.Objects;


public class FlutterProjectOpenProcessor extends ProjectOpenProcessor {

  private static void doPerform(@NotNull FlutterSdkAction action, @NotNull Project project) throws ExecutionException {
    final FlutterSdk sdk = FlutterSdk.getFlutterSdk(project);
    if (sdk == null) {
      // Lack of SDK will be flagged elsewhere by inspection.
      return;
    }

    action.perform(sdk, project, null);
  }

  @Override
  public String getName() {
    return FlutterBundle.message("flutter.module.name");
  }

  @Override
  public Icon getIcon() {
    return FlutterIcons.Flutter;
  }

  @Override
  public boolean canOpenProject(@Nullable VirtualFile file) {
    return FlutterSdkUtil.isFlutterProjectDir(file);
  }

  @Nullable
  @Override
  public Project doOpenProject(@NotNull VirtualFile file, @Nullable Project projectToClose, boolean forceOpenInNewFrame) {

    // Delegate opening to the platform open processor.
    final ProjectOpenProcessor importProvider = getDelegateImportProvider(file);
    if (importProvider == null) {
      return null;
    }

    final Project project = importProvider.doOpenProject(file, projectToClose, forceOpenInNewFrame);
    if (project == null) {
      return null;
    }

    // Once open, perform post open processing.
    doPostOpenProcessing(project);

    return project;
  }

  private void doPostOpenProcessing(@NotNull Project project) {
    try {
      final VirtualFile packagesFile = FlutterSdkUtil.findPackagesFileFrom(project, null);
      if (!FlutterUtils.exists(packagesFile)) {
        doPerform(new FlutterPackagesGetAction(), project);
      }
      //TODO(pq): Check if .packages is out of date and offer to run `packages update`.
      //else {
      //
      //}
    }
    catch (ExecutionException e) {
      FlutterErrors.showError("Error opening",
                              e.getMessage());
    }
  }

  @Nullable
  private ProjectOpenProcessor getDelegateImportProvider(@Nullable VirtualFile file) {
    return Arrays.stream(Extensions.getExtensions(EXTENSION_POINT_NAME)).filter(
      processor -> processor.canOpenProject(file) && !Objects.equals(processor.getName(), getName())
    ).findFirst().orElse(null);
  }

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

@@ -240,24 +240,225 @@
 nException;%0A
+import com.intellij.notification.Notification;%0Aimport com.intellij.notification.NotificationListener;%0Aimport com.intellij.notification.NotificationType;%0Aimport com.intellij.notification.Notifications;%0A
 import com.i
@@ -811,32 +811,88 @@
 kagesGetAction;%0A
+import io.flutter.actions.FlutterPackagesUpgradeAction;%0A
 import io.flutte
@@ -1099,16 +1099,57 @@
 wing.*;%0A
+import javax.swing.event.HyperlinkEvent;%0A
 import j
@@ -1591,32 +1591,185 @@
 ct, null);%0A  %7D%0A%0A
+  private static void handleError(@NotNull Exception e) %7B%0A    FlutterErrors.showError(%22Error opening%22,%0A                            e.getMessage());%0A  %7D%0A%0A
   @Override%0A  pu
@@ -2961,122 +2961,306 @@
     
-//TODO(pq): Check if .packages is out of date and offer to run %60packages update%60.%0A      //else %7B
+else %7B%0A        final VirtualFile pubspecFile = FlutterSdkUtil.findPubspecFrom(project, null);%0A        if (FlutterUtils.exists(pubspecFile) && pubspecFile.getTimeStamp() %3E packagesFile.getTimeStamp()) %7B%0A          Notifications.Bus.notify(new PackagesOutOfDateNotification(project));
 %0A      
-//
+  %7D
 %0A      
-//
 %7D%0A  
@@ -3308,93 +3308,21 @@
     
-FlutterErrors.showError(%22Error opening%22,%0A                              e.getMessage()
+handleError(e
 );%0A 
@@ -3728,11 +3728,1104 @@
 rue;%0A  %7D
+%0A%0A  private static class PackagesOutOfDateNotification extends Notification %7B%0A%0A    @NotNull%0A    private final Project myProject;%0A%0A    public PackagesOutOfDateNotification(@NotNull Project project) %7B%0A      super(%22Flutter Packages%22, FlutterIcons.Flutter, %22Package updates.%22,%0A            null, %22This project's packages are ready to%22 +%0A                  %22 %3Ca href=%5C%22%5C%22%3Eupdate%3C/a%3E.%22,%0A            NotificationType.INFORMATION, new NotificationListener() %7B%0A          @Override%0A          public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) %7B%0A            final Project project = ((PackagesOutOfDateNotification)notification).myProject;%0A            final FlutterSdk sdk = FlutterSdk.getFlutterSdk(project);%0A            if (sdk != null) %7B%0A              try %7B%0A                new FlutterPackagesUpgradeAction().perform(sdk, project, null);%0A                notification.expire();%0A              %7D%0A              catch (ExecutionException e) %7B%0A                handleError(e);%0A              %7D%0A            %7D%0A          %7D%0A        %7D);%0A      myProject = project;%0A    %7D%0A  %7D
 %0A%7D%0A

Add check on open for out of date .packages. (#648)