package org.dbgl.gui.controls;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import org.apache.commons.lang3.StringUtils;
import org.dbgl.gui.interfaces.DaControlConvertor;
import org.dbgl.model.NativeCommand;
import org.dbgl.model.aggregate.Profile;
import org.dbgl.model.aggregate.Template;
import org.dbgl.model.conf.Autoexec;
import org.dbgl.model.conf.Configuration;
import org.dbgl.model.conf.Settings;
import org.dbgl.model.entity.TemplateProfileBase;
import org.dbgl.service.SettingsService;
import org.dbgl.service.TextService;
import org.dbgl.util.SystemUtils;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Color;
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.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Scale;
import org.eclipse.swt.widgets.Spinner;
import org.eclipse.swt.widgets.Text;


public class Chain {

	@FunctionalInterface
	public interface TriConsumer<T, U, V> {
		public void accept(T t, U u, V v);

		public default TriConsumer<T, U, V> andThen(TriConsumer<? super T, ? super U, ? super V> after) {
			Objects.requireNonNull(after);
			return (a, b, c) -> {
				accept(a, b, c);
				after.accept(a, b, c);
			};
		}
	}

	private final Label[] labels_;
	private final Control[] controls_;
	private final Text[] texts_;
	private final Combo[] combos_;
	private final Button[] buttons_;
	private final org.eclipse.swt.widgets.List[] lists_;
	private final Spinner[] spinners_;
	private final Scale[] scales_;

	private final Function<TemplateProfileBase, List<NativeCommand>> nativeCommandsGetter_;
	private final BiConsumer<TemplateProfileBase, List<NativeCommand>> nativeCommandsUpdater_;
	private final Function<Template, String> templateStringGetter_;
	private final BiConsumer<Template, String> templateStringUpdater_;
	private final Function<Profile, String> profileStringGetter_;
	private final BiFunction<Profile, Integer, String> profileIndexedStringGetter_;
	private final BiConsumer<Profile, String> profileStringUpdater_;
	private final TriConsumer<Profile, Integer, String> profileIndexedStringUpdater_;
	private final Function<Autoexec, String> autoexecStringGetter_;
	private final BiConsumer<Autoexec, String> autoexecStringUpdater_;
	private final BiFunction<Autoexec, Integer, String> autoexecIndexedStringGetter_;
	private final TriConsumer<Autoexec, Integer, String> autoexecIndexedStringUpdater_;
	private final int index_;

	private final String section_, sectionNew_, item_, itemNew_;
	private final boolean isOnOff_;
	private final DaControlConvertor convertor_;

	private boolean isMultiEdit_, initialValueSet_, conflictingValues_;
	private Object initialValue_, currentValue_;

	public static final class Builder {

		private static final DaControlConvertor defaultConvertor = new DaControlConvertorAdapter() {
		};

		private final Composite composite_;
		private final List<Label> labels_;
		private final List<Control> controls_;
		private final List<Text> texts_;
		private final List<Combo> combos_;
		private final List<Button> buttons_;
		private final List<org.eclipse.swt.widgets.List> lists_;
		private final List<Spinner> spinners_;
		private final List<Scale> scales_;

		private Function<TemplateProfileBase, List<NativeCommand>> nativeCommandsGetter_;
		private BiConsumer<TemplateProfileBase, List<NativeCommand>> nativeCommandsUpdater_;
		private Function<Template, String> templateStringGetter_;
		private BiConsumer<Template, String> templateStringUpdater_;
		private Function<Profile, String> profileStringGetter_;
		private BiFunction<Profile, Integer, String> profileIndexedStringGetter_;
		private BiConsumer<Profile, String> profileStringUpdater_;
		private TriConsumer<Profile, Integer, String> profileIndexedStringUpdater_;
		private Function<Autoexec, String> autoexecStringGetter_;
		private BiConsumer<Autoexec, String> autoexecStringUpdater_;
		private BiFunction<Autoexec, Integer, String> autoexecIndexedStringGetter_;
		private TriConsumer<Autoexec, Integer, String> autoexecIndexedStringUpdater_;
		private int index_;

		private String section_, sectionNew_, item_, itemNew_;
		private boolean isOnOff_;
		private DaControlConvertor convertor_ = defaultConvertor;

		public Builder(Composite composite) {
			composite_ = composite;
			controls_ = new ArrayList<>();
			labels_ = new ArrayList<>();
			texts_ = new ArrayList<>();
			combos_ = new ArrayList<>();
			buttons_ = new ArrayList<>();
			lists_ = new ArrayList<>();
			spinners_ = new ArrayList<>();
			scales_ = new ArrayList<>();
		}

		public Builder lbl(UnaryOperator<Label_.Builder> lb) {
			labels_.add(lb.apply(Label_.on(composite_)).ctrl());
			return this;
		}

		public Builder txt(UnaryOperator<Text_.Builder> tb) {
			texts_.add(tb.apply(Text_.on(composite_)).ctrl());
			controls_.add(texts_.get(texts_.size() - 1));
			return this;
		}

		public Builder cmb(UnaryOperator<Combo_.Builder> cb) {
			combos_.add(cb.apply(Combo_.on(composite_)).ctrl());
			controls_.add(combos_.get(combos_.size() - 1));
			return this;
		}

		public Builder but(UnaryOperator<Button_.Builder> bb) {
			TextControl_ textControl = !texts_.isEmpty() ? new TextControl_(texts_.get(texts_.size() - 1)): !combos_.isEmpty() ? new TextControl_(combos_.get(combos_.size() - 1)): null;
			Button button = bb.apply(Button_.on(composite_)).ctrl(textControl);
			buttons_.add(button);
			if (IntStream.of(SWT.RADIO, SWT.TOGGLE, SWT.CHECK).anyMatch(x -> (button.getStyle() & x) == x))
				controls_.add(button);
			return this;
		}

		public Builder lst(UnaryOperator<List_.Builder> lb) {
			lists_.add(lb.apply(List_.on(composite_)).ctrl());
			controls_.add(lists_.get(lists_.size() - 1));
			return this;
		}

		public Builder spn(UnaryOperator<Spinner_.Builder> sb) {
			spinners_.add(sb.apply(Spinner_.on(composite_)).ctrl());
			controls_.add(spinners_.get(spinners_.size() - 1));
			return this;
		}

		public Builder scl(UnaryOperator<Scale_.Builder> sb) {
			scales_.add(sb.apply(Scale_.on(composite_)).ctrl());
			controls_.add(scales_.get(scales_.size() - 1));
			return this;
		}

		public Builder section(String section) {
			return section(section, null);
		}

		public Builder section(String sectionOld, String sectionNew) {
			section_ = sectionOld;
			sectionNew_ = sectionNew;
			return this;
		}

		public Builder item(String item) {
			return item(item, null);
		}

		public Builder item(String itemOld, String itemNew) {
			item_ = itemOld;
			itemNew_ = itemNew;
			return this;
		}

		public Builder onOff() {
			isOnOff_ = true;
			return this;
		}

		public Builder convert(DaControlConvertor convertor) {
			convertor_ = convertor;
			return this;
		}

		public Builder nativeCommands(Function<TemplateProfileBase, List<NativeCommand>> getMethod, BiConsumer<TemplateProfileBase, List<NativeCommand>> updateMethod) {
			nativeCommandsGetter_ = getMethod;
			nativeCommandsUpdater_ = updateMethod;
			return this;
		}

		public Builder template(Function<Template, String> getMethod, BiConsumer<Template, String> updateMethod) {
			templateStringGetter_ = getMethod;
			templateStringUpdater_ = updateMethod;
			return this;
		}

		public Builder profile(Function<Profile, String> getMethod, BiConsumer<Profile, String> updateMethod) {
			profileStringGetter_ = getMethod;
			profileStringUpdater_ = updateMethod;
			return this;
		}

		public Builder profile(int i, BiFunction<Profile, Integer, String> getMethod, TriConsumer<Profile, Integer, String> updateMethod) {
			index_ = i;
			profileIndexedStringGetter_ = getMethod;
			profileIndexedStringUpdater_ = updateMethod;
			return this;
		}

		public Builder autoexec(Function<Autoexec, String> getMethod, BiConsumer<Autoexec, String> updateMethod) {
			autoexecStringGetter_ = getMethod;
			autoexecStringUpdater_ = updateMethod;
			return this;
		}

		public Builder autoexec(int i, BiFunction<Autoexec, Integer, String> getMethod, TriConsumer<Autoexec, Integer, String> updateMethod) {
			index_ = i;
			autoexecIndexedStringGetter_ = getMethod;
			autoexecIndexedStringUpdater_ = updateMethod;
			return this;
		}

		public Chain build(List<Chain> chains) {
			Chain chain = build();
			chains.add(chain);
			return chain;
		}

		public Chain build() {
			return new Chain(this);
		}

		public Label label() {
			return build().getLabel();
		}

		public Text text() {
			return build().getText();
		}

		public Combo combo() {
			return build().getCombo();
		}

		public Button button() {
			return build().getButton();
		}

		public org.eclipse.swt.widgets.List list() {
			return build().getList();
		}

		public Spinner spinner() {
			return build().getSpinner();
		}

		public Scale scale() {
			return build().getScale();
		}
	}

	public Chain(Builder builder) {
		labels_ = builder.labels_.toArray(new Label[builder.labels_.size()]);
		controls_ = builder.controls_.toArray(new Control[builder.controls_.size()]);
		texts_ = builder.texts_.toArray(new Text[builder.texts_.size()]);
		combos_ = builder.combos_.toArray(new Combo[builder.combos_.size()]);
		buttons_ = builder.buttons_.toArray(new Button[builder.buttons_.size()]);
		lists_ = builder.lists_.toArray(new org.eclipse.swt.widgets.List[builder.lists_.size()]);
		spinners_ = builder.spinners_.toArray(new Spinner[builder.spinners_.size()]);
		scales_ = builder.scales_.toArray(new Scale[builder.scales_.size()]);

		nativeCommandsGetter_ = builder.nativeCommandsGetter_;
		nativeCommandsUpdater_ = builder.nativeCommandsUpdater_;
		templateStringGetter_ = builder.templateStringGetter_;
		templateStringUpdater_ = builder.templateStringUpdater_;
		profileStringGetter_ = builder.profileStringGetter_;
		profileStringUpdater_ = builder.profileStringUpdater_;
		profileIndexedStringGetter_ = builder.profileIndexedStringGetter_;
		profileIndexedStringUpdater_ = builder.profileIndexedStringUpdater_;
		autoexecStringGetter_ = builder.autoexecStringGetter_;
		autoexecStringUpdater_ = builder.autoexecStringUpdater_;
		autoexecIndexedStringGetter_ = builder.autoexecIndexedStringGetter_;
		autoexecIndexedStringUpdater_ = builder.autoexecIndexedStringUpdater_;
		index_ = builder.index_;

		section_ = builder.section_;
		sectionNew_ = builder.sectionNew_;
		item_ = builder.item_;
		itemNew_ = builder.itemNew_;
		isOnOff_ = builder.isOnOff_;
		convertor_ = builder.convertor_;
	}

	public static Builder on(Composite composite) {
		return new Builder(composite);
	}

	public void multiEdit() {
		isMultiEdit_ = true;
	}

	public void bindListenersAndSetLabelColor() {
		final ModifyListener modifyListener = event -> updateLabelColor();

		final SelectionAdapter selectionListener = new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent event) {
				if ((event.widget instanceof Button) && conflictingValues_) {
					Button button = (Button)event.widget;
					if ((button.getStyle() & SWT.CHECK) == SWT.CHECK) {
						if (button.getSelection()) {
							if (button.getGrayed()) {
								button.setSelection(false);
							}
						} else {
							if (button.getGrayed()) {
								button.setGrayed(false);
							} else {
								button.setGrayed(true);
								button.setSelection(true);
							}
						}
					}
				}
				updateLabelColor();
			}
		};

		for (Control cntrl: controls_) {
			if (cntrl instanceof Combo)
				((Combo)cntrl).addModifyListener(modifyListener);
			else if (cntrl instanceof Text)
				((Text)cntrl).addModifyListener(modifyListener);
			else if (cntrl instanceof Button)
				((Button)cntrl).addSelectionListener(selectionListener);
			else if (cntrl instanceof Spinner)
				((Spinner)cntrl).addModifyListener(modifyListener);
			else if (cntrl instanceof Scale)
				((Scale)cntrl).addSelectionListener(selectionListener);
			else if (cntrl instanceof org.eclipse.swt.widgets.List)
				((org.eclipse.swt.widgets.List)cntrl).addSelectionListener(selectionListener);
		}

		updateLabelColor();
	}

	private void updateLabelColor() {

		if (hasChangedValue()) {
			Color color = DarkTheme.forced() ? DarkTheme.changedForeground: Display.getDefault().getSystemColor(SWT.COLOR_RED);
			getLabel().setForeground(color);
			getLabel().setToolTipText(TextService.getInstance().get("dialog.multiprofile.title.alteredvalue"));
		} else if (conflictingValues_) {
			Color color = DarkTheme.forced() ? DarkTheme.conflictingForeground: Display.getDefault().getSystemColor(SWT.COLOR_DARK_RED);
			getLabel().setForeground(color);
			getLabel().setToolTipText(TextService.getInstance().get("dialog.multiprofile.title.conflictingvalues"));
		} else {
			Color color = DarkTheme.forced() ? DarkTheme.defaultForeground: Display.getDefault().getSystemColor(SWT.COLOR_WIDGET_FOREGROUND);
			getLabel().setForeground(color);
			getLabel().setToolTipText(TextService.getInstance().get("dialog.multiprofile.title.unalteredvalue"));
		}
	}

	public Label[] getLabels() {
		return labels_;
	}

	public Label getLabel() {
		return labels_[0];
	}

	public Text getText() {
		return texts_[0];
	}

	public Combo[] getCombos() {
		return combos_;
	}

	public Combo getCombo() {
		return combos_[0];
	}

	public Button[] getButtons() {
		return buttons_;
	}

	public Button getButton() {
		return buttons_[0];
	}

	public org.eclipse.swt.widgets.List getList() {
		return lists_[0];
	}

	public Spinner getSpinner() {
		return spinners_[0];
	}

	public Scale[] getScales() {
		return scales_;
	}

	public Scale getScale() {
		return scales_[0];
	}

	public void enableOrDisableControl(TemplateProfileBase configurable) {
		if (StringUtils.isNotBlank(section_)) {
			Configuration conf = configurable.getDosboxVersion().getConfiguration();
			boolean dosboxHasConfSetting = conf.hasValue(confSection(conf), confItem(conf));
			Stream.of(controls_).forEach(x -> x.setEnabled(dosboxHasConfSetting));
			labels_[0].setEnabled(dosboxHasConfSetting);
		}
	}

	public void setControlByConfigurable(TemplateProfileBase configurable, Configuration combinedConf) {
		if (nativeCommandsGetter_ != null)
			setControlByNativeCommands(nativeCommandsGetter_.apply(configurable));
		else if (templateStringGetter_ != null)
			setControlByStringValue(templateStringGetter_.apply((Template)configurable));
		else if (profileStringGetter_ != null)
			setControlByStringValue(profileStringGetter_.apply((Profile)configurable));
		else if (profileIndexedStringGetter_ != null)
			setControlByStringValue(profileIndexedStringGetter_.apply((Profile)configurable, index_));
		else if (autoexecStringGetter_ != null)
			setControlByStringValue(autoexecStringGetter_.apply(isMultiEdit_ ? configurable.getConfiguration().getAutoexec(): combinedConf.getAutoexec()));
		else if (autoexecIndexedStringGetter_ != null)
			setControlByStringValue(autoexecIndexedStringGetter_.apply(isMultiEdit_ ? configurable.getConfiguration().getAutoexec(): combinedConf.getAutoexec(), index_));
		else if (StringUtils.isNotBlank(section_)) {
			if (allControlsDisabled())
				return;

			Configuration dosboxConf = configurable.getDosboxVersion().getConfiguration();
			Configuration conf = isMultiEdit_ ? configurable.getConfiguration(): combinedConf;
			String[] values = conf.hasValue(section_, item_) && conf.hasValue(sectionNew_, itemNew_)
					? convertor_.toControlValues(new String[] {conf.getValue(section_, item_), conf.getValue(sectionNew_, itemNew_)})
					: convertor_.toControlValues(conf.getValue(confSection(dosboxConf), confItem(dosboxConf)));

			if ((values == null) || (values.length != controls_.length)) {
				if (!isMultiEdit_) {
					Stream.of(controls_).forEach(x -> x.setEnabled(false));
					System.err.println(toString() + ": control disabled because of configuration mismatch");
					return;
				}
			} else {
				for (int i = 0; i < controls_.length; i++)
					setFieldValue(controls_[i], values[i]);
			}

			if (!initialValueSet_) {
				initialValue_ = getCurrentStringValue();
				initialValueSet_ = true;
				conflictingValues_ = (values == null) || (values.length == 0);
			}
		}
	}

	public void updateConfigurableByControl(TemplateProfileBase configurable, Configuration combinedConf) {
		if (nativeCommandsUpdater_ != null)
			nativeCommandsUpdater_.accept(configurable, getCurrentNativeCommands());
		else if (templateStringUpdater_ != null)
			templateStringUpdater_.accept((Template)configurable, getCurrentStringValue());
		else if (profileStringUpdater_ != null)
			profileStringUpdater_.accept((Profile)configurable, getCurrentStringValue());
		else if (profileIndexedStringUpdater_ != null)
			profileIndexedStringUpdater_.accept((Profile)configurable, index_, getCurrentStringValue());
		else if (autoexecStringUpdater_ != null)
			autoexecStringUpdater_.accept(configurable.getConfiguration().getAutoexec(), getCurrentStringValue());
		else if (autoexecIndexedStringUpdater_ != null)
			autoexecIndexedStringUpdater_.accept(configurable.getConfiguration().getAutoexec(), index_, getCurrentStringValue());
		else if (StringUtils.isNotBlank(section_)) {
			String[] fieldValues = Stream.of(controls_).map(this::getFieldValue).filter(Objects::nonNull).toArray(String[]::new);
			if (fieldValues.length == 0)
				return;

			if (combinedConf.hasValue(section_, item_) && combinedConf.hasValue(sectionNew_, itemNew_)) {
				String[] confValues = convertor_.toConfValues(fieldValues);
				configurable.setValue(section_, item_, confValues[0]);
				configurable.setValue(sectionNew_, itemNew_, confValues[1]);
			} else {
				Configuration dosboxConf = configurable.getDosboxVersion().getConfiguration();
				String value = convertor_.toConfValue(fieldValues);
				configurable.setValue(confSection(dosboxConf), confItem(dosboxConf), value);
			}
		}
	}

	public void setControlByNativeCommands(List<NativeCommand> cmds) {
		if (cmds == null) {
			conflictingValues_ = true;
			return;
		}

		currentValue_ = cmds;
		org.eclipse.swt.widgets.List nativeCommandsList = ((org.eclipse.swt.widgets.List)controls_[0]);
		nativeCommandsList.setItems(cmds.stream().map(NativeCommand::toString).toArray(String[]::new));
		nativeCommandsList.notifyListeners(SWT.Selection, new Event());

		if (!initialValueSet_) {
			initialValue_ = cmds;
			initialValueSet_ = true;
		}
	}

	private void setControlByStringValue(String value) {
		setFieldValue(controls_[0], value);

		if (!initialValueSet_) {
			initialValue_ = getCurrentStringValue();
			initialValueSet_ = true;
			conflictingValues_ = (value == null);
		}
	}

	@SuppressWarnings("unchecked")
	public String getInitialNativeCommandsAsString() {
		return nativeCommandsToString((List<NativeCommand>)initialValue_);
	}

	public String getInitialStringValue() {
		return (String)initialValue_;
	}

	@SuppressWarnings("unchecked")
	public List<NativeCommand> getCurrentNativeCommands() {
		return (List<NativeCommand>)currentValue_;
	}

	public String getCurrentNativeCommandsAsString() {
		return nativeCommandsToString(getCurrentNativeCommands());
	}

	private static String nativeCommandsToString(List<NativeCommand> obj) {
		if (obj != null)
			return obj.stream().map(NativeCommand::toString).collect(Collectors.joining("; "));
		return StringUtils.EMPTY;
	}

	public String getCurrentStringValue() {
		String[] values = Stream.of(controls_).map(this::getFieldValue).filter(Objects::nonNull).toArray(String[]::new);
		return values.length == controls_.length ? convertor_.toConfValue(values): null;
	}

	public boolean conflictingValues() {
		return conflictingValues_;
	}

	@SuppressWarnings("unchecked")
	public boolean hasChangedValue() {
		if (allControlsDisabled())
			return false;
		else if (nativeCommandsGetter_ != null)
			return (List<NativeCommand>)initialValue_ != getCurrentNativeCommands();
		else
			return !StringUtils.equals(getInitialStringValue(), getCurrentStringValue());
	}

	private String getFieldValue(Control control) {
		if (control.isEnabled()) {
			if (control instanceof Text) {
				String contents = ((Text)control).getText();
				String del = ((Text)control).getLineDelimiter();
				return StringUtils.replace(StringUtils.strip(contents, del), del, SystemUtils.EOLN);
			} else if (control instanceof Combo) {
				return ((Combo)control).getText();
			} else if (control instanceof Button && !((Button)control).getGrayed()) {
				boolean v = ((Button)control).getSelection();
				return isOnOff_ ? (v ? "on": "off"): String.valueOf(v);
			} else if (control instanceof Scale) {
				return String.valueOf(((Scale)control).getSelection());
			} else if (control instanceof Spinner) {
				return String.valueOf(((Spinner)control).getSelection());
			}
		}
		return null;
	}

	private void setFieldValue(Control control, String value) {
		if (control.isEnabled()) {
			if (value == null) {
				if ((control instanceof Button) && ((((Button)control).getStyle() & SWT.CHECK) == SWT.CHECK)) {
					((Button)control).setSelection(true);
					((Button)control).setGrayed(true);
				}
			} else {
				if (control instanceof Text) {
					String newValue = StringUtils.replace(value, SystemUtils.EOLN, ((Text)control).getLineDelimiter());
					if (!((Text)control).getText().equals(newValue))
						((Text)control).setText(newValue);
				} else if (control instanceof Combo) {
					if (!((Combo)control).getText().equals(value))
						((Combo)control).setText(value);
				} else if (control instanceof Button) {
					boolean newValue = isOnOff_ ? "on".equalsIgnoreCase(value): Boolean.valueOf(value);
					if ((((Button)control).getSelection() != newValue) || ((Button)control).getGrayed()) {
						((Button)control).setSelection(newValue);
						((Button)control).notifyListeners(SWT.Selection, new Event());
					}
				} else if (control instanceof Scale) {
					Integer newValue = Integer.valueOf(value);
					if (((Scale)control).getSelection() != newValue)
						((Scale)control).setSelection(newValue);
				} else if (control instanceof Spinner) {
					Integer newValue = Integer.valueOf(value);
					if (((Spinner)control).getSelection() != newValue)
						((Spinner)control).setSelection(newValue);
				}
			}
		}
	}

	public void setComboValues(Map<String, String> map) {
		Stream.of(getCombos()).forEach(x -> {
			String section = (String)x.getData(Combo_.DYN_OPT_SECTION);
			String item = (String)x.getData(Combo_.DYN_OPT_ITEM);
			if (StringUtils.isNoneBlank(section, item)) {
				x.setItems(map == null ? SettingsService.getInstance().getValues(section, item): Settings.splitValues(map.get(item)));
			}
		});
	}

	private String confSection(Configuration conf) {
		return StringUtils.isNotBlank(sectionNew_) && StringUtils.isNotBlank(itemNew_) && conf.hasValue(sectionNew_, itemNew_) ? sectionNew_: section_;
	}

	private String confItem(Configuration conf) {
		return StringUtils.isNotBlank(itemNew_) && (conf.hasValue(section_, itemNew_) || (StringUtils.isNotBlank(sectionNew_) && conf.hasValue(sectionNew_, itemNew_))) ? itemNew_: item_;
	}

	private boolean allControlsDisabled() {
		return Stream.of(controls_).noneMatch(Control::isEnabled);
	}

	@Override
	public String toString() {
		StringBuilder result = new StringBuilder(getLabel().getText());
		if (StringUtils.isNotBlank(section_)) {
			result.append(" ([");
			result.append(StringUtils.isNotBlank(sectionNew_) ? sectionNew_: section_);
			result.append("] ").append(StringUtils.isNotBlank(itemNew_) ? itemNew_: item_);
			result.append(")");
		}
		return result.toString();
	}
}
