/*******************************************************************************
 * Copyright (c) 2005, 2016 Intel Corporation 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:
 * Intel Corporation - Initial API and implementation
 *******************************************************************************/
package org.eclipse.cdt.managedbuilder.ui.properties;

import java.util.Arrays;
import java.util.Comparator;

import org.eclipse.cdt.core.cdtvariables.CdtVariableException;
import org.eclipse.cdt.core.cdtvariables.ICdtVariable;
import org.eclipse.cdt.core.settings.model.ICConfigurationDescription;
import org.eclipse.cdt.core.settings.model.ICMultiItemsHolder;
import org.eclipse.cdt.managedbuilder.core.IOption;
import org.eclipse.cdt.managedbuilder.internal.macros.BuildMacro;
import org.eclipse.cdt.managedbuilder.internal.ui.Messages;
import org.eclipse.cdt.managedbuilder.macros.IBuildMacro;
import org.eclipse.cdt.utils.cdtvariables.CdtVariableResolver;
import org.eclipse.cdt.utils.ui.controls.FileListControl;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CCombo;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.DirectoryDialog;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;

import com.ibm.icu.text.Collator;

/**
 * the dialog used to create or edit the build macro
 *
 * @noextend This class is not intended to be subclassed by clients.
 * @noinstantiate This class is not intended to be instantiated by clients.
 */
public class NewVarDialog extends Dialog {
	private static final String EMPTY_STRING = ""; //$NON-NLS-1$

	// The title of the dialog.
	private String fTitle;
	// hold the macro being edited(in the case of the "edit" mode)
	private ICdtVariable fEditedMacro;
	//the resulting macro. Can be accessed only when the dialog is closed
	private ICdtVariable fResultingMacro;

	private boolean fTotalSizeCalculated;

	private String fTypedName;
	private int fTypedType = -1;
	public boolean isForAllCfgs = false;

	// Widgets
	private Composite fContainer;
	private CCombo fMacroNameEdit;
	private Label fMacroValueLabel;
	private Text fMacroValueEdit;
	private Button fBrowseButton;
	private Combo fTypeSelector;
	private Composite fListEditorContainier;

	private FileListControl fListEditor;
	private ICConfigurationDescription cfgd;
	private ICdtVariable[] vars;

	public NewVarDialog(Shell parentShell, ICdtVariable editedMacro, ICConfigurationDescription _cfgd,
			ICdtVariable[] _vars) {
		super(parentShell);
		cfgd = _cfgd;
		if (editedMacro != null)
			fTitle = Messages.NewBuildMacroDialog_label_title_edit;
		else
			fTitle = Messages.NewBuildMacroDialog_label_title_new;
		fEditedMacro = editedMacro;
		vars = _vars;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jface.window.Window#configureShell(org.eclipse.swt.widgets.Shell)
	 */
	@Override
	protected void configureShell(Shell shell) {
		super.configureShell(shell);
		if (fTitle != null)
			shell.setText(fTitle);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite)
	 */
	@Override
	protected Control createDialogArea(Composite parent) {
		Composite comp = new Composite(parent, SWT.NULL);
		comp.setFont(parent.getFont());
		comp.setLayout(new GridLayout(3, false));
		GridData gd = new GridData(GridData.FILL_BOTH);
		comp.setLayoutData(gd);

		fContainer = comp;

		Label nameLabel = new Label(comp, SWT.LEFT);
		nameLabel.setFont(comp.getFont());
		nameLabel.setText(Messages.NewVarDialog_0);
		nameLabel.setLayoutData(new GridData());

		fMacroNameEdit = new CCombo(comp, SWT.BORDER);
		fMacroNameEdit.setItems(getMacroNames());
		fMacroNameEdit.setFont(comp.getFont());
		gd = new GridData(); // don't use GridData.FILL_HORIZONTAL; see bugzilla 229793
		gd.horizontalSpan = 2;
		gd.widthHint = IDialogConstants.ENTRY_FIELD_WIDTH + 50;
		fMacroNameEdit.setLayoutData(gd);
		fMacroNameEdit.addModifyListener(new ModifyListener() {
			@Override
			public void modifyText(ModifyEvent e) {
				handleMacroNameModified();
			}
		});
		fMacroNameEdit.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				handleMacroNameSelection();
			}
		});

		if (fEditedMacro == null && cfgd != null && !(cfgd instanceof ICMultiItemsHolder)) {
			Button c_all = new Button(comp, SWT.CHECK);
			c_all.setText(Messages.NewVarDialog_1);
			gd = new GridData(GridData.BEGINNING);
			gd.horizontalSpan = 3;
			c_all.setLayoutData(gd);
			c_all.addSelectionListener(new SelectionAdapter() {
				@Override
				public void widgetSelected(SelectionEvent e) {
					isForAllCfgs = ((Button) e.widget).getSelection();
				}
			});
		}

		Label typeLabel = new Label(comp, SWT.LEFT);
		typeLabel.setFont(comp.getFont());
		typeLabel.setText(Messages.NewBuildMacroDialog_label_type);
		gd = new GridData();
		typeLabel.setLayoutData(gd);

		fTypeSelector = new Combo(comp, SWT.READ_ONLY | SWT.DROP_DOWN | SWT.BORDER);
		gd = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING);
		gd.horizontalSpan = 2;
		//		gd.widthHint = 100;
		fTypeSelector.setLayoutData(gd);
		fTypeSelector.setItems(new String[] { Messages.NewBuildMacroDialog_label_type_text,
				Messages.NewBuildMacroDialog_label_type_text_list, Messages.NewBuildMacroDialog_label_type_path_file,
				Messages.NewBuildMacroDialog_label_type_path_file_list,
				Messages.NewBuildMacroDialog_label_type_path_dir, Messages.NewBuildMacroDialog_label_type_path_dir_list,
				Messages.NewBuildMacroDialog_label_type_path_any,
				Messages.NewBuildMacroDialog_label_type_path_any_list });
		setSelectedType(IBuildMacro.VALUE_TEXT);

		fTypeSelector.addListener(SWT.Selection, new Listener() {
			@Override
			public void handleEvent(Event e) {
				handleTypeModified();
			}
		});

		fMacroValueLabel = new Label(comp, SWT.LEFT);
		fMacroValueLabel.setFont(comp.getFont());
		fMacroValueLabel.setText(Messages.NewBuildMacroDialog_label_value);
		gd = new GridData();
		gd.horizontalSpan = 1;
		fMacroValueLabel.setLayoutData(gd);

		fMacroValueEdit = new Text(comp, SWT.SINGLE | SWT.BORDER);
		fMacroValueEdit.setFont(comp.getFont());
		gd = new GridData(GridData.FILL_HORIZONTAL);
		gd.widthHint = IDialogConstants.ENTRY_FIELD_WIDTH + 100;
		gd.horizontalSpan = 1;
		fMacroValueEdit.setLayoutData(gd);
		fMacroValueEdit.addModifyListener(new ModifyListener() {
			@Override
			public void modifyText(ModifyEvent e) {
				handleMacroValueModified();
			}
		});

		fBrowseButton = new Button(comp, SWT.PUSH);
		fBrowseButton.setFont(comp.getFont());
		fBrowseButton.setText(Messages.NewBuildMacroDialog_label_browse);
		fBrowseButton.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				handleBrowseButtonPressed();
			}
		});

		gd = new GridData();
		gd.widthHint = IDialogConstants.BUTTON_WIDTH;
		gd.horizontalSpan = 1;
		fBrowseButton.setLayoutData(gd);

		fListEditorContainier = new Composite(comp, 0);
		gd = new GridData(GridData.FILL_BOTH);
		gd.horizontalSpan = 3;
		fListEditorContainier.setLayoutData(gd);
		fListEditorContainier.setLayout(new GridLayout());

		fListEditor = new FileListControl(fListEditorContainier, Messages.NewBuildMacroDialog_label_list_title,
				IOption.BROWSE_NONE);
		/* Enable workspace support for list editor */
		fListEditor.setWorkspaceSupport(true);

		// TODO: getContextInfo
		// fListEditor.setContext(fMacrosBlock.getContextInfo());

		if (fEditedMacro != null) {
			loadVar(fEditedMacro);
			fMacroNameEdit.setEnabled(false);
		}
		return comp;
	}

	/*
	 * get the names to be displayed in the var Name combo.
	 */
	private String[] getMacroNames() {
		String names[] = null;
		if (vars == null || vars.length == 0)
			names = new String[0];
		else {
			names = new String[vars.length];
			for (int i = 0; i < vars.length; i++)
				names[i] = vars[i].getName();
			final Collator collator = Collator.getInstance();
			Arrays.sort(names, new Comparator<String>() {
				@Override
				public int compare(final String a, final String b) {
					final String strA = a.toUpperCase();
					final String strB = b.toUpperCase();
					return collator.compare(strA, strB);
				}
			});
		}
		return names;
	}

	private void handleBrowseButtonPressed() {
		switch (getBrowseType(getSelectedType())) {
		case IOption.BROWSE_FILE:
			FileDialog fileDlg = new FileDialog(fBrowseButton.getShell());
			String file = fileDlg.open();
			if (file != null)
				fMacroValueEdit.setText(file);
			break;
		case IOption.BROWSE_DIR:
			DirectoryDialog dirDlg = new DirectoryDialog(fBrowseButton.getShell());
			String dir = dirDlg.open();
			if (dir != null)
				fMacroValueEdit.setText(dir);
			break;
		}
	}

	private int getBrowseType(int type) {
		int browseType = IOption.BROWSE_NONE;
		switch (type) {
		case IBuildMacro.VALUE_PATH_FILE:
		case IBuildMacro.VALUE_PATH_FILE_LIST:
			browseType = IOption.BROWSE_FILE;
			break;
		case IBuildMacro.VALUE_PATH_DIR:
		case IBuildMacro.VALUE_PATH_DIR_LIST:
			browseType = IOption.BROWSE_DIR;
			break;
		case IBuildMacro.VALUE_PATH_ANY:
		case IBuildMacro.VALUE_PATH_ANY_LIST:
			break;
		case IBuildMacro.VALUE_TEXT:
		case IBuildMacro.VALUE_TEXT_LIST:
		default:
		}
		return browseType;

	}

	/*
	 * called when the variable name is selected, loads all the dialog fields with the variable settings
	 */
	private void handleMacroNameSelection() {
		int index = fMacroNameEdit.getSelectionIndex();
		if (index != -1)
			loadVarSettings(fMacroNameEdit.getItem(index));
	}

	private void loadVarSettings(String name) {
		ICdtVariable v = null;
		for (int i = 0; i < vars.length; i++) {
			if (vars[i].getName().equals(name)) {
				v = vars[i];
				break;
			}
		}
		if (v != null)
			loadVar(v);
		else
			loadVar(name, IBuildMacro.VALUE_TEXT, EMPTY_STRING);
	}

	private void loadVar(String name, int type, String value[]) {
		setSelectedType(type);
		setSelectedMacroName(notNull(name));
		fListEditor.setList(value);
		updateWidgetState();
	}

	private void loadVar(String name, int type, String value) {
		setSelectedType(type);
		setSelectedMacroName(notNull(name));
		fMacroValueEdit.setText(notNull(value));
		updateWidgetState();
	}

	/*
	 * loads all the dialog fields with the variable settings
	 */
	private void loadVar(ICdtVariable var) {
		try {
			if (CdtVariableResolver.isStringListVariable(var.getValueType()))
				loadVar(var.getName(), var.getValueType(), var.getStringListValue());
			else
				loadVar(var.getName(), var.getValueType(), var.getStringValue());
		} catch (CdtVariableException e) {
		}
	}

	/*
	 * returns an empty string in the case the string passed is null.
	 * otherwise returns the string passed
	 */
	private String notNull(String str) {
		return str == null ? EMPTY_STRING : str;
	}

	/*
	 * returns the name typed in the dialog var name edit triming spaces
	 */
	private String getSelectedVarName() {
		return fMacroNameEdit.getText().trim();
	}

	/*
	 * sets the variable name to the dialog "variable name" edit control
	 */
	private void setSelectedMacroName(String name) {
		if (!macroNamesEqual(fMacroNameEdit.getText(), name)) {
			fTypedName = name;
			fMacroNameEdit.setText(notNull(name).trim());
		}
	}

	private boolean macroNamesEqual(String name1, String name2) {
		name1 = name1.trim();
		name2 = name2.trim();
		return name1.equalsIgnoreCase(name2);
	}

	/*
	 * returns the selected type
	 */
	private int getSelectedType() {
		switch (fTypeSelector.getSelectionIndex()) {
		case 1:
			return IBuildMacro.VALUE_TEXT_LIST;
		case 2:
			return IBuildMacro.VALUE_PATH_FILE;
		case 3:
			return IBuildMacro.VALUE_PATH_FILE_LIST;
		case 4:
			return IBuildMacro.VALUE_PATH_DIR;
		case 5:
			return IBuildMacro.VALUE_PATH_DIR_LIST;
		case 6:
			return IBuildMacro.VALUE_PATH_ANY;
		case 7:
			return IBuildMacro.VALUE_PATH_ANY_LIST;
		case 0:
		default:
			return IBuildMacro.VALUE_TEXT;
		}
	}

	/*
	 * sets the selected type
	 */
	private void setSelectedType(int type) {
		switch (type) {
		case IBuildMacro.VALUE_TEXT_LIST:
			fTypeSelector.select(1);
			break;
		case IBuildMacro.VALUE_PATH_FILE:
			fTypeSelector.select(2);
			break;
		case IBuildMacro.VALUE_PATH_FILE_LIST:
			fTypeSelector.select(3);
			break;
		case IBuildMacro.VALUE_PATH_DIR:
			fTypeSelector.select(4);
			break;
		case IBuildMacro.VALUE_PATH_DIR_LIST:
			fTypeSelector.select(5);
			break;
		case IBuildMacro.VALUE_PATH_ANY:
			fTypeSelector.select(6);
			break;
		case IBuildMacro.VALUE_PATH_ANY_LIST:
			fTypeSelector.select(7);
			break;
		case IBuildMacro.VALUE_TEXT:
		default:
			fTypeSelector.select(0);
			break;
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jface.dialogs.Dialog#okPressed()
	 */
	@Override
	protected void okPressed() {
		String name = getSelectedVarName();
		if (name != null || !EMPTY_STRING.equals(name)) {
			int type = getSelectedType();
			if (CdtVariableResolver.isStringListVariable(type))
				fResultingMacro = new BuildMacro(name, type, getSelectedStringListValue());
			else
				fResultingMacro = new BuildMacro(name, type, getSelectedStringValue());
		}

		super.okPressed();
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jface.window.Window#open()
	 */
	@Override
	public int open() {
		fResultingMacro = null;
		return super.open();
	}

	/*
	 * returns the macro value that should be stored in the resulting variable
	 */
	private String getSelectedStringValue() {
		return fMacroValueEdit.getText();
	}

	private String[] getSelectedStringListValue() {
		return fListEditor.getItems();
	}

	/*
	 * this method should be called after the dialog is closed
	 * to obtain the created variable.
	 * if the variable was not created, e.g. because a user has pressed
	 * the cancel button this method returns null
	 */
	public ICdtVariable getDefinedMacro() {
		return fResultingMacro;
	}

	/*
	 * called when the variable name is modified
	 */
	private void handleMacroNameModified() {
		String name = getSelectedVarName();
		if (fTypedName == null || !fTypedName.equals(name)) {
			loadVarSettings(name);
		}
	}

	private void handleMacroValueModified() {
	}

	/*
	 * called when the operation is modified
	 */
	private void handleTypeModified() {
		int type = getSelectedType();
		if (fTypedType != -1 && fTypedType == type)
			return;

		fTypedType = type;

		adjustLayout(type);
	}

	private void adjustLayout(int type) {
		GridData listGd = (GridData) fListEditorContainier.getLayoutData();
		GridData labelGd = (GridData) fMacroValueLabel.getLayoutData();
		GridData editGd = (GridData) fMacroValueEdit.getLayoutData();
		GridData browseGd = (GridData) fBrowseButton.getLayoutData();

		if (CdtVariableResolver.isStringListVariable(type)) {
			listGd.exclude = false;
			labelGd.exclude = true;
			editGd.exclude = true;
			browseGd.exclude = true;
			fListEditorContainier.setVisible(true);
			fListEditor.setType(getBrowseType(type));
			fMacroValueLabel.setVisible(false);
			fMacroValueEdit.setVisible(false);
			fBrowseButton.setVisible(false);
		} else {
			listGd.exclude = true;
			labelGd.exclude = false;
			editGd.exclude = false;
			int editSpan;
			fListEditorContainier.setVisible(false);
			fMacroValueLabel.setVisible(true);
			fMacroValueEdit.setVisible(true);
			if (getBrowseType(type) != IOption.BROWSE_NONE) {
				browseGd.exclude = false;
				editSpan = 1;
				fBrowseButton.setVisible(true);
			} else {
				browseGd.exclude = true;
				editSpan = 2;
				fBrowseButton.setVisible(false);
			}
			editGd.horizontalSpan = editSpan;
		}
		fContainer.layout(true, true);
	}

	/*
	 * updates the state of the dialog controls
	 */
	private void updateWidgetState() {
		if (!fTotalSizeCalculated)
			return;
		handleTypeModified();

		Button b = getButton(IDialogConstants.OK_ID);
		if (b != null) {
			String name = getSelectedVarName();
			b.setEnabled(!EMPTY_STRING.equals(name));
		}
	}

	@Override
	protected Point getInitialSize() {
		Point size = super.getInitialSize();
		fTotalSizeCalculated = true;
		updateWidgetState();
		return size;
	}
}
