// Copyright 2016 The Bazel Authors. All rights reserved.
//
// 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.devtools.bazel.e4b.wizard;

import java.io.File;
import java.io.IOException;
import java.util.function.Consumer;

import org.eclipse.jface.bindings.keys.KeyStroke;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.fieldassist.ContentProposalAdapter;
import org.eclipse.jface.fieldassist.TextContentAdapter;
import org.eclipse.jface.viewers.CheckboxTreeViewer;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.DirectoryDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.List;
import org.eclipse.swt.widgets.Text;

import com.google.common.collect.ImmutableList;
import com.google.devtools.bazel.e4b.Activator;
import com.google.devtools.bazel.e4b.command.BazelNotFoundException;

/**
 * This is a quick wizard page that ask the user for the various targets and source path he wants to
 * include.
 */
public class WorkspaceWizardPage extends WizardPage {

  private Label workspaceRoot;
  private List targets;
  private Text target;
  private Button addTargetButton;
  private Button removeTargetButton;
  private CheckboxTreeViewer directories;
  private Composite container;
  private Button workspaceRootButton;
  private DirectoryDialog dialog;
  private BazelTargetCompletionContentProposalProvider completionProvider;

  protected WorkspaceWizardPage() {
    super("Import Bazel project");
    setTitle("Import Bazel project");
  }

  /**
   * Returns the list of targets selected by the user.
   */
  ImmutableList<String> getTargets() {
    return ImmutableList.copyOf(targets.getItems());
  }

  /**
   * Returns the list of directories selected by the user.
   */
  ImmutableList<String> getDirectories() {
    return DirectoryTreeContentProvider.getSelectPathsRelativeToRoot(directories);
  }

  /**
   * Returns the workspace root selected by the user.
   */
  public String getWorkspaceRoot() {
    return workspaceRoot.getText();
  }

  /**
   * Returns the language version for the new project.
   */
  int getJavaLanguageVersion() {
    return 8;
  }


  @Override
  public void createControl(Composite parent) {
    container = new Composite(parent, SWT.NONE);
    GridLayout layout = new GridLayout();
    layout.numColumns = 3;
    container.setLayout(layout);

    createWorkspaceSelectionControls();

    addLabel("Directories: ", 1, 5);
    directories = DirectoryTreeContentProvider.createTreeView(container);
    setControlGridData(directories.getTree(), 2, 5, true);
    directories.addCheckStateListener(e -> updateControls());

    new Label(container, SWT.NONE).setText("Targets:");
    createTargetTextField();
    setControlGridData(target, 1, 1, false);
    addTargetButton = createButton("+", e -> addTarget());

    targets = new List(container, SWT.SINGLE | SWT.BORDER);
    setControlGridData(targets, 2, 5, true);
    removeTargetButton = createButton("-", e -> deleteTarget());
    targets.addSelectionListener(createSelectionListener(
        e -> removeTargetButton.setEnabled(targets.getSelectionCount() > 0)));

    setControl(container);
    updateControls();
  }

  // A simple helper to use lambdas to create a SelectionListener that does the same action on all
  // cases.
  private static SelectionListener createSelectionListener(Consumer<SelectionEvent> f) {
    return new SelectionListener() {
      @Override
      public void widgetSelected(SelectionEvent e) {
        f.accept(e);

      }

      @Override
      public void widgetDefaultSelected(SelectionEvent e) {
        f.accept(e);
      }
    };
  }

  private Button createButton(String text, Consumer<SelectionEvent> handler) {
    Button result = new Button(container, SWT.DEFAULT);
    result.setText(text);
    result.addSelectionListener(createSelectionListener(handler));
    result.setEnabled(false);
    return result;
  }

  private void createWorkspaceSelectionControls() {
    Label label = new Label(container, SWT.NONE);
    label.setText("Workpsace root: ");

    workspaceRoot = new Label(container, SWT.BORDER);
    workspaceRoot.setText("");
    workspaceRoot.setLayoutData(new GridData(GridData.FILL, GridData.CENTER, true, false));

    dialog = new DirectoryDialog(getShell(), SWT.OPEN);
    workspaceRootButton = new Button(container, SWT.DEFAULT);
    workspaceRootButton.setText("...");
    workspaceRootButton.addSelectionListener(new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {
        String wr = dialog.open();
        if (wr != null) {
          workspaceRoot.setText(wr);
          DirectoryTreeContentProvider.setFileTreeRoot(directories, new File(wr));
          try {
            completionProvider.setBazelInstance(
                Activator.getDefault().getCommand().getInstance(new File(getWorkspaceRoot())));
          } catch (IOException e1) {
            MessageDialog.openError(getShell(), "Error",
                getWorkspaceRoot() + " does not seems to be a Bazel workspace");
          } catch (InterruptedException e1) {
            Activator.error("Bazel was interrupted", e1);
          } catch (BazelNotFoundException e1) {
            MessageDialog.openError(getShell(), "Error", "Cannot found Bazel: " + e1.getMessage());
          }

        }
        updateControls();
      }
    });
    workspaceRootButton.setLayoutData(new GridData(GridData.END, GridData.CENTER, false, false));
  }

  private void setControlGridData(Control control, int horizontalSpan, int verticalSpan,
      boolean verticalGrow) {
    GridData data = new GridData(GridData.FILL, verticalGrow ? GridData.FILL : GridData.CENTER,
        true, verticalGrow);
    data.horizontalSpan = horizontalSpan;
    data.verticalSpan = verticalSpan;
    control.setLayoutData(data);
  }

  private void addLabel(String labelText, int horizontalSpan, int verticalSpan) {
    Label label = new Label(container, SWT.NONE);
    label.setText(labelText);
    GridData data = new GridData(GridData.BEGINNING, GridData.FILL, false, true);
    data.horizontalSpan = horizontalSpan;
    data.verticalSpan = verticalSpan;
    label.setLayoutData(data);
  }

  private void updateControls() {
    boolean enabled = !workspaceRoot.getText().isEmpty();
    directories.getTree().setEnabled(enabled);
    targets.setEnabled(enabled);
    target.setEnabled(enabled);
    addTargetButton.setEnabled(enabled && !target.getText().isEmpty());
    removeTargetButton.setEnabled(enabled && targets.getSelectionCount() > 0);
    setPageComplete(
        enabled && (directories.getCheckedElements().length > 0) && (targets.getItemCount() > 0));
  }

  private void setAutoCompletion() {
    try {
      ContentProposalAdapter adapter = null;
      completionProvider = new BazelTargetCompletionContentProposalProvider();
      KeyStroke ks = KeyStroke.getInstance("Ctrl+Space");
      adapter = new ContentProposalAdapter(target, new TextContentAdapter(), completionProvider, ks,
          null);
      adapter.setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_REPLACE);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }


  private void createTargetTextField() {
    target = new Text(container, SWT.BORDER);
    setAutoCompletion();
    target.addKeyListener(new KeyAdapter() {
      @Override
      public void keyReleased(KeyEvent ke) {
        if (ke.keyCode == '\r' && (ke.stateMask & SWT.SHIFT) != 0 && !target.getText().isEmpty()) {
          addTarget();
        }
      }
    });
    target.addModifyListener(e -> updateControls());
  }

  private void addTarget() {
    targets.add(target.getText());
    target.setText("");
    setAutoCompletion();
    updateControls();
  }

  private void deleteTarget() {
    targets.remove(targets.getSelectionIndices());
    updateControls();
  }
}