/*******************************************************************************
 * Copyright (c) 2000, 2010 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
 *     Brock Janiczak <brockj@tpg.com.au> - Bug 166333 [Wizards] Show diff in CVS commit dialog
 *     Brock Janiczak <brockj@tpg.com.au> - Bug 190674 Conflicting resources message lost when typing in commit wizard
 *******************************************************************************/
package org.eclipse.team.internal.ccvs.ui.wizards;

import java.util.Arrays;

import org.eclipse.compare.*;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.mapping.ResourceTraversal;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.dialogs.*;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.wizard.*;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.*;
import org.eclipse.team.core.diff.*;
import org.eclipse.team.internal.ccvs.ui.*;
import org.eclipse.team.internal.ccvs.ui.IHelpContextIds;
import org.eclipse.team.internal.ccvs.ui.mappings.ChangeSetComparator;
import org.eclipse.team.internal.core.subscribers.ActiveChangeSet;
import org.eclipse.team.internal.core.subscribers.ChangeSet;
import org.eclipse.team.internal.ui.*;
import org.eclipse.team.ui.synchronize.*;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.PageBook;

/**
 * This wizard page shows a preview of the commit operation and allows entering
 * a commit comment.
 */
public class CommitWizardCommitPage extends WizardPage implements IPropertyChangeListener {
    
    public static final String SHOW_COMPARE = "ShowCompare"; //$NON-NLS-1$
    private static final String H_WEIGHT_1 = "HWeight1"; //$NON-NLS-1$
    private static final String H_WEIGHT_2 = "HWeight2"; //$NON-NLS-1$
    private static final String V_WEIGHT_1 = "VWeight1"; //$NON-NLS-1$
    private static final String V_WEIGHT_2 = "VWeight2"; //$NON-NLS-1$
    

	private final CommitCommentArea fCommentArea;
    
    private ISynchronizePageConfiguration fConfiguration;
    
    protected final CommitWizard fWizard;
    
	private ParticipantPagePane fPagePane;
    private PageBook bottomChild;

	private boolean fHasConflicts;

	private boolean fIsEmpty;
    
	private SashForm horizontalSash;
	private SashForm verticalSash;
	private Splitter placeholder;
	private boolean showCompare;
	
    public CommitWizardCommitPage(IResource [] resources, CommitWizard wizard) {
        
        super(CVSUIMessages.CommitWizardCommitPage_0); 
        setTitle(CVSUIMessages.CommitWizardCommitPage_0); 
        setDescription(CVSUIMessages.CommitWizardCommitPage_2); 
        
        fWizard= wizard;
        fCommentArea= new CommitCommentArea();
        fCommentArea.setProposedComment(getProposedComment(resources));
        if (resources.length > 0)
            fCommentArea.setProject(resources[0].getProject());
        fWizard.getDiffTree().addDiffChangeListener(new IDiffChangeListener() {
			public void propertyChanged(IDiffTree tree, int property, IPath[] paths) {
				// ignore property changes
			}
			public void diffsChanged(IDiffChangeEvent event, IProgressMonitor monitor) {
				Utils.syncExec(new Runnable() {
					public void run() {
						updateEnablements();
					}
				}, CommitWizardCommitPage.this.getControl());
			}
		});
    }
    
    /* (non-Javadoc)
     * @see org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite)
     */
    public void createControl(Composite parent) {
        initializeDialogUnits(parent);
        Dialog.applyDialogFont(parent);
        final PixelConverter converter= new PixelConverter(parent);
        
        final Composite composite= new Composite(parent, SWT.NONE);
        composite.setLayout(SWTUtils.createGridLayout(1, converter, SWTUtils.MARGINS_DEFAULT));
        // set F1 help
        PlatformUI.getWorkbench().getHelpSystem().setHelp(composite, IHelpContextIds.COMMIT_COMMENT_PAGE);
        
        
        horizontalSash = new SashForm(composite, SWT.HORIZONTAL);
        horizontalSash.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
        
        verticalSash = new SashForm(horizontalSash, SWT.VERTICAL);
        verticalSash.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));

        createCommentArea(verticalSash, converter);

        placeholder = new Splitter(horizontalSash, SWT.VERTICAL /*any*/);
        placeholder.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));

        createChangesArea(verticalSash, converter);

        IDialogSettings section = getDialogSettings().getSection(CommitWizard.COMMIT_WIZARD_DIALOG_SETTINGS);
		showCompare = section == null ? false : section.getBoolean(SHOW_COMPARE);
		int vWeight1 = 50;
		int vWeight2 = 50;
		if (section != null) {
			try {
				vWeight1 = section.getInt(V_WEIGHT_1);
				vWeight2 = section.getInt(V_WEIGHT_2);
			} catch (NumberFormatException e) {
			}
		}
		
		int hWeight1 = 35;
		int hWeight2 = 65;
		if (section != null) {
			try {
				hWeight1 = section.getInt(H_WEIGHT_1);
				hWeight2 = section.getInt(H_WEIGHT_2);
			} catch (NumberFormatException e) {
			}
		}

		if (!showCompare) {
			horizontalSash.setMaximizedControl(verticalSash);
		}
		
		verticalSash.setWeights(new int[] {vWeight1, vWeight2});
		horizontalSash.setWeights(new int[] {hWeight1, hWeight2});
		
        //fSashForm.setWeights(weights);
        Dialog.applyDialogFont(parent);
        setControl(composite);
        
        fCommentArea.setFocus();
        
        validatePage(false);
    }
    
    private void createCommentArea(Composite parent, PixelConverter converter) {
        Composite c = new Composite(parent, SWT.NONE);
        c.setLayout(GridLayoutFactory.fillDefaults().margins(0, 0).create());
        
        fCommentArea.createArea(c);
        GridData gd = SWTUtils.createGridData(SWT.DEFAULT, SWT.DEFAULT, SWT.FILL, SWT.FILL, true, true);
        fCommentArea.getComposite().setLayoutData(gd);
        fCommentArea.addPropertyChangeListener(this);
        
        createPlaceholder(c);
    }
    
    private void createChangesArea(Composite parent, PixelConverter converter) {
        
        ISynchronizeParticipant participant= fWizard.getParticipant();
        int size = fWizard.getDiffTree().getAffectedResources().length;
        if (size > getFileDisplayThreshold()) {
            // Create a page book to allow eventual inclusion of changes
            bottomChild = new PageBook(parent, SWT.NONE);
            bottomChild.setLayoutData(SWTUtils.createGridData(SWT.DEFAULT, SWT.DEFAULT, SWT.FILL, SWT.FILL, true, false));
            // Create composite for showing the reason for not showing the changes and a button to show them
            Composite changeDesc = new Composite(bottomChild, SWT.NONE);
            changeDesc.setLayout(SWTUtils.createGridLayout(1, converter, SWTUtils.MARGINS_NONE));
            SWTUtils.createLabel(changeDesc, NLS.bind(CVSUIMessages.CommitWizardCommitPage_1, new String[] { Integer.toString(size), Integer.toString(getFileDisplayThreshold()) })); 
            Button showChanges = new Button(changeDesc, SWT.PUSH);
            showChanges.setText(CVSUIMessages.CommitWizardCommitPage_5); 
            showChanges.addSelectionListener(new SelectionAdapter() {
                public void widgetSelected(SelectionEvent e) {
                    showChangesPane();
                }
            });
            showChanges.setLayoutData(new GridData());
            bottomChild.showPage(changeDesc);
            // Hide compare pane
            horizontalSash.setMaximizedControl(verticalSash);
        } else {
            final Composite composite= new Composite(parent, SWT.NONE);
            composite.setLayout(SWTUtils.createGridLayout(1, converter, SWTUtils.MARGINS_NONE));
            composite.setLayoutData(SWTUtils.createGridData(SWT.DEFAULT, SWT.DEFAULT, SWT.FILL, SWT.FILL, true, true));
            
            Control c = createChangesPage(composite, participant);
            c.setLayoutData(SWTUtils.createHVFillGridData());
        }
    }

    protected void showChangesPane() {
        Control c = createChangesPage(bottomChild, fWizard.getParticipant());
        bottomChild.setLayoutData(SWTUtils.createGridData(SWT.DEFAULT, SWT.DEFAULT, SWT.FILL, SWT.FILL, true, true));
        bottomChild.showPage(c);
        // Restore compare pane. It has been hidden when file display threshold was reached.
        if (showCompare) {
        	horizontalSash.setMaximizedControl(null);
        }
        Dialog.applyDialogFont(getControl());
        ((Composite)getControl()).layout();
    }

    private Control createChangesPage(final Composite composite, ISynchronizeParticipant participant) {
        fConfiguration= participant.createPageConfiguration();
		CompareConfiguration cc = new CompareConfiguration();
		cc.setLeftEditable(false);
		cc.setRightEditable(false);
		ParticipantPageCompareEditorInput input = new CommitWizardParticipantPageCompareEditorInput(cc, fConfiguration, participant);
		Control control = input.createContents(composite);
		control.setLayoutData(new GridData(GridData.FILL_BOTH));
		return control;
    }
    
	private class CommitWizardParticipantPageCompareEditorInput extends
			ParticipantPageCompareEditorInput {
		public CommitWizardParticipantPageCompareEditorInput(
				CompareConfiguration cc,
				ISynchronizePageConfiguration configuration,
				ISynchronizeParticipant participant) {
			super(cc, configuration, participant);
		}

		protected boolean isOfferToRememberParticipant() {
			return false;
		}

		protected CompareViewerSwitchingPane createContentViewerSwitchingPane(
				Splitter parent, int style, CompareEditorInput cei) {
			return super.createContentViewerSwitchingPane(placeholder, style, cei);
		}

		protected void setPageDescription(String title) {
			super.setPageDescription(TeamUIMessages.ParticipantPageSaveablePart_0);
		}
    }
    
	private int getFileDisplayThreshold() {
        return CVSUIPlugin.getPlugin().getPreferenceStore().getInt(ICVSUIConstants.PREF_COMMIT_FILES_DISPLAY_THRESHOLD);
    }

    /* (non-Javadoc)
	 * @see org.eclipse.jface.dialogs.DialogPage#dispose()
	 */
	public void dispose() {
		super.dispose();
		// Disposing of the page pane will dispose of the page and the configuration
		if (fPagePane != null)
			fPagePane.dispose();
	}
	
    private void createPlaceholder(final Composite composite) {
        final Composite placeholder= new Composite(composite, SWT.NONE);
        placeholder.setLayoutData(new GridData(SWT.DEFAULT, convertHorizontalDLUsToPixels(IDialogConstants.VERTICAL_SPACING) /3));
    }
    
    public String getComment(Shell shell) {
        return fCommentArea.getCommentWithPrompt(shell);
    }
    
    /* (non-Javadoc)
     * @see org.eclipse.jface.wizard.WizardPage#isPageComplete()
     */
    public boolean isPageComplete() {
		/* if empty comment is not allowed (see bug 114678) */
		final IPreferenceStore store = CVSUIPlugin.getPlugin()
				.getPreferenceStore();
		final String allowEmptyComment = store
				.getString(ICVSUIConstants.PREF_ALLOW_EMPTY_COMMIT_COMMENTS);
		if (allowEmptyComment.equals(MessageDialogWithToggle.NEVER)) {
			/* but is empty */
			final String comment = fCommentArea.getComment(false);
			if (comment.equals("")) { //$NON-NLS-1$
				return false; // then the page is not complete
			}
		}
		return super.isPageComplete();
	}
    
    /*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.jface.dialogs.DialogPage#setVisible(boolean)
	 */
    public void setVisible(boolean visible) {
        super.setVisible(visible);
        expand();
        if (visible && fConfiguration != null) {
            final Viewer viewer= fConfiguration.getPage().getViewer();
            viewer.refresh();
        }
        updateEnablements();
        setFocus();
    }

    protected void expand() {
        if (fConfiguration != null) {
            final Viewer viewer= fConfiguration.getPage().getViewer();
            if (viewer instanceof TreeViewer) {
            	try {
    	        	viewer.getControl().setRedraw(false);
    	            ((TreeViewer)viewer).expandAll();
            	} finally {
            		viewer.getControl().setRedraw(true);
            	}
            }
        }
    }
    
	/*
	 * Expand the sync elements and update the page enablement
	 */
	protected void updateForModelChange() {
        Control control = getControl();
        if (control == null || control.isDisposed()) return;
		expand();
		updateEnablements();
	}
	
	public void updateEnablements() {
        if (fConfiguration != null) {
        	fHasConflicts = false;
        	fIsEmpty = false;
        	
			if (fWizard.hasConflicts()) {
    			fHasConflicts = true;
    		}
			if (!fWizard.hasOutgoingChanges()) {
    			fIsEmpty = true;
    		}
        }
        
		validatePage(false);
	}

	boolean validatePage(boolean setMessage) {
        if (fCommentArea != null && fCommentArea.getComment(false).length() == 0) {
            final IPreferenceStore store= CVSUIPlugin.getPlugin().getPreferenceStore();
            final String value= store.getString(ICVSUIConstants.PREF_ALLOW_EMPTY_COMMIT_COMMENTS);
            if (MessageDialogWithToggle.NEVER.equals(value)) {
                setPageComplete(false);
                if (setMessage)
                    setErrorMessage(CVSUIMessages.CommitWizardCommitPage_3); 
                return false;
            }
        }
        
        if (fHasConflicts) {
			setErrorMessage(CVSUIMessages.CommitWizardCommitPage_4); 
			setPageComplete(false);
			return false;
		}
		if (fIsEmpty) {
			// No need for a message as it should be obvious that there are no resources to commit
			setErrorMessage(null);
			setPageComplete(false);
			return false;
		}
        
        setPageComplete(true);
        setErrorMessage(null);
        return true;
    }
    
    public void setFocus() {
        fCommentArea.setFocus();
        validatePage(true);
    }
    
    protected IWizardContainer getContainer() {
        return super.getContainer();
    }

	ResourceTraversal[] getTraversalsToCommit() {
		return fWizard.getParticipant().getContext().getScope().getTraversals();
    }

    /* (non-Javadoc)
     * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
     */
    public void propertyChange(PropertyChangeEvent event) {
        
        if (event.getProperty().equals(CommitCommentArea.OK_REQUESTED)) {
            final IWizardContainer container= getContainer();
            if (container instanceof WizardDialog) {
                final WizardDialog dialog= (WizardDialog)container;
                if (getWizard().canFinish()) {
                    try {
                        getWizard().performFinish();
                    } finally {
                        dialog.close();
                    }
                }
            }
        }
        if (event.getProperty().equals(CommitCommentArea.COMMENT_MODIFIED)) {
            validatePage(true); 
        }
    }
    
	/*
	 * Get a proposed comment by looking at the active change sets
	 */
    private String getProposedComment(IResource[] resourcesToCommit) {
    	StringBuffer comment = new StringBuffer();
        ChangeSet[] sets = CVSUIPlugin.getPlugin().getChangeSetManager().getSets();
        Arrays.sort(sets, new ChangeSetComparator());
        int numMatchedSets = 0;
        for (int i = 0; i < sets.length; i++) {
            ChangeSet set = sets[i];
            if (isUserSet(set) && containsOne(set, resourcesToCommit)) {
            	if(numMatchedSets > 0) comment.append(System.getProperty("line.separator")); //$NON-NLS-1$
                comment.append(set.getComment());
                numMatchedSets++;
            }
        }
        return comment.toString();
    }
    
    private boolean isUserSet(ChangeSet set) {
		if (set instanceof ActiveChangeSet) {
			ActiveChangeSet acs = (ActiveChangeSet) set;
			return acs.isUserCreated();
		}
		return false;
	}

	private boolean containsOne(ChangeSet set, IResource[] resourcesToCommit) {
   	 	for (int j = 0; j < resourcesToCommit.length; j++) {
			IResource resource = resourcesToCommit[j];
			if (set.contains(resource)) {
				return true;
			}
			if (set instanceof ActiveChangeSet) {
				ActiveChangeSet acs = (ActiveChangeSet) set;
				if (acs.getDiffTree().members(resource).length > 0)
					return true;
			}
		}
		return false;
   }
	
	public void finish() {
		int[] hWeights = horizontalSash.getWeights();
		int[] vWeights = verticalSash.getWeights();
		IDialogSettings section = getDialogSettings().getSection(CommitWizard.COMMIT_WIZARD_DIALOG_SETTINGS);
    	if (section == null)
    		section= getDialogSettings().addNewSection(CommitWizard.COMMIT_WIZARD_DIALOG_SETTINGS);
		if (showCompare) {
			section.put(H_WEIGHT_1, hWeights[0]);
			section.put(H_WEIGHT_2, hWeights[1]);
		}
		section.put(V_WEIGHT_1, vWeights[0]);
		section.put(V_WEIGHT_2, vWeights[1]);
		section.put(SHOW_COMPARE, showCompare);
	}

	public void showComparePane(boolean showCompare) {
		this.showCompare = showCompare;
		if (showCompare) {
			horizontalSash.setMaximizedControl(null);
		} else {
			horizontalSash.setMaximizedControl(verticalSash);
		}
		
	}
    
}    

