/*******************************************************************************
 * Copyright (c) 2010, 2012 Ericsson and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     Ericsson - initial API and implementation
 *     Marc Khouzam (Ericsson) - Added support for collecting char pointers as strings (bug 373707)
 *******************************************************************************/
package org.eclipse.cdt.dsf.gdb.internal.ui.tracepointactions;

import org.eclipse.cdt.debug.core.breakpointactions.IBreakpointAction;
import org.eclipse.cdt.debug.ui.breakpointactions.IBreakpointActionPage;
import org.eclipse.cdt.dsf.gdb.internal.tracepointactions.CollectAction;
import org.eclipse.core.runtime.PlatformObject;
import org.eclipse.jface.fieldassist.ControlDecoration;
import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
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.Label;
import org.eclipse.swt.widgets.Text;

/**
 * @since 2.1
 */
public class CollectActionPage extends PlatformObject implements IBreakpointActionPage {

	/**
	 * An exception to indicate that the user-specified string limit is invalid
	 */
	private class IllegalCollectStringLimitException extends Exception {
		private static final long serialVersionUID = -2087722354642237691L;

		public IllegalCollectStringLimitException(String message) {
			super(message);
		}
	}

	private Text fCollectString;
	private Button fTreatCharPtrAsStrings;
	private Text fTreatCharPtrAsStringsLimit;

	private CollectAction fCollectAction;

	/**
	 * Create the composite
	 */
	private Composite createCollectActionComposite(Composite parent, int style) {
		Composite composite = new Composite(parent, style);
		composite.setLayout(new GridLayout(2, false));

		// The label asking for what to collect
		final Label collectLabel = new Label(composite, SWT.NONE);
		collectLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1));
		collectLabel.setText(MessagesForTracepointActions.TracepointActions_Collect_Label);

		// The user-specified string of what to collect
		fCollectString = new Text(composite, SWT.BORDER);
		fCollectString.setText(fCollectAction.getCollectString());
		fCollectString.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));

		// An option to collect character pointers as strings
		fTreatCharPtrAsStrings = new Button(composite, SWT.CHECK);
		GridData gd = new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1);
		gd.verticalIndent = 15;
		// Store the button width before we add the text as we only care about the checkbox width
		int buttonWidth = fTreatCharPtrAsStrings.computeSize(SWT.DEFAULT, SWT.DEFAULT).x;
		fTreatCharPtrAsStrings.setText(MessagesForTracepointActions.TracepointActions_Collect_Strings_Label);
		fTreatCharPtrAsStrings.setLayoutData(gd);
		fTreatCharPtrAsStrings.setSelection(fCollectAction.getCharPtrAsStrings());
		fTreatCharPtrAsStrings.addSelectionListener(new SelectionListener() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				// Disable/enable the limit field
				fTreatCharPtrAsStringsLimit.setEnabled(fTreatCharPtrAsStrings.getSelection());
			}

			@Override
			public void widgetDefaultSelected(SelectionEvent e) {
				// Disable/enable the limit field
				fTreatCharPtrAsStringsLimit.setEnabled(fTreatCharPtrAsStrings.getSelection());
			}
		});

		// A label asking for an optional limit of bytes of collected strings
		final Label limitLabel = new Label(composite, SWT.NONE);
		gd = new GridData(SWT.CENTER, SWT.CENTER, false, false);
		gd.horizontalIndent = buttonWidth;
		limitLabel.setLayoutData(gd);
		limitLabel.setText(MessagesForTracepointActions.TracepointActions_Collect_Strings_Limit_Label);

		// A user-specified limit of bytes
		fTreatCharPtrAsStringsLimit = new Text(composite, SWT.BORDER);
		fTreatCharPtrAsStringsLimit.setText(getCharPtrAsStringLimit(fCollectAction.getCharPtrAsStringsLimit()));

		gd = new GridData(SWT.FILL, SWT.CENTER, false, false);
		gd.horizontalIndent = FieldDecorationRegistry.getDefault().getMaximumDecorationWidth();
		fTreatCharPtrAsStringsLimit.setLayoutData(gd);
		fTreatCharPtrAsStringsLimit.setEnabled(fTreatCharPtrAsStrings.getSelection());

		final ControlDecoration decoration = new ControlDecoration(fTreatCharPtrAsStringsLimit, SWT.TOP | SWT.LEFT,
				composite);
		decoration.hide();
		fTreatCharPtrAsStringsLimit.addModifyListener(new ModifyListener() {
			@Override
			public void modifyText(ModifyEvent e) {
				try {
					getCharPtrAsStringLimit(fTreatCharPtrAsStringsLimit.getText());
					decoration.hide();
				} catch (IllegalCollectStringLimitException exception) {
					decoration.setImage(FieldDecorationRegistry.getDefault()
							.getFieldDecoration(FieldDecorationRegistry.DEC_ERROR).getImage());
					decoration.setDescriptionText(exception.getMessage());
					decoration.show();
				}
			}
		});

		return composite;
	}

	public CollectAction getCollectAction() {
		return fCollectAction;
	}

	@Override
	public void actionDialogCanceled() {
	}

	@Override
	public void actionDialogOK() {
		fCollectAction.setCollectString(fCollectString.getText());
		fCollectAction.setCharPtrAsStrings(fTreatCharPtrAsStrings.getSelection());

		try {
			Integer limit = getCharPtrAsStringLimit(fTreatCharPtrAsStringsLimit.getText());
			fCollectAction.setCharPtrAsStringsLimit(limit);
		} catch (IllegalCollectStringLimitException e) {
			// ignore and keep old value
		}
	}

	@Override
	public Composite createComposite(IBreakpointAction action, Composite composite, int style) {
		fCollectAction = (CollectAction) action;
		return createCollectActionComposite(composite, style);
	}

	/**
	 * Convert the user-specified string into an integer.
	 * If the string is not valid, disable the limit by using null.
	 * @param limitStr The string provided by the user
	 * @return An non-negative integer limit, or null for no limit.
	 */
	private Integer getCharPtrAsStringLimit(String limitStr) throws IllegalCollectStringLimitException {
		limitStr = limitStr.trim();
		Integer limit = null;
		try {
			limit = Integer.parseInt(limitStr);
			if (limit < 0) {
				throw new IllegalCollectStringLimitException(
						MessagesForTracepointActions.TracepointActions_Collect_Strings_Limit_Error);
			}
		} catch (NumberFormatException e) {
			if (!limitStr.isEmpty()) {
				// We only accept an empty string, which means no limit
				throw new IllegalCollectStringLimitException(
						MessagesForTracepointActions.TracepointActions_Collect_Strings_Limit_Error);
			}
		}
		return limit;
	}

	/**
	 * Convert the integer limit into a string.
	 * If the string is not valid, disable the limit by using null.
	 * @param limit The integer limit to convert.  Can be null for no limit.
	 * @return The limit as a string, where no limit or a negative limit is the empty string.
	 */
	private String getCharPtrAsStringLimit(Integer limit) {
		if (limit == null || limit < 0)
			return ""; //$NON-NLS-1$
		return Integer.toString(limit);
	}
}
