// Copyright (C) 2013 The Android Open Source Project
//
// 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 com.google.gerrit.client.api;

import com.google.gerrit.client.ErrorDialog;
import com.google.gerrit.client.Gerrit;
import com.google.gerrit.client.VoidResult;
import com.google.gerrit.client.rpc.NativeMap;
import com.google.gerrit.client.rpc.Natives;
import com.google.gwt.core.client.Callback;
import com.google.gwt.core.client.CodeDownloadException;
import com.google.gwt.core.client.ScriptInjector;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwtexpui.progress.client.ProgressBar;
import java.util.List;
import java.util.stream.Collectors;

/** Loads JavaScript plugins with a progress meter visible. */
public class PluginLoader extends DialogBox {
  private static PluginLoader self;

  public static void load(
      List<String> plugins, int loadTimeout, AsyncCallback<VoidResult> callback) {
    if (plugins == null || plugins.isEmpty()) {
      callback.onSuccess(VoidResult.create());
    } else {
      plugins = plugins.stream().filter(p -> p.endsWith(".js")).collect(Collectors.toList());
      self = new PluginLoader(loadTimeout, callback);
      self.load(plugins);
      self.startTimers();
      self.center();
    }
  }

  static void loaded() {
    self.loadedOne();
  }

  private final int loadTimeout;
  private final AsyncCallback<VoidResult> callback;
  private ProgressBar progress;
  private Timer show;
  private Timer update;
  private Timer timeout;
  private boolean visible;

  private PluginLoader(int loadTimeout, AsyncCallback<VoidResult> cb) {
    super(/* auto hide */ false, /* modal */ true);
    callback = cb;
    this.loadTimeout = loadTimeout;
    progress = new ProgressBar(Gerrit.C.loadingPlugins());

    setStyleName(Gerrit.RESOURCES.css().errorDialog());
    addStyleName(Gerrit.RESOURCES.css().loadingPluginsDialog());
  }

  private void load(List<String> pluginUrls) {
    for (String url : pluginUrls) {
      Plugin plugin = Plugin.create(url);
      plugins().put(url, plugin);
      ScriptInjector.fromUrl(url)
          .setWindow(ScriptInjector.TOP_WINDOW)
          .setCallback(new LoadCallback(plugin))
          .inject();
    }
  }

  private void startTimers() {
    show =
        new Timer() {
          @Override
          public void run() {
            setText(Window.getTitle());
            setWidget(progress);
            setGlassEnabled(true);
            getGlassElement().addClassName(Gerrit.RESOURCES.css().errorDialogGlass());
            hide(true);
            center();
            visible = true;
          }
        };
    show.schedule(500);

    update =
        new Timer() {
          private int cycle;

          @Override
          public void run() {
            progress.setValue(100 * ++cycle * 250 / loadTimeout);
          }
        };
    update.scheduleRepeating(250);

    timeout =
        new Timer() {
          @Override
          public void run() {
            finish();
          }
        };
    timeout.schedule(loadTimeout);
  }

  private void loadedOne() {
    boolean done = true;
    for (Plugin plugin : Natives.asList(plugins().values())) {
      done &= plugin.loaded();
    }
    if (done) {
      finish();
    }
  }

  private void finish() {
    show.cancel();
    update.cancel();
    timeout.cancel();
    self = null;

    if (!hadFailures()) {
      if (visible) {
        progress.setValue(100);
        new Timer() {
          @Override
          public void run() {
            hide(true);
          }
        }.schedule(250);
      } else {
        hide(true);
      }
    }

    callback.onSuccess(VoidResult.create());
  }

  private boolean hadFailures() {
    boolean failed = false;
    for (Plugin plugin : Natives.asList(plugins().values())) {
      if (!plugin.success()) {
        failed = true;

        Exception e = plugin.failure();
        String msg;
        if (e != null && e instanceof CodeDownloadException) {
          msg = Gerrit.M.cannotDownloadPlugin(plugin.url());
        } else {
          msg = Gerrit.M.pluginFailed(plugin.name());
        }
        hide(true);
        new ErrorDialog(msg).center();
      }
    }
    return failed;
  }

  private static native NativeMap<Plugin> plugins() /*-{ return $wnd.Gerrit.plugins }-*/;

  private class LoadCallback implements Callback<Void, Exception> {
    private final Plugin plugin;

    LoadCallback(Plugin plugin) {
      this.plugin = plugin;
    }

    @Override
    public void onSuccess(Void result) {}

    @Override
    public void onFailure(Exception reason) {
      plugin.failure(reason);
      loadedOne();
    }
  }
}

@@ -1767,24 +1767,121 @@
 .toList());%0A
+      if (plugins.isEmpty()) %7B%0A        callback.onSuccess(VoidResult.create());%0A      %7D else %7B%0A  
       self =
@@ -1924,24 +1924,26 @@
 ack);%0A      
+ 
+ 
 self.load(pl
@@ -1946,24 +1946,26 @@
 d(plugins);%0A
+  
       self.s
@@ -1980,24 +1980,26 @@
 rs();%0A      
+ 
+ 
 self.center(
@@ -1997,24 +1997,32 @@
 f.center();%0A
+      %7D%0A
     %7D%0A  %7D%0A%0A 

check if plugin list is empty after filter