package bndtools.wizards.project;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
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.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;

import bndtools.Plugin;
import bndtools.utils.JavaLangUtils;

public class ProjectNameGroup {

	private static final String			PROP_PROJECT_NAME	= "projectName";
	private static final String			PROP_PACKAGE_NAME	= "packageName";
	private static final String			PROP_STATUS			= "status";

	private final PropertyChangeSupport	propSupport			= new PropertyChangeSupport(this);

	private String						projectName			= "";
	private String						packageName			= "";
	private IStatus						status				= Status.OK_STATUS;

	private Text						txtProjectName;
	private Text						txtBasePackageName;
	private Button						btnInferBasePackageName;

	/**
	 * @wbp.parser.entryPoint
	 */
	public Control createControl(Composite parent) {
		Composite composite = new Composite(parent, SWT.NONE);
		composite.setLayout(new GridLayout(2, false));

		Label lblProjectName = new Label(composite, SWT.NONE);
		lblProjectName.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
		lblProjectName.setText("Project Name:");

		txtProjectName = new Text(composite, SWT.BORDER);
		txtProjectName.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));

		txtProjectName.setText(projectName);
		txtProjectName.addModifyListener(e -> {
			String old = projectName;
			projectName = txtProjectName.getText()
				.trim();
			propSupport.firePropertyChange(PROP_PROJECT_NAME, old, projectName);

			if (btnInferBasePackageName.getSelection()) {
				txtBasePackageName.setText(toLegalPackageName(projectName));
			}
		});

		Label lblBasePackageName = new Label(composite, SWT.NONE);
		lblBasePackageName.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
		lblBasePackageName.setText("Java Package:");

		txtBasePackageName = new Text(composite, SWT.BORDER);
		txtBasePackageName.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
		txtBasePackageName.addModifyListener(ev -> {
			String old = packageName;
			packageName = txtBasePackageName.getText()
				.trim();
			propSupport.firePropertyChange(PROP_PACKAGE_NAME, old, packageName);
			updateStatus();
		});

		@SuppressWarnings("unused")
		Label lblSpacer1 = new Label(composite, SWT.NONE);

		btnInferBasePackageName = new Button(composite, SWT.CHECK);
		btnInferBasePackageName.setText("Derive from project name");
		btnInferBasePackageName.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));

		btnInferBasePackageName.setSelection(true);
		txtBasePackageName.setEnabled(false);
		btnInferBasePackageName.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				boolean infer = btnInferBasePackageName.getSelection();
				txtBasePackageName.setEnabled(!infer);
				if (infer) {
					txtBasePackageName.setText(toLegalPackageName(projectName));
				} else {
					txtBasePackageName.setFocus();
				}
			}
		});

		return composite;
	}

	private void updateStatus() {
		String error = validatePackageName(packageName);

		IStatus oldStatus = status;
		status = error != null ? new Status(IStatus.ERROR, Plugin.PLUGIN_ID, 0, error, null) : Status.OK_STATUS;
		if (oldStatus != status)
			propSupport.firePropertyChange(PROP_STATUS, oldStatus, status);
	}

	/**
	 * Validate a Java package name.
	 *
	 * @return null if the package name is legal; an error message if illegal
	 */
	static String validatePackageName(String packageName) {
		if (packageName.isEmpty())
			return "Package name may not be empty";

		String[] parts = packageName.split("\\.", -1);
		for (String part : parts) {
			if (part.isEmpty())
				return "Package name part may not be empty";

			char firstChar = part.charAt(0);
			if (!Character.isJavaIdentifierStart(firstChar))
				return "Illegal character at start of package name part: " + firstChar;

			for (int i = 1; i < part.length(); i++) {
				char ch = part.charAt(i);
				if (!Character.isJavaIdentifierPart(ch))
					return "Illegal character in package name part: " + ch;
			}

			if (JavaLangUtils.isKeyword(part) || JavaLangUtils.isReserved(part))
				return "Invalid package name. '" + part + "' is not a valid Java identifier";
		}

		// Orl Korrect
		return null;
	}

	/**
	 * Convert a string to a legal package name
	 */
	static String toLegalPackageName(String input) throws IllegalArgumentException {
		if (input.isEmpty())
			return "";

		String[] parts = input.split("\\.", -1);
		List<String> newParts = new ArrayList<>(parts.length);

		for (String part : parts) {
			StringBuilder builder = new StringBuilder();

			if (!part.isEmpty()) {
				char firstChar = part.charAt(0);
				if (Character.isJavaIdentifierStart(firstChar))
					builder.append(firstChar);
				else if (Character.isJavaIdentifierPart(firstChar))
					builder.append('_')
						.append(firstChar);
				else if ('-' == firstChar)
					builder.append('-');

				for (int i = 1; i < part.length(); i++) {
					char ch = part.charAt(i);
					if (Character.isJavaIdentifierPart(ch)) {
						builder.append(ch);
					} else if ('-' == ch) {
						builder.append('_');
					}
				}
			}

			String newPart = builder.toString();
			if (!newPart.isEmpty())
				newParts.add(newPart);
		}

		StringBuilder builder = new StringBuilder();
		for (int i = 0; i < newParts.size(); i++) {
			if (i > 0)
				builder.append('.');
			builder.append(newParts.get(i));
		}
		return builder.toString();
	}

	public String getProjectName() {
		return projectName;
	}

	public String getPackageName() {
		return packageName;
	}

	public IStatus getStatus() {
		return status;
	}

	public void addPropertyChangeListener(PropertyChangeListener var0) {
		propSupport.addPropertyChangeListener(var0);
	}

	public void removePropertyChangeListener(PropertyChangeListener var0) {
		propSupport.removePropertyChangeListener(var0);
	}

}
