/*
 * PublishingPreferencesPane.java
 *
 * Copyright (C) 2009-14 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.workbench.prefs.views;

import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.inject.Inject;

import org.rstudio.core.client.CommandWithArg;
import org.rstudio.core.client.prefs.PreferencesDialogBaseResources;
import org.rstudio.core.client.widget.Operation;
import org.rstudio.core.client.widget.OperationWithInput;
import org.rstudio.core.client.widget.ThemedButton;
import org.rstudio.studio.client.common.GlobalDisplay;
import org.rstudio.studio.client.common.dependencies.DependencyManager;
import org.rstudio.studio.client.rsconnect.RSConnect;
import org.rstudio.studio.client.rsconnect.model.RSConnectAccount;
import org.rstudio.studio.client.rsconnect.model.RSConnectServerOperations;
import org.rstudio.studio.client.rsconnect.ui.RSAccountConnector;
import org.rstudio.studio.client.rsconnect.ui.RSConnectAccountList;
import org.rstudio.studio.client.server.Int;
import org.rstudio.studio.client.server.ServerError;
import org.rstudio.studio.client.server.ServerRequestCallback;
import org.rstudio.studio.client.server.Void;
import org.rstudio.studio.client.workbench.prefs.model.RPrefs;
import org.rstudio.studio.client.workbench.prefs.model.UIPrefs;

public class PublishingPreferencesPane extends PreferencesPane
{
   @Inject
   public PublishingPreferencesPane(GlobalDisplay globalDisplay,
                                    RSConnectServerOperations server,
                                    RSAccountConnector connector,
                                    UIPrefs prefs,
                                    DependencyManager deps)
   {
      reloadRequired_ = false;
      display_ = globalDisplay;
      uiPrefs_ = prefs;
      server_ = server;
      connector_ = connector;
      deps_ = deps;
      
      VerticalPanel accountPanel = new VerticalPanel();
      Label accountLabel = headerLabel("Publishing Accounts");
      HorizontalPanel hpanel = new HorizontalPanel();
      
      accountPanel.add(accountLabel);
      
      accountList_ = new RSConnectAccountList(server, globalDisplay, true, 
            true);
      accountList_.setHeight("200px");
      accountList_.setWidth("300px");
      accountList_.getElement().getStyle().setMarginBottom(15, Unit.PX);
      accountList_.getElement().getStyle().setMarginLeft(3, Unit.PX);
      hpanel.add(accountList_);
      
      accountList_.setOnRefreshCompleted(new Operation() 
      {
         @Override
         public void execute()
         {
            setButtonEnabledState();
         }
      });
      accountList_.addChangeHandler(new ChangeHandler()
      {
         @Override
         public void onChange(ChangeEvent arg0)
         {
            setButtonEnabledState();
         }
      });
      
      VerticalPanel vpanel = new VerticalPanel();
      hpanel.add(vpanel);

      connectButton_ = new ThemedButton("Connect...");
      connectButton_.getElement().getStyle().setMarginBottom(5, Unit.PX);
      connectButton_.setWidth("100%");
      connectButton_.setWrapperWidth("100%");
      connectButton_.addClickHandler(new ClickHandler()
      {
         @Override
         public void onClick(ClickEvent event)
         {
            onConnect();
         }
      });
      vpanel.add(connectButton_);

      reconnectButton_ = new ThemedButton("Reconnect...");
      reconnectButton_.getElement().getStyle().setMarginBottom(5, Unit.PX);
      reconnectButton_.setWidth("100%");
      reconnectButton_.setWrapperWidth("100%");
      reconnectButton_.addClickHandler(new ClickHandler()
      {
         @Override
         public void onClick(ClickEvent event)
         {
            onReconnect();
         }
      });
      vpanel.add(reconnectButton_);
      
      disconnectButton_ = new ThemedButton("Disconnect");
      disconnectButton_.setWidth("100%");
      disconnectButton_.setWrapperWidth("100%");
      disconnectButton_.addClickHandler(new ClickHandler()
      {
         @Override
         public void onClick(ClickEvent event)
         {
            onDisconnect();
         }
      });
      vpanel.add(disconnectButton_);
      
      setButtonEnabledState();

      accountPanel.add(hpanel);
      add(accountPanel);
      
      // special UI to show when we detect that there are account records but
      // the RSConnect package isn't installed
      final VerticalPanel missingPkgPanel = new VerticalPanel();
      missingPkgPanel.setVisible(false);
      missingPkgPanel.add(new Label(
            "Account records appear to exist, but cannot be viewed because a " +
            "required package is not installed."));
      ThemedButton installPkgs = new ThemedButton("Install Missing Packages");
      installPkgs.addClickHandler(new ClickHandler()
      {
         @Override
         public void onClick(ClickEvent arg0)
         {
            deps_.withRSConnect("Viewing publish accounts", false, null, 
                                new CommandWithArg<Boolean>() 
            {
               @Override
               public void execute(Boolean succeeded)
               {
                  if (succeeded)
                  {
                     // refresh the account list to show the accounts
                     accountList_.refreshAccountList();
                     
                     // remove the "missing package" UI
                     missingPkgPanel.setVisible(false);
                  }
               }
            });
         }
      });
      installPkgs.getElement().getStyle().setMarginLeft(0, Unit.PX);
      installPkgs.getElement().getStyle().setMarginTop(10, Unit.PX);
      missingPkgPanel.add(installPkgs);
      missingPkgPanel.getElement().getStyle().setMarginBottom(20, Unit.PX);
      add(missingPkgPanel);
      
      final CheckBox chkEnableRSConnect = checkboxPref("Enable publishing to RStudio Connect (Beta)",
            uiPrefs_.enableRStudioConnect());
      final HorizontalPanel rsconnectPanel = checkBoxWithHelp(chkEnableRSConnect, 
                                                        "rstudio_connect");
      lessSpaced(rsconnectPanel);
      
      add(headerLabel("Settings"));
      CheckBox chkEnablePublishing = checkboxPref("Enable publishing documents and apps", 
            uiPrefs_.showPublishUi());
      chkEnablePublishing.addValueChangeHandler(new ValueChangeHandler<Boolean>(){
         @Override
         public void onValueChange(ValueChangeEvent<Boolean> event)
         {
            reloadRequired_ = true;
            rsconnectPanel.setVisible(
                  RSConnect.showRSConnectUI() && event.getValue());
         }
      });
      add(chkEnablePublishing);
      
      if (RSConnect.showRSConnectUI())
         add(rsconnectPanel);
      
      server_.hasOrphanedAccounts(new ServerRequestCallback<Int>()
      {
         @Override
         public void onResponseReceived(Int numOrphans)
         {
            missingPkgPanel.setVisible(numOrphans.getValue() > 0);
         }

         @Override
         public void onError(ServerError error)
         {
            // if we can't determine whether orphans exist, presume that they
            // don't (this state is recoverable as we'll attempt to install
            // rsconnect if necessary and refresh the account list when the user
            // tries to interact with it)
         }
      });
   }

   @Override
   protected void initialize(RPrefs rPrefs)
   {
   }

   @Override
   public boolean onApply(RPrefs rPrefs)
   {
      boolean reload = super.onApply(rPrefs);

      return reload || reloadRequired_;
   }

   
   @Override
   public ImageResource getIcon()
   {
      return PreferencesDialogBaseResources.INSTANCE.iconPublishing();
   }

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

   @Override
   public String getName()
   {
      return "Publishing";
   }

   private void onDisconnect()
   {
      final RSConnectAccount account = accountList_.getSelectedAccount();
      if (account == null)
      {
         display_.showErrorMessage("Error Disconnecting Account", 
               "Please select an account to disconnect.");
         return;
      }
      display_.showYesNoMessage(
            GlobalDisplay.MSG_QUESTION, 
            "Confirm Remove Account", 
            "Are you sure you want to disconnect the '" + 
              account.getName() + 
            "' account on '" + 
              account.getServer() + "'" + 
            "? This won't delete the account on the server.", 
            false, 
            new Operation()
            {
               @Override
               public void execute()
               {
                  onConfirmDisconnect(account);
               }
            }, null, null, "Disconnect Account", "Cancel", false);
   }
   
   private void onConfirmDisconnect(final RSConnectAccount account)
   {
      server_.removeRSConnectAccount(account.getName(), 
            account.getServer(), new ServerRequestCallback<Void>()
      {
         @Override
         public void onResponseReceived(Void v)
         {
            accountList_.refreshAccountList();
         }

         @Override
         public void onError(ServerError error)
         {
            display_.showErrorMessage("Error Disconnecting Account", 
                                      error.getMessage());
         }
      });
   }
   
   private void onConnect()
   {
      // if there's already at least one account connected, the requisite
      // packages must be installed
      if (accountList_.getAccountCount() > 0)
      {
         showAccountWizard();
      }
      else
      {
         deps_.withRSConnect("Connecting a publishing account", false, null,
                             new CommandWithArg<Boolean>() 
         {
            @Override
            public void execute(Boolean succeeded)
            {
               // refresh the account list in case there are accounts already on
               // the system (e.g. package was installed at one point and some
               // metadata remains)
               accountList_.refreshAccountList();

               showAccountWizard();
            }
         });
      }
   }
   
   private void onReconnect()
   {
      connector_.showReconnectWizard(accountList_.getSelectedAccount(), 
            new OperationWithInput<Boolean>()
      {
         @Override
         public void execute(Boolean successful)
         {
            if (successful)
            {
               accountList_.refreshAccountList();
            }
         }
      });
   }
   
   private void showAccountWizard()
   {
      connector_.showAccountWizard(false, true, 
            new OperationWithInput<Boolean>() 
      {
         @Override
         public void execute(Boolean successful)
         {
            if (successful)
            {
               accountList_.refreshAccountList();
            }
         }
      });
   }

   private void setButtonEnabledState()
   {
      disconnectButton_.setEnabled(
            accountList_.getSelectedAccount() != null);
      
      reconnectButton_.setEnabled(
            accountList_.getSelectedAccount() != null &&
            !accountList_.getSelectedAccount().isCloudAccount());
   }
   
   private final GlobalDisplay display_;
   private final UIPrefs uiPrefs_;
   private final RSConnectServerOperations server_;
   private final RSAccountConnector connector_;
   private final DependencyManager deps_;

   private RSConnectAccountList accountList_;
   private ThemedButton connectButton_;
   private ThemedButton disconnectButton_;
   private ThemedButton reconnectButton_;
   private boolean reloadRequired_;
}

