/*******************************************************************************
 * Copyright (c) 2007, 2011 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
 * IBM Corporation
 * Dmitry Kozlov (CodeSourcery) - save build output preferences (bug 294106)
 * Andrew Gvozdev (Quoin Inc)   - Saving build output implemented in different way (bug 306222)
 *******************************************************************************/
package org.eclipse.cdt.managedbuilder.ui.properties;

import org.eclipse.cdt.core.settings.model.ICResourceDescription;
import org.eclipse.cdt.managedbuilder.core.IBuilder;
import org.eclipse.cdt.managedbuilder.core.IConfiguration;
import org.eclipse.cdt.managedbuilder.core.IMultiConfiguration;
import org.eclipse.cdt.managedbuilder.internal.core.Configuration;
import org.eclipse.cdt.managedbuilder.internal.core.MultiConfiguration;
import org.eclipse.cdt.managedbuilder.internal.ui.Messages;
import org.eclipse.cdt.ui.newui.AbstractCPropertyTab;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.swt.SWT;
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.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.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Widget;

/**
 * @noextend This class is not intended to be subclassed by clients.
 * @noinstantiate This class is not intended to be instantiated by clients.
 */
public class BuilderSettingsTab extends AbstractCBuildPropertyTab {
	// Widgets
	//1
	private Button b_useDefault;
	private Combo c_builderType;
	private Text t_buildCmd;
	//2
	private Button b_genMakefileAuto;
	private Button b_expandVars;
	//5
	private Text t_dir;
	private Button b_dirWsp;
	private Button b_dirFile;
	private Button b_dirVars;
	private Group group_dir;

	private IBuilder bldr;
	private IConfiguration icfg;
	private boolean canModify = true;

	@Override
	public void createControls(Composite parent) {
		super.createControls(parent);
		usercomp.setLayout(new GridLayout(1, false));

		// Builder group
		Group g1 = setupGroup(usercomp, Messages.BuilderSettingsTab_0, 3, GridData.FILL_HORIZONTAL);
		setupLabel(g1, Messages.BuilderSettingsTab_1, 1, GridData.BEGINNING);
		c_builderType = new Combo(g1, SWT.READ_ONLY | SWT.DROP_DOWN | SWT.BORDER);
		setupControl(c_builderType, 2, GridData.FILL_HORIZONTAL);
		c_builderType.add(Messages.BuilderSettingsTab_2);
		c_builderType.add(Messages.BuilderSettingsTab_3);
		c_builderType.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent event) {
				enableInternalBuilder(c_builderType.getSelectionIndex() == 1);
				updateButtons();
			}
		});

		b_useDefault = setupCheck(g1, Messages.BuilderSettingsTab_4, 3, GridData.BEGINNING);

		setupLabel(g1, Messages.BuilderSettingsTab_5, 1, GridData.BEGINNING);
		t_buildCmd = setupBlock(g1, b_useDefault);
		t_buildCmd.addModifyListener(new ModifyListener() {
			@Override
			public void modifyText(ModifyEvent e) {
				if (!canModify)
					return;
				String fullCommand = t_buildCmd.getText().trim();
				String buildCommand = parseMakeCommand(fullCommand);
				String buildArgs = fullCommand.substring(buildCommand.length()).trim();
				if (!buildCommand.equals(bldr.getCommand()) || !buildArgs.equals(bldr.getArguments())) {
					setCommand(buildCommand);
					setArguments(buildArgs);
				}
			}
		});

		Group g2 = setupGroup(usercomp, Messages.BuilderSettingsTab_6, 2, GridData.FILL_HORIZONTAL);
		((GridLayout) (g2.getLayout())).makeColumnsEqualWidth = true;

		b_genMakefileAuto = setupCheck(g2, Messages.BuilderSettingsTab_7, 1, GridData.BEGINNING);
		b_expandVars = setupCheck(g2, Messages.BuilderSettingsTab_8, 1, GridData.BEGINNING);

		// Build location group
		group_dir = setupGroup(usercomp, Messages.BuilderSettingsTab_21, 2, GridData.FILL_HORIZONTAL);
		setupLabel(group_dir, Messages.BuilderSettingsTab_22, 1, GridData.BEGINNING);
		t_dir = setupText(group_dir, 1, GridData.FILL_HORIZONTAL);
		t_dir.addModifyListener(new ModifyListener() {
			@Override
			public void modifyText(ModifyEvent e) {
				if (canModify)
					setBuildPath(t_dir.getText());
			}
		});
		Composite c = new Composite(group_dir, SWT.NONE);
		setupControl(c, 2, GridData.FILL_HORIZONTAL);
		GridLayout f = new GridLayout(4, false);
		c.setLayout(f);
		Label dummy = new Label(c, 0);
		dummy.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
		b_dirWsp = setupBottomButton(c, WORKSPACEBUTTON_NAME);
		b_dirFile = setupBottomButton(c, FILESYSTEMBUTTON_NAME);
		b_dirVars = setupBottomButton(c, VARIABLESBUTTON_NAME);
	}

	private void setManagedBuild(boolean enable) {
		setManagedBuildOn(enable);
		page.informPages(MANAGEDBUILDSTATE, null);
		updateButtons();
	}

	/**
	 * sets widgets states
	 */
	@Override
	protected void updateButtons() {
		bldr = icfg.getEditableBuilder();

		canModify = false; // avoid extra update from modifyListeners
		int[] extStates = BuildBehaviourTab.calc3states(page, icfg, 0);

		b_genMakefileAuto.setEnabled(icfg.supportsBuild(true));
		if (extStates == null) { // no extended states available
			BuildBehaviourTab.setTriSelection(b_genMakefileAuto, bldr.isManagedBuildOn());
			BuildBehaviourTab.setTriSelection(b_useDefault, bldr.isDefaultBuildCmd());
			// b_expandVars.setGrayed(false);
			if (!bldr.canKeepEnvironmentVariablesInBuildfile())
				b_expandVars.setEnabled(false);
			else {
				b_expandVars.setEnabled(true);
				BuildBehaviourTab.setTriSelection(b_expandVars, !bldr.keepEnvironmentVariablesInBuildfile());
			}
		} else {
			BuildBehaviourTab.setTriSelection(b_genMakefileAuto, extStates[0]);
			BuildBehaviourTab.setTriSelection(b_useDefault, extStates[1]);
			if (extStates[2] != BuildBehaviourTab.TRI_YES)
				b_expandVars.setEnabled(false);
			else {
				b_expandVars.setEnabled(true);
				BuildBehaviourTab.setTriSelection(b_expandVars, extStates[3]);
			}
		}
		c_builderType.select(isInternalBuilderEnabled() ? 1 : 0);
		c_builderType.setEnabled(canEnableInternalBuilder(true) && canEnableInternalBuilder(false));

		t_buildCmd.setText(getMakeCommand());

		if (page.isMultiCfg()) {
			group_dir.setVisible(false);
		} else {
			group_dir.setVisible(true);
			t_dir.setText(bldr.getBuildPath());
			boolean mbOn = bldr.isManagedBuildOn();
			t_dir.setEnabled(!mbOn);
			b_dirVars.setEnabled(!mbOn);
			b_dirWsp.setEnabled(!mbOn);
			b_dirFile.setEnabled(!mbOn);
		}
		boolean external = (c_builderType.getSelectionIndex() == 0);

		b_useDefault.setEnabled(external);
		t_buildCmd.setEnabled(external);
		((Control) t_buildCmd.getData()).setEnabled(external & !b_useDefault.getSelection());

		b_genMakefileAuto.setEnabled(external && icfg.supportsBuild(true));
		if (b_expandVars.getEnabled())
			b_expandVars.setEnabled(external && b_genMakefileAuto.getSelection());

		if (external) { // just set relatet text widget state,
			checkPressed(b_useDefault, false); // do not update
		}
		canModify = true;
	}

	private Button setupBottomButton(Composite c, String name) {
		Button b = new Button(c, SWT.PUSH);
		b.setText(name);
		GridData fd = new GridData(GridData.CENTER);
		fd.minimumWidth = BUTTON_WIDTH;
		b.setLayoutData(fd);
		b.setData(t_dir);
		b.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent event) {
				buttonVarPressed(event);
			}
		});
		return b;
	}

	/**
	 * Sets up text + corresponding button
	 * Checkbox can be implemented either by Button or by TriButton
	 */
	private Text setupBlock(Composite c, Control check) {
		Text t = setupText(c, 1, GridData.FILL_HORIZONTAL);
		Button b = setupButton(c, VARIABLESBUTTON_NAME, 1, GridData.END);
		b.setData(t); // to get know which text is affected
		t.setData(b); // to get know which button to enable/disable
		b.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent event) {
				buttonVarPressed(event);
			}
		});
		if (check != null)
			check.setData(t);
		return t;
	}

	/*
	 * Unified handler for "Variables" buttons
	 */
	private void buttonVarPressed(SelectionEvent e) {
		Widget b = e.widget;
		if (b == null || b.getData() == null)
			return;
		if (b.getData() instanceof Text) {
			String x = null;
			if (b.equals(b_dirWsp)) {
				x = getWorkspaceDirDialog(usercomp.getShell(), EMPTY_STR);
				if (x != null)
					((Text) b.getData()).setText(x);
			} else if (b.equals(b_dirFile)) {
				x = getFileSystemDirDialog(usercomp.getShell(), EMPTY_STR);
				if (x != null)
					((Text) b.getData()).setText(x);
			} else {
				x = AbstractCPropertyTab.getVariableDialog(usercomp.getShell(), getResDesc().getConfiguration());
				if (x != null)
					((Text) b.getData()).insert(x);
			}
		}
	}

	@Override
	public void checkPressed(SelectionEvent e) {
		checkPressed((Control) e.widget, true);
		updateButtons();
	}

	private void checkPressed(Control b, boolean needUpdate) {
		if (b == null)
			return;

		boolean val = false;
		if (b instanceof Button)
			val = ((Button) b).getSelection();

		if (b.getData() instanceof Text) {
			Text t = (Text) b.getData();
			if (b == b_useDefault) {
				val = !val;
			}
			t.setEnabled(val);
			if (t.getData() != null && t.getData() instanceof Control) {
				Control c = (Control) t.getData();
				c.setEnabled(val);
			}
		}
		// call may be used just to set text state above
		// in this case, settings update is not required
		if (!needUpdate)
			return;

		if (b == b_useDefault) {
			setUseDefaultBuildCmd(!val);
		} else if (b == b_genMakefileAuto) {
			setManagedBuild(val);
		} else if (b == b_expandVars) {
			if (bldr.canKeepEnvironmentVariablesInBuildfile())
				setKeepEnvironmentVariablesInBuildfile(!val);
		}
	}

	/**
	 * @return make command
	 */
	private String getMakeCommand() {
		String makeCommand = icfg.getBuildCommand();
		String makeArgs = icfg.getBuildArguments();

		if (!makeCommand.isEmpty() && makeArgs != null) {
			return makeCommand + " " + makeArgs; //$NON-NLS-1$
		}
		return EMPTY_STR;
	}

	/**
	 * Performs common settings for all controls
	 * (Copy from config to widgets)
	 */
	@Override
	public void updateData(ICResourceDescription cfgd) {
		if (cfgd == null)
			return;
		icfg = getCfg(cfgd.getConfiguration());
		updateButtons();
	}

	@Override
	public void performApply(ICResourceDescription src, ICResourceDescription dst) {
		BuildBehaviourTab.apply(src, dst, page.isMultiCfg());
	}

	/* (non-Javadoc)
	 *
	 * @param string
	 * @return
	 */
	private String parseMakeCommand(String rawCommand) {
		String[] result = rawCommand.split("\\s"); //$NON-NLS-1$
		if (result != null && result.length > 0)
			return result[0];
		else
			return rawCommand;

	}

	// This page can be displayed for project only
	@Override
	public boolean canBeVisible() {
		return page.isForProject() || page.isForPrefs();
	}

	@Override
	public void setVisible(boolean b) {
		super.setVisible(b);
	}

	@Override
	protected void performDefaults() {
		if (icfg instanceof IMultiConfiguration) {
			IConfiguration[] cfs = (IConfiguration[]) ((IMultiConfiguration) icfg).getItems();
			for (int i = 0; i < cfs.length; i++) {
				IBuilder b = cfs[i].getEditableBuilder();
				BuildBehaviourTab.copyBuilders(b.getSuperClass(), b);
			}
		} else
			BuildBehaviourTab.copyBuilders(bldr.getSuperClass(), bldr);
		updateData(getResDesc());
	}

	private boolean canEnableInternalBuilder(boolean v) {
		if (icfg instanceof Configuration)
			return ((Configuration) icfg).canEnableInternalBuilder(v);
		if (icfg instanceof IMultiConfiguration)
			return ((IMultiConfiguration) icfg).canEnableInternalBuilder(v);
		return false;
	}

	private void enableInternalBuilder(boolean v) {
		if (icfg instanceof Configuration)
			((Configuration) icfg).enableInternalBuilder(v);
		if (icfg instanceof IMultiConfiguration)
			((IMultiConfiguration) icfg).enableInternalBuilder(v);
	}

	private boolean isInternalBuilderEnabled() {
		if (icfg instanceof Configuration)
			return ((Configuration) icfg).isInternalBuilderEnabled();
		if (icfg instanceof IMultiConfiguration)
			return ((MultiConfiguration) icfg).isInternalBuilderEnabled();
		return false;
	}

	private void setUseDefaultBuildCmd(boolean val) {
		try {
			if (icfg instanceof IMultiConfiguration) {
				IConfiguration[] cfs = (IConfiguration[]) ((IMultiConfiguration) icfg).getItems();
				for (int i = 0; i < cfs.length; i++) {
					IBuilder b = cfs[i].getEditableBuilder();
					if (b != null)
						b.setUseDefaultBuildCmd(val);
				}
			} else {
				icfg.getEditableBuilder().setUseDefaultBuildCmd(val);
			}
		} catch (CoreException e) {
			ManagedBuilderUIPlugin.log(e);
		}
	}

	private void setKeepEnvironmentVariablesInBuildfile(boolean val) {
		if (icfg instanceof IMultiConfiguration) {
			IConfiguration[] cfs = (IConfiguration[]) ((IMultiConfiguration) icfg).getItems();
			for (int i = 0; i < cfs.length; i++) {
				IBuilder b = cfs[i].getEditableBuilder();
				if (b != null)
					b.setKeepEnvironmentVariablesInBuildfile(val);
			}
		} else {
			icfg.getEditableBuilder().setKeepEnvironmentVariablesInBuildfile(val);
		}
	}

	private void setCommand(String buildCommand) {
		if (icfg instanceof IMultiConfiguration) {
			IConfiguration[] cfs = (IConfiguration[]) ((IMultiConfiguration) icfg).getItems();
			for (int i = 0; i < cfs.length; i++) {
				IBuilder b = cfs[i].getEditableBuilder();
				b.setCommand(buildCommand);
			}
		} else {
			icfg.getEditableBuilder().setCommand(buildCommand);
		}
	}

	private void setArguments(String makeArgs) {
		if (icfg instanceof IMultiConfiguration) {
			IConfiguration[] cfs = (IConfiguration[]) ((IMultiConfiguration) icfg).getItems();
			for (int i = 0; i < cfs.length; i++) {
				IBuilder b = cfs[i].getEditableBuilder();
				b.setArguments(makeArgs);
			}
		} else {
			icfg.getEditableBuilder().setArguments(makeArgs);
		}
	}

	private void setBuildPath(String path) {
		if (icfg instanceof IMultiConfiguration) {
			IConfiguration[] cfs = (IConfiguration[]) ((IMultiConfiguration) icfg).getItems();
			for (int i = 0; i < cfs.length; i++) {
				IBuilder b = cfs[i].getEditableBuilder();
				b.setBuildPath(path);
			}
		} else {
			icfg.getEditableBuilder().setBuildPath(path);
		}
	}

	private void setManagedBuildOn(boolean on) {
		try {
			if (icfg instanceof IMultiConfiguration) {
				IConfiguration[] cfs = (IConfiguration[]) ((IMultiConfiguration) icfg).getItems();
				for (int i = 0; i < cfs.length; i++) {
					IBuilder b = cfs[i].getEditableBuilder();
					b.setManagedBuildOn(on);
				}
			} else {
				icfg.getEditableBuilder().setManagedBuildOn(on);
			}
		} catch (CoreException e) {
			ManagedBuilderUIPlugin.log(e);
		}
	}
}
