/*******************************************************************************
 * Copyright 2011 Google Inc. All Rights Reserved.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * 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.gwt.eclipse.core.launch.ui;

import com.google.gdt.eclipse.core.AdapterUtilities;
import com.google.gdt.eclipse.core.CorePluginLog;
import com.google.gdt.eclipse.core.ResourceUtils;
import com.google.gdt.eclipse.core.SWTUtilities;
import com.google.gdt.eclipse.core.WebAppUtilities;
import com.google.gdt.eclipse.core.properties.WebAppProjectProperties;
import com.google.gwt.eclipse.core.GWTPlugin;
import com.google.gwt.eclipse.core.GWTPluginLog;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.internal.ui.SWTFactory;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
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.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.FilteredResourcesSelectionDialog;
import org.osgi.service.prefs.BackingStoreException;

/**
 * Selects an HTML host page from the given project's war directory.
 * 
 * TODO: If there are no HTML files to select, the checkbox will still stay
 * checked and the user will have to uncheck the box to continue.
 * 
 * TODO: The FilteredResourcesSelectionDialog is pretty heavy weight for what
 * we're trying to do here (just select an html page), eg, it makes it difficult
 * to know right at the start of the dialog how many items there are because it
 * fills the list in a number of asynchronous jobs (and there doesn't seem to be
 * any way to get notified when that's done), so we may want to use a different
 * dialog base, or make our own.
 * 
 */
@SuppressWarnings("restriction")
// Restricted access to SWTFactory
public class WebAppHostPageSelectionDialog extends
    FilteredResourcesSelectionDialog {

  private class HostPageFilter extends ResourceFilter {
    public HostPageFilter() {
      /*
       * A new filter is created every time the filter text is changed, so we
       * can just tack a on the end of the pattern to make the pattern match
       * even if the user starts typing part of the file extension
       */
      patternMatcher.setPattern(patternMatcher.getPattern() + "*");
    }

    @Override
    public boolean matchItem(Object item) {
      IFile file = AdapterUtilities.getAdapter(item, IFile.class);
      if (!super.matchItem(item) || (file == null)) {
        return false;
      }

      if (!ResourceUtils.hasJspOrHtmlExtension(file)) {
        return false;
      }

      if (!warFolder.equals(file.getParent())) {
        return false;
      }

      return true;
    }
  }

  private static final String URL_LABEL_TEXT = "Final URL: ";
  private static final IStatus OK_STATUS = new Status(Status.OK,
      GWTPlugin.PLUGIN_ID, "");

  /**
   * Selects an HTML host page from a given project. Returns <code>null</code>
   * if the selection dialog was canceled.
   * 
   * @param javaProject the project from which to select a host page
   * @return a relative URL, or null if the selection dialog was canceled.
   * 
   */
  public static String show(IJavaProject javaProject, boolean isExternal) {
    Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
    try {
      IProject project = javaProject.getProject();
      WebAppUtilities.verifyIsWebApp(project);

      IFolder warFolder = WebAppUtilities.getWarSrc(project);
      if (warFolder.exists()) {
        WebAppHostPageSelectionDialog dialog = new WebAppHostPageSelectionDialog(
            shell, javaProject, warFolder, isExternal);
        if (dialog.open() == OK) {
          Object[] result = dialog.getResult();
          IFile htmlFile = dialog.getSelectedFile(result);
          try {
            WebAppProjectProperties.setLaunchConfigExternalUrlPrefix(project,
                dialog.getExternalUrlPrefix());
          } catch (BackingStoreException e) {
            GWTPluginLog.logError(e);
          }
          return dialog.getUrl(warFolder, htmlFile);
        }
      }
    } catch (CoreException e) {
      CorePluginLog.logError(e);
    }

    return null;
  }

  private Text externalUrlPrefixText;
  private String externalUrlPrefixTextCache;
  private Group fileSelectionGroup;
  private final boolean isExternal;

  private IStatus oldStatus;
  private final IProject project;
  // superclass keeps status private, so we have to duplicate functionality
  private IStatus status;
  private boolean useFile = true;
  private Button useProjectFileCheckbox;
  private Label urlLabel;
  private IFile currentSelection;

  private final IFolder warFolder;

  private WebAppHostPageSelectionDialog(Shell shell, IJavaProject project,
      IFolder warFolder, boolean isExternal) {
    super(shell, false, project.getProject(), IResource.FILE);

    assert (warFolder != null);

    this.project = project.getProject();
    this.isExternal = isExternal;
    this.warFolder = warFolder;
    setTitle("HTML Page Selection");
    setMessage("");
  }

  @Override
  protected Control createDialogArea(Composite parent) {
    SWTFactory.createVerticalSpacer(parent, 16);
    if (isExternal) {
      createExternalUI(parent);
    }

    fileSelectionGroup = new Group(parent, SWT.SHADOW_ETCHED_IN);
    GridData gd = new GridData(GridData.FILL_BOTH);
    fileSelectionGroup.setLayoutData(gd);

    GridLayout layout = new GridLayout();
    layout.numColumns = 1;
    layout.marginWidth = 0;
    layout.marginHeight = 0;
    fileSelectionGroup.setLayout(layout);

    Control controlToReturn = super.createDialogArea(fileSelectionGroup);

    SWTFactory.createVerticalSpacer(parent, 8);
    Composite urlComposite = new Composite(parent, SWT.NONE);
    urlComposite.setLayout(layout);
    urlComposite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

    new Label(urlComposite, SWT.NONE).setText(URL_LABEL_TEXT);

    gd = new GridData(GridData.FILL_HORIZONTAL);
    Group urlGroup = new Group(urlComposite, SWT.SHADOW_ETCHED_IN);
    urlGroup.setLayoutData(gd);
    urlGroup.setLayout(layout);

    gd = new GridData(GridData.FILL_BOTH);
    urlLabel = new Label(urlGroup, SWT.NONE);
    urlLabel.setLayoutData(gd);

    return controlToReturn;
  }

  protected Control createExternalRootContentArea(Composite parent) {

    Composite c = SWTFactory.createComposite(parent, 1, 1,
        GridData.FILL_HORIZONTAL);
    SWTFactory.createLabel(c, "External server root:", 1);
    externalUrlPrefixText = SWTFactory.createSingleText(c, 1);
    externalUrlPrefixTextCache = WebAppProjectProperties.getLaunchConfigExternalUrlPrefix(project);
    externalUrlPrefixText.setText(externalUrlPrefixTextCache);
    externalUrlPrefixText.addModifyListener(new ModifyListener() {
      public void modifyText(ModifyEvent e) {
        externalUrlPrefixTextCache = externalUrlPrefixText.getText();
        updateUrlLabelText();
      }
    });

    return c;
  }
  
  protected void createExternalUI(Composite parent) {
    createExternalRootContentArea(parent);
    SWTFactory.createVerticalSpacer(parent, 16);
    useProjectFileCheckbox = new Button(parent, SWT.CHECK);
    useProjectFileCheckbox.setText("Select an HTML page:");
    useProjectFileCheckbox.setSelection(true);
    useProjectFileCheckbox.addSelectionListener(new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {
        boolean enabled = useProjectFileCheckbox.getSelection();
        useFile = enabled;
        SWTUtilities.setEnabledRecursive(fileSelectionGroup, enabled);

        if (enabled) {
          updateStatus(oldStatus);
        } else {
          // save the current status so that if the user re-enables the file
          // selector,
          // if it was in a bad state (ie, no selection), that state is
          // restored.
          if (status != null) {
            oldStatus = status;
          } else {
            oldStatus = OK_STATUS;
          }

          // it's ok to blast the current state because it doesn't depend on
          // anything new added to the UI (specifically, the external server
          // text)
          updateStatus(OK_STATUS);
        }

        updateUrlLabelText();
      }
    });
  }

  @Override
  protected ItemsFilter createFilter() {
    return new HostPageFilter();
  }

  protected IFile getSelectedFile(Object[] result) {
    if (result.length == 1 && result[0] instanceof IFile) {
      return (IFile) result[0];
    }
    return null;
  }

  protected String getUrl(IFolder warFolder, IFile selectedFile) {
    if (!isExternal && useFile) {
      if (selectedFile == null) {
        return null;
      }
      return selectedFile.getName();
    }

    String url = "";
    if (useFile && selectedFile != null) {
      url = selectedFile.getFullPath().removeFirstSegments(
          warFolder.getFullPath().segmentCount()).toString();
    }

    String externalURLPrefix = getExternalUrlPrefix();

    if (externalURLPrefix.length() > 0) {
      if (!externalURLPrefix.endsWith("/")) {
        externalURLPrefix += '/';
      }
    }

    return externalURLPrefix + url;
  }

  @Override
  protected void handleSelected(StructuredSelection selection) {
    super.handleSelected(selection);
    Object[] objs = selection.toArray();
    if (objs.length == 1) {
      if (objs[0] instanceof IFile) {
        currentSelection = (IFile) objs[0];
      } else {
        currentSelection = null;
      }
    } else {
      currentSelection = null;
    }
    updateUrlLabelText();
  }

  /**
   * The superclass keeps the status private, and doesn't provide a getter, so
   * we have to override updateStatus and keep track of it ourselves.
   */
  @Override
  protected void updateStatus(IStatus status) {
    this.status = status;
    super.updateStatus(status);
  }

  protected void updateUrlLabelText() {

    String url = getUrl(warFolder, currentSelection);

    if (url == null) {
      url = "";
    }

    urlLabel.setText(url);
  }

  private String getExternalUrlPrefix() {
    if (isExternal) {
      return externalUrlPrefixTextCache.trim();
    }

    return "";
  }
}