/*
 * Copyright 2000-2016 JetBrains s.r.o.
 *
 * 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 org.jetbrains.idea.svn.actions;

import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.application.ApplicationNamesInfo;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.vcs.ProjectLevelVcsManager;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.idea.svn.*;
import org.jetbrains.idea.svn.api.ClientFactory;
import org.jetbrains.idea.svn.api.Depth;
import org.jetbrains.idea.svn.checkout.SvnCheckoutProvider;
import org.jetbrains.idea.svn.dialogs.ShareDialog;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc2.SvnTarget;

import static com.intellij.openapi.progress.ProgressManager.progress;
import static com.intellij.openapi.ui.Messages.*;
import static com.intellij.openapi.vfs.VfsUtilCore.virtualToIoFile;
import static com.intellij.util.ArrayUtil.isEmpty;
import static org.jetbrains.idea.svn.SvnBundle.message;
import static org.jetbrains.idea.svn.SvnUtil.append;
import static org.jetbrains.idea.svn.SvnUtil.createUrl;

public class ShareProjectAction extends BasicAction {

  @NotNull
  @Override
  protected String getActionName() {
    return message("share.directory.action");
  }

  @Override
  public void update(@NotNull AnActionEvent e) {
    VirtualFile[] files = e.getData(CommonDataKeys.VIRTUAL_FILE_ARRAY);
    Project project = e.getProject();
    boolean visible = project != null &&
                      !ProjectLevelVcsManager.getInstance(project).isBackgroundVcsOperationRunning() &&
                      !isEmpty(files) &&
                      files.length == 1 &&
                      files[0].isDirectory();

    e.getPresentation().setVisible(visible);
    e.getPresentation().setEnabled(visible && !SvnStatusUtil.isUnderControl(project, files[0]));
  }

  @Override
  protected boolean isEnabled(@NotNull SvnVcs vcs, @NotNull VirtualFile file) {
    return false;
  }

  public static boolean share(@NotNull Project project, @NotNull VirtualFile file) throws VcsException {
    return performImpl(SvnVcs.getInstance(project), file);
  }

  @Override
  protected void perform(@NotNull SvnVcs vcs, @NotNull VirtualFile file, @NotNull DataContext context) throws VcsException {
    performImpl(vcs, file);
  }

  private static boolean performImpl(@NotNull SvnVcs vcs, @NotNull VirtualFile file) throws VcsException {
    ShareDialog shareDialog = new ShareDialog(vcs.getProject(), file.getName());
    shareDialog.show();

    String parent = shareDialog.getSelectedURL();
    if (shareDialog.isOK() && parent != null) {
      Ref<Boolean> actionStarted = new Ref<>(Boolean.TRUE);
      Exception[] error = new Exception[1];
      ShareDialog.ShareTarget shareTarget = shareDialog.getShareTarget();

      if (ShareDialog.ShareTarget.useSelected.equals(shareTarget) &&
          !isFolderEmpty(vcs, parent) &&
          YES !=
          showYesNoDialog(vcs.getProject(), "Remote folder \"" + parent + "\" is not empty.\nDo you want to continue sharing?",
                          "Share Directory", getWarningIcon())) {
        return false;
      }

      WorkingCopyFormat format = SvnCheckoutProvider.promptForWCopyFormat(virtualToIoFile(file), vcs.getProject());
      actionStarted.set(format != WorkingCopyFormat.UNKNOWN);
      // means operation cancelled
      if (format == WorkingCopyFormat.UNKNOWN) {
        return true;
      }

      ExclusiveBackgroundVcsAction.run(vcs.getProject(), () ->
        ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> {
          try {
            SvnWorkingCopyFormatHolder.setPresetFormat(format);

            SvnTarget checkoutTarget =
              createFolderStructure(vcs, file, shareTarget, shareDialog.createStandardStructure(), createUrl(parent),
                                    shareDialog.getCommitText());

            progress(message("share.directory.checkout.back.progress.text", checkoutTarget.getPathOrUrlString()));

            ClientFactory factory = SvnCheckoutProvider.getFactory(vcs, format);

            factory.createCheckoutClient()
              .checkout(SvnTarget.fromURL(checkoutTarget.getURL()), virtualToIoFile(file), checkoutTarget.getPegRevision(), Depth.INFINITY,
                        false, false, format, null);
            addRecursively(vcs, factory, file);
          }
          catch (VcsException e) {
            error[0] = e;
          }
          finally {
            vcs.invokeRefreshSvnRoots();
            SvnWorkingCopyFormatHolder.setPresetFormat(null);
          }
        }, message("share.directory.title"), true, vcs.getProject()));

      if (Boolean.TRUE.equals(actionStarted.get())) {
        if (error[0] != null) {
          throw new VcsException(error[0].getMessage());
        }
        showInfoMessage(vcs.getProject(), message("share.directory.info.message", file.getName()), message("share.directory.title"));
      }
      return true;
    }
    return false;
  }

  @NotNull
  private static SvnTarget createFolderStructure(@NotNull SvnVcs vcs,
                                                 @NotNull VirtualFile file,
                                                 @NotNull ShareDialog.ShareTarget shareTarget,
                                                 boolean createStandardStructure,
                                                 @NotNull SVNURL parentUrl,
                                                 @NotNull String commitText) throws VcsException {
    switch (shareTarget) {
      case useSelected:
        return SvnTarget.fromURL(parentUrl, SVNRevision.HEAD);
      case useProjectName:
        return createRemoteFolder(vcs, parentUrl, file.getName(), commitText);
      default:
        SvnTarget projectRoot = createRemoteFolder(vcs, parentUrl, file.getName(), commitText);
        SvnTarget trunk = createRemoteFolder(vcs, projectRoot.getURL(), "trunk", commitText);

        if (createStandardStructure) {
          createRemoteFolder(vcs, projectRoot.getURL(), "branches", commitText);
          createRemoteFolder(vcs, projectRoot.getURL(), "tags", commitText);
        }
        return trunk;
    }
  }

  private static boolean isFolderEmpty(@NotNull SvnVcs vcs, @NotNull String folderUrl) throws VcsException {
    return ProgressManager.getInstance().runProcessWithProgressSynchronously(
      () -> SvnUtil.remoteFolderIsEmpty(vcs, folderUrl), "Check Remote Folder Contents", false, vcs.getProject());
  }

  @NotNull
  private static SvnTarget createRemoteFolder(@NotNull SvnVcs vcs,
                                              @NotNull SVNURL parent,
                                              @NotNull String folderName,
                                              @NotNull String commitText) throws VcsException {
    SVNURL url = append(parent, folderName);
    String message =
      message("share.directory.commit.message", folderName, ApplicationNamesInfo.getInstance().getFullProductName(), commitText);
    SvnTarget target = SvnTarget.fromURL(url);

    progress(message("share.directory.create.dir.progress.text", url.toString()));

    long revision = vcs.getFactoryFromSettings().createBrowseClient().createDirectory(target, message, false);
    return SvnTarget.fromURL(url, SVNRevision.create(revision));
  }

  @Override
  protected void doVcsRefresh(@NotNull SvnVcs vcs, @NotNull VirtualFile file) {
    VcsDirtyScopeManager.getInstance(vcs.getProject()).dirDirtyRecursively(file);
  }

  private static void addRecursively(@NotNull SvnVcs vcs, @NotNull ClientFactory factory, @NotNull VirtualFile rootFile)
    throws VcsException {
    SvnExcludingIgnoredOperation operation = new SvnExcludingIgnoredOperation(vcs.getProject(), file -> {
      progress(message("share.or.import.add.progress.text", file.getPath()));

      factory.createAddClient().add(virtualToIoFile(file), Depth.EMPTY, false, false, true, null);
    }, Depth.INFINITY);

    operation.execute(rootFile);
  }

  @Override
  protected void batchPerform(@NotNull SvnVcs vcs, @NotNull VirtualFile[] files, @NotNull DataContext context) throws VcsException {
  }

  protected boolean isBatchAction() {
    return false;
  }
}
