/*
 *  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you 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.apache.directory.studio.ldapbrowser.common.dialogs;


import java.util.Iterator;

import org.apache.directory.studio.ldapbrowser.common.BrowserCommonActivator;
import org.apache.directory.studio.ldapbrowser.common.BrowserCommonConstants;
import org.apache.directory.studio.ldapbrowser.common.widgets.entryeditor.EntryEditorWidget;
import org.apache.directory.studio.ldapbrowser.common.widgets.entryeditor.EntryEditorWidgetActionGroup;
import org.apache.directory.studio.ldapbrowser.common.widgets.entryeditor.EntryEditorWidgetConfiguration;
import org.apache.directory.studio.ldapbrowser.common.widgets.entryeditor.EntryEditorWidgetUniversalListener;
import org.apache.directory.studio.ldapbrowser.common.widgets.entryeditor.OpenDefaultEditorAction;
import org.apache.directory.studio.ldapbrowser.core.events.AttributeDeletedEvent;
import org.apache.directory.studio.ldapbrowser.core.events.EmptyValueAddedEvent;
import org.apache.directory.studio.ldapbrowser.core.events.EmptyValueDeletedEvent;
import org.apache.directory.studio.ldapbrowser.core.events.EntryModificationEvent;
import org.apache.directory.studio.ldapbrowser.core.events.EventRegistry;
import org.apache.directory.studio.ldapbrowser.core.events.ValueAddedEvent;
import org.apache.directory.studio.ldapbrowser.core.events.ValueDeletedEvent;
import org.apache.directory.studio.ldapbrowser.core.events.ValueModifiedEvent;
import org.apache.directory.studio.ldapbrowser.core.events.ValueMultiModificationEvent;
import org.apache.directory.studio.ldapbrowser.core.model.AttributeHierarchy;
import org.apache.directory.studio.ldapbrowser.core.model.IAttribute;
import org.apache.directory.studio.ldapbrowser.core.model.IEntry;
import org.apache.directory.studio.ldapbrowser.core.model.IValue;
import org.apache.directory.studio.ldapbrowser.core.utils.CompoundModification;
import org.apache.directory.studio.ldapbrowser.core.utils.Utils;
import org.apache.directory.studio.ldifparser.model.LdifFile;
import org.apache.directory.studio.valueeditors.ValueEditorManager;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.contexts.IContextActivation;
import org.eclipse.ui.contexts.IContextService;


/**
 * Dialog to view and edit multi-valued attributes.
 *
 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
 */
public class MultivaluedDialog extends Dialog
{

    /** The dialog title. */
    private static final String DIALOG_TITLE = Messages.getString( "MultivaluedDialog.MultivaluedEditor" ); //$NON-NLS-1$

    /** The attribute hierarchy to edit. */
    private AttributeHierarchy workingAttributeHierarchy;

    /** The original attribute hierarchy. */
    private AttributeHierarchy referenceAttributeHierarchy;

    /** The entry editor widget configuration. */
    private MultiValuedEntryEditorConfiguration configuration;

    /** The entry edtior widget action group. */
    private EntryEditorWidgetActionGroup actionGroup;

    /** The entry editor widget. */
    private EntryEditorWidget mainWidget;

    /** The universal listener. */
    private MultiValuedEntryEditorUniversalListener universalListener;

    /** Token used to activate and deactivate shortcuts in the editor */
    private IContextActivation contextActivation;


    /**
     * Creates a new instance of MultivaluedDialog.
     * 
     * @param parentShell the parent shell
     * @param workingAttributeHierarchy the attribute hierarchy
     */
    public MultivaluedDialog( Shell parentShell, AttributeHierarchy attributeHierarchy )
    {
        super( parentShell );
        setShellStyle( getShellStyle() | SWT.RESIZE );
        this.referenceAttributeHierarchy = attributeHierarchy;

        // clone the entry and attribute hierarchy
        IEntry entry = attributeHierarchy.getEntry();
        String attributeDescription = attributeHierarchy.getAttributeDescription();
        IEntry clone = new CompoundModification().cloneEntry( entry );
        this.workingAttributeHierarchy = clone.getAttributeWithSubtypes( attributeDescription );
    }


    /**
     * @see org.eclipse.jface.window.Window#configureShell(org.eclipse.swt.widgets.Shell)
     */
    protected void configureShell( Shell shell )
    {
        super.configureShell( shell );
        shell.setText( DIALOG_TITLE );
        shell.setImage( BrowserCommonActivator.getDefault().getImage( BrowserCommonConstants.IMG_MULTIVALUEDEDITOR ) );
    }


    @Override
    protected void okPressed()
    {
        LdifFile diff = Utils
            .computeDiff( referenceAttributeHierarchy.getEntry(), workingAttributeHierarchy.getEntry() );
        if ( diff != null )
        {
            EventRegistry.suspendEventFiringInCurrentThread();
            IEntry entry = referenceAttributeHierarchy.getEntry();
            for ( IAttribute attribute : referenceAttributeHierarchy.getAttributes() )
            {
                entry.deleteAttribute( attribute );
            }
            for ( IAttribute attribute : workingAttributeHierarchy.getAttributes() )
            {
                entry.addAttribute( attribute );
            }
            EventRegistry.resumeEventFiringInCurrentThread();

            ValueMultiModificationEvent event = new ValueMultiModificationEvent( entry.getBrowserConnection(), entry );
            EventRegistry.fireEntryUpdated( event, this );
        }

        super.okPressed();
    }


    /**
     * @see org.eclipse.jface.window.Window#open()
     */
    public int open()
    {
        if ( workingAttributeHierarchy.getAttribute().getValueSize() == 0 )
        {
            workingAttributeHierarchy.getAttribute().addEmptyValue();
        }

        return super.open();
    }


    /**
     * @see org.eclipse.jface.dialogs.Dialog#close()
     */
    public boolean close()
    {
        boolean returnValue = super.close();
        if ( returnValue )
        {
            dispose();

            // cleanup attribute hierarchy after editing
            for ( Iterator<IAttribute> it = workingAttributeHierarchy.iterator(); it.hasNext(); )
            {
                IAttribute attribute = it.next();
                if ( attribute != null )
                {
                    // remove empty values
                    IValue[] values = attribute.getValues();
                    for ( int i = 0; i < values.length; i++ )
                    {
                        if ( values[i].isEmpty() )
                        {
                            attribute.deleteEmptyValue();
                        }
                    }

                    // delete attribute from entry if all values were deleted
                    if ( attribute.getValueSize() == 0 )
                    {
                        attribute.getEntry().deleteAttribute( attribute );
                    }
                }
            }
        }
        return returnValue;
    }


    /**
     * Disposes all widgets.
     */
    public void dispose()
    {
        if ( configuration != null )
        {
            universalListener.dispose();
            universalListener = null;
            mainWidget.dispose();
            mainWidget = null;
            actionGroup.deactivateGlobalActionHandlers();
            actionGroup.dispose();
            actionGroup = null;
            configuration.dispose();
            configuration = null;

            if ( contextActivation != null )
            {
                IContextService contextService = ( IContextService ) PlatformUI.getWorkbench().getAdapter(
                    IContextService.class );
                contextService.deactivateContext( contextActivation );
                contextActivation = null;
            }
        }
    }


    /**
     * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite)
     */
    protected Control createDialogArea( Composite parent )
    {
        Composite composite = ( Composite ) super.createDialogArea( parent );

        // create configuration
        configuration = new MultiValuedEntryEditorConfiguration();

        // create main widget
        mainWidget = new EntryEditorWidget( configuration );
        mainWidget.createWidget( composite );
        mainWidget.getViewer().getTree().setFocus();

        // create actions
        actionGroup = new EntryEditorWidgetActionGroup( mainWidget, configuration );
        actionGroup.fillToolBar( mainWidget.getToolBarManager() );
        actionGroup.fillMenu( mainWidget.getMenuManager() );
        actionGroup.fillContextMenu( mainWidget.getContextMenuManager() );
        IContextService contextService = ( IContextService ) PlatformUI.getWorkbench().getAdapter(
            IContextService.class );
        contextActivation = contextService.activateContext( BrowserCommonConstants.CONTEXT_DIALOGS );
        actionGroup.activateGlobalActionHandlers();

        // create the listener
        universalListener = new MultiValuedEntryEditorUniversalListener( mainWidget.getViewer(), configuration,
            actionGroup, actionGroup.getOpenDefaultEditorAction() );
        universalListener.setInput( workingAttributeHierarchy );

        // start edit mode if an empty value exists
        for ( Iterator<IAttribute> it = workingAttributeHierarchy.iterator(); it.hasNext(); )
        {
            IAttribute attribute = it.next();
            IValue[] values = attribute.getValues();
            for ( int i = 0; i < values.length; i++ )
            {
                IValue value = values[i];
                if ( value.isEmpty() )
                {
                    mainWidget.getViewer().setSelection( new StructuredSelection( value ), true );
                    if ( actionGroup.getOpenDefaultEditorAction().isEnabled() )
                    {
                        actionGroup.getOpenDefaultEditorAction().run();
                        break;
                    }
                }
            }
        }

        applyDialogFont( composite );
        return composite;
    }

    /**
     * A special listener for the {@link MultivaluedDialog}.
     */
    class MultiValuedEntryEditorUniversalListener extends EntryEditorWidgetUniversalListener
    {

        /**
         * Creates a new instance of MultiValuedEntryEditorUniversalListener.
         * 
         * @param treeViewer the tree viewer
         * @param configuration the configuration
         * @param actionGroup the action group
         * @param startEditAction the start edit action
         */
        public MultiValuedEntryEditorUniversalListener( TreeViewer treeViewer,
            EntryEditorWidgetConfiguration configuration, EntryEditorWidgetActionGroup actionGroup,
            OpenDefaultEditorAction startEditAction )
        {
            super( treeViewer, configuration, actionGroup, startEditAction );
        }


        /**
         * @see org.apache.directory.studio.ldapbrowser.common.widgets.entryeditor.EntryEditorWidgetUniversalListener#entryUpdated(org.apache.directory.studio.ldapbrowser.core.events.EntryModificationEvent)
         */
        public void entryUpdated( EntryModificationEvent event )
        {
            if ( viewer == null || viewer.getTree() == null || viewer.getTree().isDisposed() )
            {
                return;
            }

            if ( viewer.isCellEditorActive() )
            {
                viewer.cancelEditing();
            }

            viewer.refresh();

            // select added/modified value
            if ( event instanceof ValueAddedEvent )
            {
                ValueAddedEvent vaEvent = ( ValueAddedEvent ) event;
                viewer.setSelection( new StructuredSelection( vaEvent.getAddedValue() ), true );
                viewer.refresh();
            }
            else if ( event instanceof ValueModifiedEvent )
            {
                ValueModifiedEvent vmEvent = ( ValueModifiedEvent ) event;
                viewer.setSelection( new StructuredSelection( vmEvent.getNewValue() ), true );
            }
            else if ( event instanceof ValueDeletedEvent )
            {
                ValueDeletedEvent vdEvent = ( ValueDeletedEvent ) event;
                if ( vdEvent.getDeletedValue().getAttribute().getValueSize() > 0 )
                {
                    viewer.setSelection( new StructuredSelection(
                        vdEvent.getDeletedValue().getAttribute().getValues()[0] ), true );
                }
            }
            else if ( event instanceof EmptyValueAddedEvent )
            {
                viewer.refresh();
                EmptyValueAddedEvent evaEvent = ( EmptyValueAddedEvent ) event;
                viewer.setSelection( new StructuredSelection( evaEvent.getAddedValue() ), true );
                if ( startEditAction.isEnabled() )
                    startEditAction.run();
            }
            else if ( event instanceof EmptyValueDeletedEvent )
            {
                EmptyValueDeletedEvent evdEvent = ( EmptyValueDeletedEvent ) event;
                if ( viewer.getSelection().isEmpty() && evdEvent.getDeletedValue().getAttribute().getValueSize() > 0 )
                    viewer.setSelection( new StructuredSelection(
                        evdEvent.getDeletedValue().getAttribute().getValues()[0] ), true );
            }
            else if ( event instanceof AttributeDeletedEvent )
            {
            }
        }
    }

    /**
     * A special configuration for the {@link MultivaluedDialog}.
     */
    class MultiValuedEntryEditorConfiguration extends EntryEditorWidgetConfiguration
    {
        @Override
        public ValueEditorManager getValueEditorManager( TreeViewer viewer )
        {
            if ( valueEditorManager == null )
            {
                valueEditorManager = new ValueEditorManager( viewer.getTree(), false, false );
            }

            return valueEditorManager;
        }
    }
}
