/*******************************************************************************
 * Copyright (c) 2001, 2008 IBM Corporation and others.
 * 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
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *     David Carver - STAR - added nillable advanced tab option, bug 209356
 *******************************************************************************/

package org.eclipse.wst.xsd.ui.internal.common.properties.sections;

import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CCombo;
import org.eclipse.swt.custom.CLabel;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.ui.IEditorPart;
import org.eclipse.wst.common.ui.internal.search.dialogs.ComponentSpecification;
import org.eclipse.wst.xsd.ui.internal.adt.edit.ComponentReferenceEditManager;
import org.eclipse.wst.xsd.ui.internal.adt.edit.IComponentDialog;
import org.eclipse.wst.xsd.ui.internal.common.commands.UpdateAttributeValueCommand;
import org.eclipse.wst.xsd.ui.internal.editor.Messages;
import org.eclipse.wst.xsd.ui.internal.editor.XSDSubstitutionGroupEditManager;
import org.eclipse.xsd.XSDElementDeclaration;
import org.eclipse.xsd.XSDSchema;
import org.eclipse.xsd.util.XSDConstants;
import org.w3c.dom.Element;

public class XSDElementDeclarationAdvancedSection extends AbstractSection
{
  private static final String EMPTY = ""; //$NON-NLS-1$
  private static final String FALSE = "false"; //$NON-NLS-1$
  private static final String TRUE = "true"; //$NON-NLS-1$
  protected CCombo blockCombo;
  protected CCombo finalCombo;
  protected CCombo abstractCombo;
  protected CCombo substGroupCombo;
  protected CCombo nillableCombo;
  private boolean isTraversing = false;

  private String blockValues[] = { EMPTY, "#" + XSDConstants.ALL_ELEMENT_TAG, //$NON-NLS-1$
      XSDConstants.EXTENSION_ELEMENT_TAG, XSDConstants.RESTRICTION_ELEMENT_TAG,
      "substitution" }; //$NON-NLS-1$

  private String finalValues[] = { EMPTY, "#" + XSDConstants.ALL_ELEMENT_TAG, XSDConstants.EXTENSION_ELEMENT_TAG, //$NON-NLS-1$
      XSDConstants.RESTRICTION_ELEMENT_TAG }; //$NON-NLS-1$

  private String booleanValues[] = { EMPTY, TRUE, FALSE }; 

  protected void createContents(Composite parent)
  {
    composite = getWidgetFactory().createFlatFormComposite(parent);

    GridLayout gridLayout = new GridLayout();
    gridLayout.numColumns = 2;
    composite.setLayout(gridLayout);

    // ------------------------------------------------------------------
    // AbstractLabel
    // ------------------------------------------------------------------
    GridData data = new GridData();
    data.horizontalAlignment = GridData.HORIZONTAL_ALIGN_BEGINNING;
    data.grabExcessHorizontalSpace = false;

    CLabel abstractLabel = getWidgetFactory().createCLabel(composite, org.eclipse.wst.xsd.ui.internal.common.util.Messages._UI_LABEL_ABSTRACT + ":");
    abstractLabel.setLayoutData(data);

    // ------------------------------------------------------------------
    // AbstractCombo
    // ------------------------------------------------------------------
    data = new GridData();
    data.grabExcessHorizontalSpace = true;
    data.horizontalAlignment = GridData.FILL;
    abstractCombo = getWidgetFactory().createCCombo(composite);
    abstractCombo.setLayoutData(data);
    abstractCombo.setEditable(false);

    abstractCombo.setItems(booleanValues);
    abstractCombo.addSelectionListener(this);

    // ------------------------------------------------------------------
    // BlockLabel
    // ------------------------------------------------------------------
    data = new GridData();
    data.horizontalAlignment = GridData.HORIZONTAL_ALIGN_BEGINNING;
    data.grabExcessHorizontalSpace = false;

    CLabel blockLabel = getWidgetFactory().createCLabel(composite, org.eclipse.wst.xsd.ui.internal.common.util.Messages._UI_LABEL_BLOCK + ":");
    blockLabel.setLayoutData(data);

    // ------------------------------------------------------------------
    // BlockCombo
    // ------------------------------------------------------------------
    data = new GridData();
    data.grabExcessHorizontalSpace = true;
    data.horizontalAlignment = GridData.FILL;
    blockCombo = getWidgetFactory().createCCombo(composite);
    blockCombo.setLayoutData(data);
    blockCombo.setEditable(false);

    blockCombo.setItems(blockValues);
    blockCombo.addSelectionListener(this);

    // ------------------------------------------------------------------
    // FinalLabel
    // ------------------------------------------------------------------
    data = new GridData();
    data.horizontalAlignment = GridData.HORIZONTAL_ALIGN_BEGINNING;
    data.grabExcessHorizontalSpace = false;

    CLabel finalLabel = getWidgetFactory().createCLabel(composite, org.eclipse.wst.xsd.ui.internal.common.util.Messages._UI_LABEL_FINAL + ":");
    finalLabel.setLayoutData(data);

    // ------------------------------------------------------------------
    // FinalCombo
    // ------------------------------------------------------------------
    data = new GridData();
    data.grabExcessHorizontalSpace = true;
    data.horizontalAlignment = GridData.FILL;
    finalCombo = getWidgetFactory().createCCombo(composite);
    finalCombo.setLayoutData(data);
    finalCombo.setEditable(false);

    finalCombo.setItems(finalValues);
    finalCombo.addSelectionListener(this);

    // ------------------------------------------------------------------
    // Substitution Group Label
    // ------------------------------------------------------------------
    data = new GridData();
    data.horizontalAlignment = GridData.HORIZONTAL_ALIGN_BEGINNING;
    data.grabExcessHorizontalSpace = false;

    CLabel subGroupLabel = getWidgetFactory().createCLabel(composite, org.eclipse.wst.xsd.ui.internal.common.util.Messages._UI_LABEL_SUBSTITUTION_GROUP + ":");
    subGroupLabel.setLayoutData(data);

    // ------------------------------------------------------------------
    // Substitution Group Combo
    // ------------------------------------------------------------------
    data = new GridData();
    data.grabExcessHorizontalSpace = true;
    data.horizontalAlignment = GridData.FILL;
    substGroupCombo = getWidgetFactory().createCCombo(composite);
    substGroupCombo.setLayoutData(data);
    substGroupCombo.setEditable(true);
    substGroupCombo.addSelectionListener(this);
    substGroupCombo.addListener(SWT.Traverse, this);
    applyAllListeners(substGroupCombo);
    
    // ------------------------------------------------------------------
    // Nillable Label
    // ------------------------------------------------------------------
    data = new GridData();
    data.horizontalAlignment = GridData.HORIZONTAL_ALIGN_BEGINNING;
    data.grabExcessHorizontalSpace = false;

    CLabel nillableLabel = getWidgetFactory().createCLabel(composite, org.eclipse.wst.xsd.ui.internal.common.util.Messages._UI_LABEL_NILLABLE + ":");
    nillableLabel.setLayoutData(data);

    // ------------------------------------------------------------------
    // NillableCombo
    // ------------------------------------------------------------------
    data = new GridData();
    data.grabExcessHorizontalSpace = true;
    data.horizontalAlignment = GridData.FILL;
    nillableCombo = getWidgetFactory().createCCombo(composite);
    nillableCombo.setLayoutData(data);
    nillableCombo.setEditable(false);

    nillableCombo.setItems(booleanValues);
    nillableCombo.addSelectionListener(this);
    
  }

  public void doHandleEvent(Event e)
  {
    if (e.widget == substGroupCombo)
    {
      if (e.type == SWT.Traverse) {
        if (e.detail == SWT.TRAVERSE_ARROW_NEXT || e.detail == SWT.TRAVERSE_ARROW_PREVIOUS)
        {
          isTraversing = true;
          return;
        }
      }
		
      XSDElementDeclaration eleDec = (XSDElementDeclaration) input;
      String value = substGroupCombo.getText();
      
        String oldValue = eleDec.getElement().getAttribute(XSDConstants.SUBSTITUTIONGROUP_ATTRIBUTE);
        if (oldValue == null)
          oldValue = EMPTY;
        if (value.equals(oldValue))
          return;
  
        UpdateAttributeValueCommand command = new UpdateAttributeValueCommand(eleDec.getElement(), XSDConstants.SUBSTITUTIONGROUP_ATTRIBUTE, value, org.eclipse.wst.xsd.ui.internal.common.util.Messages._UI_LABEL_SUBSTITUTION_GROUP);
        command.setDeleteIfEmpty(true);
        getCommandStack().execute(command);
      }
  }

  public void doWidgetDefaultSelected(SelectionEvent e)
  {
    if (e.widget == substGroupCombo)
    {
      String selection = substGroupCombo.getText();
      if (shouldPerformComboSelection(SWT.DefaultSelection, selection))
        handleWidgetSelection(e);
    } else
    {
      handleWidgetSelection(e);
    }
  }

  public void doWidgetSelected(SelectionEvent e)
  {
    if (e.widget == substGroupCombo)
    {
      String selection = substGroupCombo.getText();
      if (shouldPerformComboSelection(SWT.Selection, selection))
        handleWidgetSelection(e);
    } else
    {
      handleWidgetSelection(e);
    }
  }

  private void handleWidgetSelection(SelectionEvent e)
  {
    if (e.widget == blockCombo)
    {
      XSDElementDeclaration eleDec = (XSDElementDeclaration) input;
      String value = blockCombo.getText();

      UpdateAttributeValueCommand command = new UpdateAttributeValueCommand(eleDec.getElement(), XSDConstants.BLOCK_ATTRIBUTE, value, org.eclipse.wst.xsd.ui.internal.common.util.Messages._UI_LABEL_BLOCK);
      command.setDeleteIfEmpty(true);
      getCommandStack().execute(command);
    }
    else if (e.widget == finalCombo)
    {
      XSDElementDeclaration eleDec = (XSDElementDeclaration) input;
      String value = finalCombo.getText();

      UpdateAttributeValueCommand command = new UpdateAttributeValueCommand(eleDec.getElement(), XSDConstants.FINAL_ATTRIBUTE, value, org.eclipse.wst.xsd.ui.internal.common.util.Messages._UI_LABEL_FINAL);
      command.setDeleteIfEmpty(true);
      getCommandStack().execute(command);
    }
    else if (e.widget == abstractCombo)
    {
      XSDElementDeclaration eleDec = (XSDElementDeclaration) input;
      String value = abstractCombo.getText();
      UpdateAttributeValueCommand command = new UpdateAttributeValueCommand(eleDec.getElement(), XSDConstants.ABSTRACT_ATTRIBUTE, value, org.eclipse.wst.xsd.ui.internal.common.util.Messages._UI_LABEL_ABSTRACT);
      command.setDeleteIfEmpty(true);
      getCommandStack().execute(command);
    }
    else if (e.widget == substGroupCombo)
    {
      IEditorPart editor = getActiveEditor();
      if (editor == null) return;
      ComponentReferenceEditManager manager = (ComponentReferenceEditManager)editor.getAdapter(XSDSubstitutionGroupEditManager.class);

      String selection = substGroupCombo.getText();

      ComponentSpecification newValue;
      IComponentDialog dialog= null;
      if ( selection.equals(Messages._UI_ACTION_BROWSE))
      {
        dialog = manager.getBrowseDialog();
      }
      else if ( selection.equals(Messages._UI_ACTION_NEW))
      {
        dialog = manager.getNewDialog();
      }

      if (dialog != null)
      {
        if (dialog.createAndOpen() == Window.OK)
        {
          newValue = dialog.getSelectedComponent();
          manager.modifyComponentReference(input, newValue);
        }
        else
        {
          substGroupCombo.setText("");
        }
      }
      else //use the value from selected quickPick item
      {
        newValue = getComponentSpecFromQuickPickForValue(selection, manager);
        if (newValue != null)
          manager.modifyComponentReference(input, newValue);
      }
    }
    else if (e.widget == nillableCombo)
    {
        XSDElementDeclaration eleDec = (XSDElementDeclaration) input;
        String value = nillableCombo.getText();

        UpdateAttributeValueCommand command = new UpdateAttributeValueCommand(eleDec.getElement(), XSDConstants.NILLABLE_ATTRIBUTE, value, org.eclipse.wst.xsd.ui.internal.common.util.Messages._UI_LABEL_NILLABLE);
        command.setDeleteIfEmpty(true);
        getCommandStack().execute(command);
    }
  }

  public void refresh()
  {
    super.refresh();
    fillSubstitutionGroupCombo();
    setListenerEnabled(false);
    try
    {
      if (input instanceof XSDElementDeclaration)
      {
        XSDElementDeclaration eleDec = (XSDElementDeclaration) input;

        composite.setEnabled(!isReadOnly);
        if (eleDec.getContainer() instanceof XSDSchema) // global element
        {
          abstractCombo.setEnabled(true);
          finalCombo.setEnabled(true);
          substGroupCombo.setEnabled(true);
          nillableCombo.setEnabled(true);
        }
        else
        {
          abstractCombo.setEnabled(false);
          finalCombo.setEnabled(false);
          substGroupCombo.setEnabled(false);
          
          // Nillable when used in a local element declaration can't be set on
          // on elements that use @ref.
       	  nillableCombo.setEnabled(!eleDec.isElementDeclarationReference());
        }

        Element element = eleDec.getElement();
        String blockAttValue = element.getAttribute(XSDConstants.BLOCK_ATTRIBUTE);
        if (blockAttValue != null)
        {
          blockCombo.setText(blockAttValue);
        }
        else
        {
          blockCombo.setText(EMPTY);
        }
        // We should show the value of the attribute regardless if it is invalid
        // ie. as the user starts typing 'true' in the source, the properties view
        // should show the value dynamically
        if (element.hasAttribute(XSDConstants.NILLABLE_ATTRIBUTE))
        {
          String attrValue = element.getAttribute(XSDConstants.NILLABLE_ATTRIBUTE);
          nillableCombo.setText(attrValue);
        }
        else
        {
          nillableCombo.setText(EMPTY);
        }


        String finalAttValue = element.getAttribute(XSDConstants.FINAL_ATTRIBUTE);
        if (finalAttValue != null)
        {
          finalCombo.setText(finalAttValue);
        }
        else
        {
          finalCombo.setText(EMPTY);
        }

        if (element.hasAttribute(XSDConstants.ABSTRACT_ATTRIBUTE))
        {
          abstractCombo.setText(element.getAttribute(XSDConstants.ABSTRACT_ATTRIBUTE));
        }
        else
          abstractCombo.setText(EMPTY);

        if (element.hasAttribute(XSDConstants.SUBSTITUTIONGROUP_ATTRIBUTE))
        {
          substGroupCombo.setText(element.getAttribute(XSDConstants.SUBSTITUTIONGROUP_ATTRIBUTE));
        }
        else
        {
          substGroupCombo.setText(EMPTY);
        }
      }
    }
    catch (Exception e)
    {
    }
    setListenerEnabled(true);
  }
  
  private void fillSubstitutionGroupCombo()
  {
    IEditorPart editor = getActiveEditor();
    ComponentReferenceEditManager manager = (ComponentReferenceEditManager)editor.getAdapter(XSDSubstitutionGroupEditManager.class);
    if (manager != null)
    {
      ComponentSpecification[] items = manager.getQuickPicks();

      substGroupCombo.removeAll();
      substGroupCombo.add(Messages._UI_ACTION_BROWSE);
      substGroupCombo.add(Messages._UI_ACTION_NEW);
      for (int i = 0; i < items.length; i++)
      {
        substGroupCombo.add(items[i].getName());
      }
      // Add the current substitution group if needed
      XSDElementDeclaration namedComponent = ((XSDElementDeclaration) input).getSubstitutionGroupAffiliation();
      if (namedComponent != null)
      {
        ComponentSpecification ret = getComponentSpecFromQuickPickForValue(namedComponent.getName(), manager);
        if (ret == null)
        {
          substGroupCombo.add(namedComponent.getQName(xsdSchema));
        }
      }
    }
  }
  
  private ComponentSpecification getComponentSpecFromQuickPickForValue(String value, ComponentReferenceEditManager editManager)
  {
    if (editManager != null)
    {  
      ComponentSpecification[] quickPicks = editManager.getQuickPicks();
      if (quickPicks != null)
      {
        for (int i=0; i < quickPicks.length; i++)
        {
          ComponentSpecification componentSpecification = quickPicks[i];
          if (value != null && value.equals(componentSpecification.getName()))
          {
            return componentSpecification;
          }                
        }  
      }
    }
    return null;
  }
  
  private boolean shouldPerformComboSelection(int eventType, Object selectedItem)
  {
    // if traversing through combobox, don't automatically pop up
    // the browse and new dialog boxes
    boolean wasTraversing = isTraversing;
    if (isTraversing)
      isTraversing = false;

    // we only care about default selecting (hitting enter in combobox)
    // for browse.. and new.. otherwise, selection event will be fired
    if (eventType == SWT.DefaultSelection)
    {
      if (selectedItem instanceof String && ((Messages._UI_ACTION_BROWSE.equals(selectedItem) || Messages._UI_ACTION_NEW.equals(selectedItem))))
        return true;
      return false;
    }

    // if was traversing and got selection event, do nothing if it's 
    // browse.. or new..
    if (wasTraversing && selectedItem instanceof String)
    {
      if (Messages._UI_ACTION_BROWSE.equals(selectedItem) || Messages._UI_ACTION_NEW.equals(selectedItem))
      {
        return false;
      }
    }
    return true;
  }
  
  public void dispose()
  {
  	if (substGroupCombo != null && !substGroupCombo.isDisposed())
  	{
		substGroupCombo.removeListener(SWT.Traverse, this);
	}
	super.dispose();
  }

}
