/*
 *  Copyright (C) 2006-2020  Ronald Blankendaal
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
package org.dbgl.gui.controls;

import java.util.stream.Stream;
import org.apache.commons.lang3.StringUtils;
import org.dbgl.gui.interfaces.DaControlConvertor;
import org.dbgl.model.aggregate.DosboxVersion;
import org.dbgl.model.conf.Configuration;
import org.dbgl.model.entity.TemplateProfileBase;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;


public class DaControl extends ChangeTrackingControl {

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

	private DaControl(Builder builder) {
		super(builder.label_, builder.controls_);
		section_ = builder.section_;
		sectionNew_ = builder.sectionNew_;
		item_ = builder.item_;
		itemNew_ = builder.itemNew_;
		isOnOff_ = builder.isOnOff_;
		convertor_ = builder.convertor_;
	}

	@Override
	public String getCurrentValue() {
		String[] fieldValues = Stream.of(controls_).map(x -> getFieldValue(x, isOnOff_)).filter(x -> x != null).toArray(String[]::new);
		return fieldValues.length == controls_.length ? convertor_.toConfValue(fieldValues): null;
	}

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

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

	public String getConfLabel() {
		StringBuffer result = new StringBuffer("[");
		result.append(StringUtils.isNotBlank(sectionNew_) ? sectionNew_: section_);
		result.append("] ");
		result.append(StringUtils.isNotBlank(itemNew_) ? itemNew_: item_);
		return result.toString();
	}

	public void enableOrDisable(DosboxVersion dosbox) {
		Configuration configuration = dosbox.getConfiguration();
		boolean dosboxHasConfigurationSetting = configuration.hasValue(getSectionInConf(configuration), getItemInConf(configuration));
		Stream.of(controls_).forEach(x -> x.setEnabled(dosboxHasConfigurationSetting));
	}

	public void setControlByConf(Configuration dosboxConf, Configuration overrideConf, Configuration combinedConf, boolean multiEdit) {
		if (allControlsDisabled())
			return;

		String[] values = determineValues(dosboxConf, multiEdit ? overrideConf: combinedConf);

		if ((values.length == 0) && multiEdit) {
			setOriginalValue(null, multiEdit);
		} else if (values.length != controls_.length) {
			throw new RuntimeException("invalid DaControl definition");
		} else {
			for (int i = 0; i < controls_.length; i++)
				setFieldIfEnabled(values[i], controls_[i], isOnOff_);
			setOriginalValue(getCurrentValue(), multiEdit);
		}
	}

	private String[] determineValues(Configuration dosboxConf, Configuration combinedConf) {
		if (combinedConf.hasValue(section_, item_) && combinedConf.hasValue(sectionNew_, itemNew_))
			return convertor_.toControlValues(new String[] {combinedConf.getValue(section_, item_), combinedConf.getValue(sectionNew_, itemNew_)});
		else
			return convertor_.toControlValues(combinedConf.getValue(getSectionInConf(dosboxConf), getItemInConf(dosboxConf)));
	}

	public void updateConfigurationByControl(TemplateProfileBase configurable) {
		DosboxVersion dosbox = configurable.getDosboxVersion();
		Configuration dosboxConf = dosbox.getConfiguration();
		Configuration combinedConf = configurable.getCombinedConfiguration();

		String[] fieldValues = Stream.of(controls_).map(x -> getFieldValue(x, isOnOff_)).filter(x -> x != null).toArray(String[]::new);

		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 {
			String value = convertor_.toConfValue(fieldValues);
			configurable.setValue(getSectionInConf(dosboxConf), getItemInConf(dosboxConf), value);
		}
	}

	public static Builder builder() {
		return new Builder();
	}

	public static final class Builder {

		private Label label_;
		private Control[] controls_;
		private String section_;
		private String sectionNew_;
		private String item_;
		private String itemNew_;
		private boolean isOnOff_;
		private DaControlConvertor convertor_ = defaultConvertor;

		public Builder section(String section) {
			section_ = section;
			return this;
		}

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

		public Builder item(String item) {
			item_ = item;
			return this;
		}

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

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

		public Builder label(Label label) {
			label_ = label;
			return this;
		}

		public Builder controls(Control[] controls) {
			controls_ = controls;
			return this;
		}

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

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