package org.testng.eclipse.ui.util;

import java.text.Collator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;

import org.eclipse.core.resources.IResource;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.ui.dialogs.SelectionDialog;
import org.eclipse.ui.internal.ide.IDEWorkbenchMessages;
import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;
import org.eclipse.ui.internal.ide.StringMatcher;
import org.eclipse.ui.model.WorkbenchLabelProvider;


/**
 * This class/interface 
 */
public class SuiteListSelectionDialog extends SelectionDialog {
    
  private static final String DIALOG_SETTINGS_SECTION = "ResourceListSelectionDialogSettings"; //$NON-NLS-1$
  
  Table resourceNames;

  Table folderNames;

  int typeMask;

  private static Collator collator = Collator.getInstance();

  StringMatcher stringMatcher;

  ResourceDescriptor[] descriptors;

  int descriptorsSize;

  WorkbenchLabelProvider labelProvider = new WorkbenchLabelProvider();
  
  boolean okEnabled = false;

  static class ResourceDescriptor implements Comparable {
      String label;

      ArrayList resources = new ArrayList();

      boolean resourcesSorted = true;

      public int compareTo(Object o) {
          return collator.compare(label, ((ResourceDescriptor) o).label);
      }
  }

  /**
   * Creates a new instance of the class.
   * 
   * @param parentShell shell to parent the dialog on
   * @param resources resources to display in the dialog
   */
  public SuiteListSelectionDialog(Shell parentShell, IResource[] resources) {
      super(parentShell);
      setShellStyle(getShellStyle() | SWT.RESIZE);
      initDescriptors(resources);
  }

  
  /**
   * @see org.eclipse.jface.dialogs.Dialog#cancelPressed()
   */
  protected void cancelPressed() {
      setResult(null);
      super.cancelPressed();
  }

  /**
   * @see org.eclipse.jface.window.Window#close()
   */
  public boolean close() {
      boolean result = super.close();
      labelProvider.dispose();
      return result;
  }

  /**
   * @see org.eclipse.jface.window.Window#create()
   */
  public void create() {
      super.create();
      getButton(IDialogConstants.OK_ID).setEnabled(okEnabled);
  }

  /**
   * Creates the contents of this dialog, initializes the
   * listener and the update thread.
   * 
   * @param parent parent to create the dialog widgets in
   */
  protected Control createDialogArea(Composite parent) {
      Composite dialogArea = (Composite) super.createDialogArea(parent);
      Label l = new Label(dialogArea, SWT.NONE);
      l = new Label(dialogArea, SWT.NONE);
      l.setText(IDEWorkbenchMessages.ResourceSelectionDialog_matching);
      GridData data = new GridData(GridData.FILL_HORIZONTAL);
      l.setLayoutData(data);
      resourceNames = new Table(dialogArea, SWT.SINGLE | SWT.BORDER
              | SWT.V_SCROLL);
      data = new GridData(GridData.FILL_BOTH);
      data.heightHint = 12 * resourceNames.getItemHeight();
      resourceNames.setLayoutData(data);

      l = new Label(dialogArea, SWT.NONE);
      l.setText(IDEWorkbenchMessages.ResourceSelectionDialog_folders);
      data = new GridData(GridData.FILL_HORIZONTAL);
      l.setLayoutData(data);

      folderNames = new Table(dialogArea, SWT.SINGLE | SWT.BORDER
              | SWT.V_SCROLL | SWT.H_SCROLL);
      data = new GridData(GridData.FILL_BOTH);
      data.widthHint = 300;
      data.heightHint = 4 * folderNames.getItemHeight();
      folderNames.setLayoutData(data);

      resourceNames.addSelectionListener(new SelectionAdapter() {
          public void widgetSelected(SelectionEvent e) {
              updateFolders((ResourceDescriptor) e.item.getData());
          }

          public void widgetDefaultSelected(SelectionEvent e) {
              okPressed();
          }
      });

      folderNames.addSelectionListener(new SelectionAdapter() {
          public void widgetDefaultSelected(SelectionEvent e) {
              okPressed();
          }
      });

      applyDialogFont(dialogArea);

      return dialogArea;
  }

  /**
   * Return an image for a resource descriptor.
   * 
   * @param desc resource descriptor to return image for
   * @return an image for a resource descriptor.
   */
  private Image getImage(ResourceDescriptor desc) {
      IResource r = (IResource) desc.resources.get(0);
      return labelProvider.getImage(r);
  }
  
  private Image getParentImage(IResource resource) {
      IResource parent = resource.getParent();
      return labelProvider.getImage(parent);
  }

  private String getParentLabel(IResource resource) {
      IResource parent = resource.getParent();
      String text;
      if (parent.getType() == IResource.ROOT) {
          // Get readable name for workspace root ("Workspace"), without duplicating language-specific string here.
          text = labelProvider.getText(parent);
      } else {
          text = parent.getFullPath().makeRelative().toString();
      }
      if(text == null) {
            return ""; //$NON-NLS-1$
        }
      return text;
  }

  /**
   * Creates a ResourceDescriptor for each IResource,
   * sorts them and removes the duplicated ones.
   * 
   * @param resources resources to create resource descriptors for
   */
  private void initDescriptors(final IResource resources[]) {
      BusyIndicator.showWhile(null, new Runnable() {
          public void run() {
              descriptors = new ResourceDescriptor[resources.length];
              for (int i = 0; i < resources.length; i++) {
                  IResource r = resources[i];
                  ResourceDescriptor d = new ResourceDescriptor();
                  //TDB: Should use the label provider and compare performance.
                  d.label = r.getName();
                  d.resources.add(r);
                  descriptors[i] = d;
              }
              Arrays.sort(descriptors);
              descriptorsSize = descriptors.length;

              //Merge the resource descriptor with the same label and type.
              int index = 0;
              if (descriptorsSize < 2) {
                    return;
                }
              ResourceDescriptor current = descriptors[index];
              IResource currentResource = (IResource) current.resources.get(0);
              for (int i = 1; i < descriptorsSize; i++) {
                  ResourceDescriptor next = descriptors[i];
                  IResource nextResource = (IResource) next.resources.get(0);
                  if (nextResource.getType() == currentResource.getType()
                          && next.label.equals(current.label)) {
                      current.resources.add(nextResource);
                      // If we are merging resources with the same name, into a single descriptor,
                      // then we must mark the descriptor unsorted so that we will sort the folder
                      // names.  
                      // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=76496
                      current.resourcesSorted = false;
                  } else {
                      if (current.resources.size() > 1) {
                          current.resourcesSorted = false;
                      }
                      descriptors[index + 1] = descriptors[i];
                      index++;
                      current = descriptors[index];
                      currentResource = (IResource) current.resources.get(0);
                  }
              }
              descriptorsSize = index + 1;
          }
      });
  }

  /**
   * The user has selected a resource and the dialog is closing.
   * Set the selected resource as the dialog result.
   */
  protected void okPressed() {
      TableItem items[] = folderNames.getSelection();
      if (items.length == 1) {
          ArrayList result = new ArrayList();
          result.add(items[0].getData());
          setResult(result);
      }
      super.okPressed();
  }

  /**
   * Use this method to further filter resources.  As resources are gathered,
   * if a resource matches the current pattern string, this method will be called.
   * If this method answers false, the resource will not be included in the list
   * of matches and the resource's children will NOT be considered for matching.
   */
  protected boolean select(IResource resource) {
      return true;
  }

  /**
   * A new resource has been selected. Change the contents
   * of the folder names list.
   * 
   * @desc resource descriptor of the selected resource
   */
  private void updateFolders(final ResourceDescriptor desc) {
      BusyIndicator.showWhile(getShell().getDisplay(), new Runnable() {
          public void run() {
              if (!desc.resourcesSorted) {
                  // sort the folder names
                  Collections.sort(desc.resources, new Comparator() {
                      public int compare(Object o1, Object o2) {
                          String s1 = getParentLabel((IResource) o1);
                          String s2 = getParentLabel((IResource) o2);
                          return collator.compare(s1, s2);
                      }
                  });
                  desc.resourcesSorted = true;
              }
              folderNames.removeAll();
              for (int i = 0; i < desc.resources.size(); i++) {
                  TableItem newItem = new TableItem(folderNames, SWT.NONE);
                  IResource r = (IResource) desc.resources.get(i);
                  newItem.setText(getParentLabel(r));
                  newItem.setImage(getParentImage(r));
                  newItem.setData(r);
              }
              folderNames.setSelection(0);
          }
      });
  }

  /**
   * Update the specified item with the new info from the resource 
   * descriptor.
   * Create a new table item if there is no item. 
   * 
   * @param index index of the resource descriptor
   * @param itemPos position of the existing item to update
   * @param itemCount number of items in the resources table widget
   */
  private void updateItem(int index, int itemPos, int itemCount) {
      ResourceDescriptor desc = descriptors[index];
      TableItem item;
      if (itemPos < itemCount) {
          item = resourceNames.getItem(itemPos);
          if (item.getData() != desc) {
              item.setText(desc.label);
              item.setData(desc);
              item.setImage(getImage(desc));
              if (itemPos == 0) {
                  resourceNames.setSelection(0);
                  updateFolders(desc);
              }
          }
      } else {
          item = new TableItem(resourceNames, SWT.NONE);
          item.setText(desc.label);
          item.setData(desc);
          item.setImage(getImage(desc));
          if (itemPos == 0) {
              resourceNames.setSelection(0);
              updateFolders(desc);
          }
      }
      updateOKState(true);
  }
  
  protected Control createContents(Composite parent) {
    Control ctrl= super.createContents(parent);
    
    for(int i= 0; i < descriptorsSize; i++ ) {
      updateItem(i, i, 0);
    }
    
    return ctrl;
  }
  
  /**
   * Update the enabled state of the OK button.  To be called when
   * the resource list is updated.
   * @param state the new enabled state of the button
   */
  protected void updateOKState(boolean state) {
    Button okButton = getButton(IDialogConstants.OK_ID);
    if(okButton != null && !okButton.isDisposed() && state != okEnabled) {
        okButton.setEnabled(state);
        okEnabled = state;
    }
  }
  
    
    /* (non-Javadoc)
   * @see org.eclipse.jface.window.Dialog#getDialogBoundsSettings()
   * 
   * @since 3.2
   */
    protected IDialogSettings getDialogBoundsSettings() {
      IDialogSettings settings = IDEWorkbenchPlugin.getDefault().getDialogSettings();
      IDialogSettings section = settings.getSection(DIALOG_SETTINGS_SECTION);
      if (section == null) {
          section = settings.addNewSection(DIALOG_SETTINGS_SECTION);
      } 
      return section;
    }
}
