/*
 *  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.apacheds.configuration.editor;


import org.apache.directory.server.config.beans.AuthenticationInterceptorBean;
import org.apache.directory.server.config.beans.InterceptorBean;
import org.apache.directory.server.config.beans.PasswordPolicyBean;
import org.apache.directory.studio.apacheds.configuration.ApacheDS2ConfigurationPlugin;
import org.apache.directory.studio.apacheds.configuration.ApacheDS2ConfigurationPluginConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
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.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Table;
import org.eclipse.ui.forms.DetailsPart;
import org.eclipse.ui.forms.IManagedForm;
import org.eclipse.ui.forms.MasterDetailsBlock;
import org.eclipse.ui.forms.SectionPart;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.Section;


/**
 * This class represents the Password Policies Master/Details Block used in the Password Policies Page.
 *
 * <pre>
 * +------------------------------------------------------------------------------------+
 * | .----------------------------------. .-------------------------------------------. |
 * | | All password Policies            | | Password Policy Details                   | |
 * | +----------------------------------+ +-------------------------------------------+ |
 * | | +---------------------+          | | Set the properties of the password Policy | |
 * | | | Default (enabled)   | [ Add  ] | |  [X] Enabled                              | |
 * | | |                     | [Delete] | |  ID :          [//////////]               | |
 * | | |                     |          | |  Description : [////////////////////////] | |
 * | | |                     |          | |  Attribute   : [////////////////////////] | |
 * | | |                     |          | .-------------------------------------------. |
 * | | |                     |          | | Quality                                   | |
 * | | |                     |          | +-------------------------------------------+ |
 * | | |                     |          | | Check quality : [=======================] | |
 * | | |                     |          | | Validator :     [///////////////////////] | |
 * | | |                     |          | | [X] Enable Minimum Length                 | |
 * | | |                     |          | |   Number of chars : [NNN]                 | |
 * | | |                     |          | | [X] Enable Maximum Length                 | |
 * | | |                     |          | |   Number of chars : [NNN]                 | |
 * | | |                     |          | .-------------------------------------------. |
 * | | |                     |          | | Expiration                                | |
 * | | |                     |          | +-------------------------------------------+ |
 * | | |                     |          | | Minimum age (seconds): [NNN]              | |
 * | | |                     |          | | Maximum age (seconds): [NNN]              | |
 * | | |                     |          | | [X] Enable Expire Warning                 | |
 * | | |                     |          | |   Number of seconds  : [NNN]              | |
 * | | |                     |          | | [X] Enable Grace Authentication Limit     | |
 * | | |                     |          | |   Number of times    : [NNN]              | |
 * | | |                     |          | | [X] Enable Grace Expire                   | |
 * | | |                     |          | |   Interval (seconds) : [NNN]              | |
 * | | |                     |          | .-------------------------------------------. |
 * | | |                     |          | | Options                                   | |
 * | | |                     |          | +-------------------------------------------+ |
 * | | |                     |          | | [X] Enable Must Change                    | |
 * | | |                     |          | | [X] Enable Allow User Change              | |
 * | | |                     |          | | [X] Enable Safe Modify                    | |
 * | | |                     |          | .-------------------------------------------. |
 * | | |                     |          | | Lockout                                   | |
 * | | |                     |          | +-------------------------------------------+ |
 * | | |                     |          | | [X] Enable Lockout                        | |
 * | | |                     |          | |   Lockout duration (seconds)   : [NNN]    | |
 * | | |                     |          | |   Maximum Consecutive Failures : [NNN]    | |
 * | | |                     |          | |   Failure Count Interval       : [NNN]    | |
 * | | |                     |          | | [X] Enable Maximum Idle                   | |
 * | | |                     |          | |   Intervals                    : [NNN]    | |
 * | | |                     |          | | [X] Enable In History                     | |
 * | | |                     |          | |   Used passwords stored in Hist: [NNN]    | |
 * | | |                     |          | | [X] Delay                                 | |
 * | | |                     |          | |   Minimum delay (seconds)      : [NNN]    | |
 * | | +---------------------+          | |   Maximum delay (seconds)      : [NNN]    | |
 * | +----------------------------------+ +-------------------------------------------+ |
 * +------------------------------------------------------------------------------------+
 * </pre>
 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
 */
public class PasswordPoliciesMasterDetailsBlock extends MasterDetailsBlock
{
    private static final String NEW_ID = Messages.getString( "PasswordPoliciesMasterDetailsBlock.PasswordPolicyNewId" ); //$NON-NLS-1$

    private static final String AUTHENTICATION_INTERCEPTOR_ID = "authenticationInterceptor";

    /** The associated page */
    private PasswordPoliciesPage page;

    /** The Details Page */
    private PasswordPolicyDetailsPage detailsPage;

    // UI Fields
    private TableViewer viewer;
    private Button addButton;
    private Button deleteButton;


    /**
     * Creates a new instance of PasswordPoliciesMasterDetailsBlock.
     *
     * @param page the associated page
     */
    public PasswordPoliciesMasterDetailsBlock( PasswordPoliciesPage page )
    {
        this.page = page;
    }


    /**
     * {@inheritDoc}
     */
    public void createContent( IManagedForm managedForm )
    {
        super.createContent( managedForm );

        this.sashForm.setWeights( new int[]
            { 40, 60 } );
    }


    /**
     * {@inheritDoc}
     */
    protected void createMasterPart( final IManagedForm managedForm, Composite parent )
    {
        FormToolkit toolkit = managedForm.getToolkit();

        // Creating the Section
        Section section = toolkit.createSection( parent, Section.TITLE_BAR );
        section.setText( Messages.getString( "PasswordPoliciesMasterDetailsBlock.AllPasswordPolicies" ) ); //$NON-NLS-1$
        section.marginWidth = 10;
        section.marginHeight = 5;
        Composite client = toolkit.createComposite( section, SWT.WRAP );
        GridLayout layout = new GridLayout();
        layout.numColumns = 2;
        layout.makeColumnsEqualWidth = false;
        layout.marginWidth = 2;
        layout.marginHeight = 2;
        client.setLayout( layout );
        toolkit.paintBordersFor( client );
        section.setClient( client );

        // Creating the Table and Table Viewer
        Table table = toolkit.createTable( client, SWT.NULL );
        GridData gd = new GridData( SWT.FILL, SWT.FILL, true, true, 1, 2 );
        gd.heightHint = 20;
        gd.widthHint = 100;
        table.setLayoutData( gd );
        final SectionPart spart = new SectionPart( section );
        managedForm.addPart( spart );
        viewer = new TableViewer( table );
        viewer.addSelectionChangedListener( new ISelectionChangedListener()
        {
            public void selectionChanged( SelectionChangedEvent event )
            {
                managedForm.fireSelectionChanged( spart, event.getSelection() );
            }
        } );
        viewer.setContentProvider( new ArrayContentProvider() );
        viewer.setLabelProvider( new LabelProvider()
        {
            public String getText( Object element )
            {
                if ( element instanceof PasswordPolicyBean )
                {
                    PasswordPolicyBean passwordPolicy = ( PasswordPolicyBean ) element;

                    if ( passwordPolicy.isEnabled() )
                    {
                        return NLS.bind( "{0} (enabled)", passwordPolicy.getPwdId() );
                    }
                    else
                    {
                        return NLS.bind( "{0} (disabled)", passwordPolicy.getPwdId() );
                    }
                }

                return super.getText( element );
            }


            public Image getImage( Object element )
            {
                if ( element instanceof PasswordPolicyBean )
                {
                    PasswordPolicyBean passwordPolicy = ( PasswordPolicyBean ) element;

                    if ( PasswordPoliciesPage.isDefaultPasswordPolicy( passwordPolicy ) )
                    {
                        return ApacheDS2ConfigurationPlugin.getDefault().getImage(
                            ApacheDS2ConfigurationPluginConstants.IMG_PASSWORD_POLICY_DEFAULT );
                    }
                    else
                    {
                        return ApacheDS2ConfigurationPlugin.getDefault().getImage(
                            ApacheDS2ConfigurationPluginConstants.IMG_PASSWORD_POLICY );
                    }
                }

                return super.getImage( element );
            }
        } );
        
        viewer.setComparator( new ViewerComparator()
        {
            public int compare( Viewer viewer, Object e1, Object e2 )
            {
                if ( ( e1 instanceof PasswordPolicyBean ) && ( e2 instanceof PasswordPolicyBean ) )
                {
                    PasswordPolicyBean passwordPolicy1 = ( PasswordPolicyBean ) e1;
                    PasswordPolicyBean passwordPolicy2 = ( PasswordPolicyBean ) e2;

                    String passwordPolicy1Id = passwordPolicy1.getPwdId();
                    String passwordPolicy2Id = passwordPolicy2.getPwdId();

                    if ( ( passwordPolicy1Id != null ) && ( passwordPolicy2Id != null ) )
                    {
                        return passwordPolicy1Id.compareTo( passwordPolicy2Id );
                    }
                }

                return super.compare( viewer, e1, e2 );
            }
        } );

        // Creating the button(s)
        addButton = toolkit.createButton( client,
            Messages.getString( "PasswordPoliciesMasterDetailsBlock.Add" ), SWT.PUSH ); //$NON-NLS-1$
        addButton.setLayoutData( new GridData( SWT.FILL, SWT.BEGINNING, false, false ) );

        deleteButton = toolkit.createButton( client,
            Messages.getString( "PasswordPoliciesMasterDetailsBlock.Delete" ), SWT.PUSH ); //$NON-NLS-1$
        deleteButton.setEnabled( false );
        deleteButton.setLayoutData( new GridData( SWT.FILL, SWT.BEGINNING, false, false ) );

        initFromInput();
        addListeners();
    }


    /**
     * Initializes the page with the Editor input.
     */
    private void initFromInput()
    {
        AuthenticationInterceptorBean authenticationInterceptor = getAuthenticationInterceptor();

        if ( authenticationInterceptor != null )
        {
            viewer.setInput( authenticationInterceptor.getPasswordPolicies() );
        }
        else
        {
            viewer.setInput( null );
        }
    }


    /**
     * Refreshes the UI.
     */
    public void refreshUI()
    {
        initFromInput();
        viewer.refresh();
    }


    /**
     * Gets the authentication interceptor.
     *
     * @return the authentication interceptor
     */
    private AuthenticationInterceptorBean getAuthenticationInterceptor()
    {
        // Looking for the authentication interceptor
        for ( InterceptorBean interceptor : page.getConfigBean().getDirectoryServiceBean().getInterceptors() )
        {
            if ( AUTHENTICATION_INTERCEPTOR_ID.equalsIgnoreCase( interceptor.getInterceptorId() )
                && ( interceptor instanceof AuthenticationInterceptorBean ) )
            {
                return ( AuthenticationInterceptorBean ) interceptor;
            }
        }

        return null;
    }


    /**
     * Add listeners to UI fields.
     */
    private void addListeners()
    {
        viewer.addSelectionChangedListener( new ISelectionChangedListener()
        {
            public void selectionChanged( SelectionChangedEvent event )
            {
                viewer.refresh();

                // Getting the selection of the table viewer
                StructuredSelection selection = ( StructuredSelection ) viewer.getSelection();

                // Delete button is enabled when something is selected
                deleteButton.setEnabled( !selection.isEmpty() );

                // Delete button is not enabled in the case of the system partition
                if ( !selection.isEmpty() )
                {
                    PasswordPolicyBean passwordPolicy = ( PasswordPolicyBean ) selection.getFirstElement();
                    if ( PasswordPoliciesPage.isDefaultPasswordPolicy( passwordPolicy ) )
                    {
                        deleteButton.setEnabled( false );
                    }
                }
            }
        } );

        addButton.addSelectionListener( new SelectionAdapter()
        {
            public void widgetSelected( SelectionEvent e )
            {
                addNewPasswordPolicy();
            }
        } );

        deleteButton.addSelectionListener( new SelectionAdapter()
        {
            public void widgetSelected( SelectionEvent e )
            {
                deleteSelectedPasswordPolicy();
            }
        } );
    }


    /**
     * This method is called when the 'Add' button is clicked.
     */
    private void addNewPasswordPolicy()
    {
        // Getting a new ID for the password policy
        String newId = getNewId();

        // Creating and configuring the new password policy
        PasswordPolicyBean newPasswordPolicy = new PasswordPolicyBean();
        newPasswordPolicy.setPwdId( newId );
        newPasswordPolicy.setPwdMaxAge( 0 );
        newPasswordPolicy.setPwdFailureCountInterval( 30 );
        newPasswordPolicy.setPwdAttribute( "userPassword" );
        newPasswordPolicy.setPwdMaxFailure( 5 );
        newPasswordPolicy.setPwdLockout( true );
        newPasswordPolicy.setPwdMustChange( false );
        newPasswordPolicy.setPwdLockoutDuration( 0 );
        newPasswordPolicy.setPwdMinLength( 5 );
        newPasswordPolicy.setPwdInHistory( 5 );
        newPasswordPolicy.setPwdExpireWarning( 600 );
        newPasswordPolicy.setPwdMinAge( 0 );
        newPasswordPolicy.setPwdAllowUserChange( true );
        newPasswordPolicy.setPwdGraceAuthNLimit( 5 );
        newPasswordPolicy.setPwdCheckQuality( 1 );
        newPasswordPolicy.setPwdMaxLength( 0 );
        newPasswordPolicy.setPwdGraceExpire( 0 );
        newPasswordPolicy.setPwdMinDelay( 0 );
        newPasswordPolicy.setPwdMaxDelay( 0 );
        newPasswordPolicy.setPwdMaxIdle( 0 );

        // Adding the new password policy to the authentication interceptor
        getAuthenticationInterceptor().addPasswordPolicies( newPasswordPolicy );

        // Updating the UI and editor
        viewer.refresh();
        viewer.setSelection( new StructuredSelection( newPasswordPolicy ) );
        setEditorDirty();
    }


    /**
     * Gets a new ID for a new password policy.
     *
     * @return 
     *      a new ID for a new password policy
     */
    private String getNewId()
    {
        int counter = 1;
        String name = NEW_ID;
        boolean ok = false;

        while ( !ok )
        {
            ok = true;
            name = NEW_ID + counter;

            for ( PasswordPolicyBean passwordPolicy : getAuthenticationInterceptor().getPasswordPolicies() )
            {
                if ( passwordPolicy.getPwdId().equalsIgnoreCase( name ) )
                {
                    ok = false;
                }
            }
            counter++;
        }

        return name;
    }


    /**
     * This method is called when the 'Delete' button is clicked.
     */
    private void deleteSelectedPasswordPolicy()
    {
        StructuredSelection selection = ( StructuredSelection ) viewer.getSelection();
        if ( !selection.isEmpty() )
        {
            PasswordPolicyBean passwordPolicy = ( PasswordPolicyBean ) selection.getFirstElement();
            if ( !PasswordPoliciesPage.isDefaultPasswordPolicy( passwordPolicy ) )
            {
                if ( MessageDialog
                    .openConfirm(
                        page.getManagedForm().getForm().getShell(),
                        Messages.getString( "PasswordPoliciesMasterDetailsBlock.ConfirmDelete" ), //$NON-NLS-1$
                        NLS.bind(
                            Messages.getString( "PasswordPoliciesMasterDetailsBlock.AreYouSureDeletePasswordPolicy" ), passwordPolicy.getPwdId() ) ) ) //$NON-NLS-1$
                {
                    getAuthenticationInterceptor().removePasswordPolicies( passwordPolicy );
                    setEditorDirty();
                }
            }
        }
    }


    /**
     * {@inheritDoc}
     */
    protected void registerPages( DetailsPart detailsPart )
    {
        detailsPage = new PasswordPolicyDetailsPage( this );
        detailsPart.registerPage( PasswordPolicyBean.class, detailsPage );
    }


    /**
     * {@inheritDoc}
     */
    protected void createToolBarActions( IManagedForm managedForm )
    {
        // No toolbar needed.
    }


    /**
     * Sets the Editor as dirty.
     */
    public void setEditorDirty()
    {
        ( ( ServerConfigurationEditor ) page.getEditor() ).setDirty( true );
        viewer.refresh();
    }


    /**
     * Saves the necessary elements to the input model.
     */
    public void save()
    {
        detailsPage.commit( true );
    }
}
