/*
 * DependencyManager.java
 *
 * Copyright (C) 2009-12 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

package org.rstudio.studio.client.common.dependencies;

import java.util.ArrayList;

import org.rstudio.core.client.CommandWithArg;
import org.rstudio.core.client.StringUtil;
import org.rstudio.core.client.widget.MessageDialog;
import org.rstudio.core.client.widget.Operation;
import org.rstudio.core.client.widget.ProgressIndicator;
import org.rstudio.studio.client.application.events.EventBus;
import org.rstudio.studio.client.common.GlobalDisplay;
import org.rstudio.studio.client.common.GlobalProgressDelayer;
import org.rstudio.studio.client.common.SimpleRequestCallback;
import org.rstudio.studio.client.common.console.ConsoleProcess;
import org.rstudio.studio.client.common.console.ProcessExitEvent;
import org.rstudio.studio.client.common.dependencies.events.InstallShinyEvent;
import org.rstudio.studio.client.common.dependencies.model.Dependency;
import org.rstudio.studio.client.common.dependencies.model.DependencyServerOperations;
import org.rstudio.studio.client.server.ServerError;
import org.rstudio.studio.client.server.ServerRequestCallback;
import org.rstudio.studio.client.workbench.views.vcs.common.ConsoleProgressDialog;

import com.google.gwt.core.client.JsArray;
import com.google.gwt.user.client.Command;
import com.google.inject.Inject;
import com.google.inject.Singleton;

@Singleton
public class DependencyManager implements InstallShinyEvent.Handler
{
   @Inject
   public DependencyManager(GlobalDisplay globalDisplay,
                            DependencyServerOperations server,
                            EventBus eventBus)
   {
      globalDisplay_ = globalDisplay;
      server_ = server;
      
      eventBus.addHandler(InstallShinyEvent.TYPE, this);
   }
   
   public void withDependencies(String progressCaption,
                                CommandWithArg<Command> userPrompt,
                                Dependency[] dependencies, 
                                Command command)
   {
      withDependencies(progressCaption,
                       null,
                       userPrompt,
                       dependencies,
                       command);
   }
   
   public void withDependencies(String progressCaption,
                                String userAction,
                                Dependency[] dependencies, 
                                final Command command)
   {
      withDependencies(progressCaption, 
                       userAction, 
                       null, 
                       dependencies, 
                       command);
   }
   
 
   public void withShiny(final String userAction, final Command command)
   {
      // create user prompt command
      CommandWithArg<Command> userPrompt = new CommandWithArg<Command>() {
         @Override
         public void execute(final Command yesCommand)
         {
            globalDisplay_.showYesNoMessage(
              MessageDialog.QUESTION,
              "Install Shiny Package", 
              userAction + " requires installation of the development " +
              "version of Shiny.\n\nDo you want to install this package now?",
                  new Operation() {

                     @Override
                     public void execute()
                     {
                        yesCommand.execute();
                     }
                  },
                  true);
          }
       };
       
       // perform dependency resolution 
       withDependencies(
          "Checking installed packages",
          userPrompt,
          new Dependency[] {
            Dependency.cranPackage("httpuv", "1.2"),
            Dependency.cranPackage("caTools", "1.13"),
            Dependency.cranPackage("RJSONIO", "1.0"),
            Dependency.cranPackage("xtable", "1.7"),
            Dependency.cranPackage("digest", "0.6"),
            Dependency.cranPackage("htmltools", "0.2.4"),
            Dependency.embeddedPackage("shiny")
          }, 
          command
       ); 
   }
   
   @Override
   public void onInstallShiny(InstallShinyEvent event)
   {
      withShiny(event.getUserAction(), 
                new Command() { public void execute() {}});
   }
   
   private void withDependencies(String progressCaption,
                                 final String userAction,
                                 final CommandWithArg<Command> userPrompt,
                                 Dependency[] dependencies, 
                                 final Command command)
   {
      // convert dependencies to JsArray
      JsArray<Dependency> deps = JsArray.createArray().cast();
      deps.setLength(dependencies.length);
      for (int i = 0; i<deps.length(); i++)
         deps.set(i, dependencies[i]);
      
      // create progress indicator
      final ProgressIndicator progress = new GlobalProgressDelayer(
            globalDisplay_,
            250,
            progressCaption + "...").getIndicator();
      
      // query for unsatisfied dependencies
      server_.unsatisfiedDependencies(
            deps, new ServerRequestCallback<JsArray<Dependency>>() {

         @Override
         public void onResponseReceived(
                              final JsArray<Dependency> unsatisfiedDeps)
         {
            progress.onCompleted();
            
            // if we've satisfied all dependencies then execute the command
            if (unsatisfiedDeps.length() == 0)
            {
               command.execute();
            }
            
            // otherwise ask the user if they want to install the 
            // unsatisifed dependencies
            else
            {
               Command installCommand = new Command() {
                  @Override
                  public void execute()
                  {
                     installDependencies(unsatisfiedDeps, command);
                  }
               };
               
               if (userPrompt != null)
               {
                  userPrompt.execute(installCommand);
               }
               else
               {
                  confirmPackageInstallation(userAction, 
                                             unsatisfiedDeps,
                                             installCommand);                           
               }
            }
         }
         
         @Override
         public void onError(ServerError error)
         {
            progress.onError(error.getUserMessage());
            
         }
      });
      
   }
   
   private void installDependencies(final JsArray<Dependency> dependencies,
                                    final Command onSuccess)
   {
      server_.installDependencies(
         dependencies, 
         new SimpleRequestCallback<ConsoleProcess>() {
   
            @Override
            public void onResponseReceived(ConsoleProcess proc)
            {   
               final ConsoleProgressDialog dialog = 
                     new ConsoleProgressDialog(proc, server_);
               dialog.showModal();
   
               proc.addProcessExitHandler(
                  new ProcessExitEvent.Handler()
                  {
                     @Override
                     public void onProcessExit(ProcessExitEvent event)
                     {
                        ifDependenciesSatisifed(dependencies, new Command(){
                           @Override
                           public void execute()
                           {
                              dialog.hide();
                              onSuccess.execute();
                           }
                        });     
                     }
                  }); 
            } 
         });
   }
   
   private void ifDependenciesSatisifed(JsArray<Dependency> dependencies,
                                        final Command onInstalled)
   {
      server_.unsatisfiedDependencies(
        dependencies, new SimpleRequestCallback<JsArray<Dependency>>() {
           
           @Override
           public void onResponseReceived(JsArray<Dependency> dependencies)
           {
              if (dependencies.length() == 0)
                 onInstalled.execute();
           }
        });
   }
   
   private void confirmPackageInstallation(
      String userAction, 
      final JsArray<Dependency> dependencies,
      final Command onConfirmed)
   {
      String msg = null;
      if (dependencies.length() == 1)
      {
         msg = "requires an updated version of the " + 
               dependencies.get(0).getName() + " package. " +
               "\n\nDo you want to install this package now?";
      }
      else
      {
         ArrayList<String> deps = new ArrayList<String>();
         for (int i = 0; i < dependencies.length(); i++)
            deps.add(dependencies.get(i).getName());
         
         msg = "requires updated versions of the following packages: " + 
               StringUtil.join(deps, ", ") + ". " +
               "\n\nDo you want to install these packages now?";
      }
      
      globalDisplay_.showYesNoMessage(
         MessageDialog.QUESTION,
         "Install Required Packages", 
         userAction + " " + msg,
         new Operation() {

            @Override
            public void execute()
            {
               onConfirmed.execute();
            }
         },
         true);
   }
   
   private final GlobalDisplay globalDisplay_;
   private final DependencyServerOperations server_;
}

@@ -4377,66 +4377,8 @@
 %22),%0A
-            Dependency.cranPackage(%22htmltools%22, %220.2.4%22),%0A
     

remove htmltools dependency for shiny