/*
 *  Copyright (C) 2006-2019  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.abstractdialog;

import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Stream;
import org.dbgl.gui.GeneralPurposeDialogs;
import org.dbgl.gui.controls.AutoSelectCombo;
import org.dbgl.gui.controls.BrowseButton;
import org.dbgl.gui.controls.BrowseButton.BrowseType;
import org.dbgl.gui.controls.BrowseButton.CanonicalType;
import org.dbgl.gui.controls.GrabButton;
import org.dbgl.gui.controls.MetaControl;
import org.dbgl.gui.controls.MetaControl.TriConsumer;
import org.dbgl.gui.controls.SearchEngineSelector;
import org.dbgl.model.SearchEngineImageInformation;
import org.dbgl.model.aggregate.Profile;
import org.dbgl.model.aggregate.Template;
import org.dbgl.model.conf.Autoexec;
import org.dbgl.model.entity.ITitledEntity;
import org.dbgl.model.entity.TemplateProfileBase;
import org.dbgl.model.repository.TemplateRepository;
import org.dbgl.model.repository.TitledEntityRepository;
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.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Scale;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Spinner;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.ToolBar;


public abstract class EditProfileDialog<T> extends EditConfigurableDialog<T> {

	protected List<ITitledEntity> developersList_, publishersList_, genresList_, yearsList_, statusList_;
	protected List<List<ITitledEntity>> customList_;
	protected List<Template> templatesList_;

	protected Text title_, notes_;
	protected SearchEngineSelector engineSelector_;
	protected Text[] link_, linkTitle_;
	protected BrowseButton[] linkBrowseButton_;
	protected AutoSelectCombo developer_, publisher_, genre_, year_;
	protected Scale custom9_;
	protected Combo templateCombo_;
	protected Button loadfix_;
	protected Combo loadfixValue_;
	protected Text img1_, img2_, img3_, main_, setup_;
	protected Composite webImagesSpaceHolder_;
	protected SearchEngineImageInformation[] imageInformation_;
	protected Button[] imgButtons_;

	protected int templateIndex_;

	public EditProfileDialog(Shell parent) {
		super(parent, "profiledialog");
	}

	@Override
	protected boolean prepare() {
		if (!super.prepare())
			return false;

		try {
			TemplateRepository templRepo = new TemplateRepository();
			TitledEntityRepository titledRepo = new TitledEntityRepository();

			templatesList_ = templRepo.listAll(dbversionsList_);
			developersList_ = titledRepo.listDevelopers();
			publishersList_ = titledRepo.listPublishers();
			genresList_ = titledRepo.listGenres();
			yearsList_ = titledRepo.listYears();
			statusList_ = titledRepo.listStatus();

			customList_ = new ArrayList<>();
			for (int i = 0; i < Profile.NR_OF_CUSTOM_STRING_DROPDOWNS; i++) {
				customList_.add(titledRepo.listCustomValues(i));
			}

			templateIndex_ = TemplateRepository.indexOfDefault(templatesList_);

			return true;
		} catch (Exception e) {
			GeneralPurposeDialogs.warningMessage(getParent(), e);
			return false;
		}
	}

	protected ToolBar createInfoTab() {
		Composite composite = createTabWithComposite(text_.get("dialog.profile.tab.info"), 6);

		title_ = createLabelAndTextMetaControl(composite, text_.get("dialog.profile.title"), 4, Profile::getTitle, Profile::setTitle);

		ToolBar toolBar = new ToolBar(composite, SWT.FLAT);

		engineSelector_ = new SearchEngineSelector(toolBar, false);

		developer_ = createLabelAndAutoSelectCombo(composite, text_.get("dialog.profile.developer"), developersList_, Profile::getDeveloper, Profile::setDeveloper);
		publisher_ = createLabelAndAutoSelectCombo(composite, text_.get("dialog.profile.publisher"), publishersList_, Profile::getPublisher, Profile::setPublisher);
		genre_ = createLabelAndAutoSelectCombo(composite, text_.get("dialog.profile.genre"), genresList_, Profile::getGenre, Profile::setGenre);
		year_ = createLabelAndAutoSelectCombo(composite, text_.get("dialog.profile.year"), yearsList_, Profile::getYear, Profile::setYear);

		link_ = new Text[Profile.NR_OF_LINK_DESTINATIONS];
		linkBrowseButton_ = new BrowseButton[Profile.NR_OF_LINK_TITLES];
		linkTitle_ = new Text[Profile.NR_OF_LINK_TITLES];

		for (int i = 0; i < Profile.NR_OF_LINK_TITLES / 2; i++) {
			link_[i] = createLabelAndTextMetaControl(composite, text_.get("dialog.profile.link", new Object[] {(i + 1)}), 1, i, Profile::getLinkDestination, Profile::setLinkDestination);
			linkBrowseButton_[i] = new BrowseButton(composite);
			linkTitle_[i] = createLabelAndTextMetaControl(composite, text_.get("dialog.profile.linktitle"), 2, i, Profile::getLinkTitle, Profile::setLinkTitle);
		}

		createLabelAndAutoSelectCombo(composite, text_.get("dialog.profile.status"), statusList_, Profile::getStatus, Profile::setStatus);
		createLabelAndCheckboxMetaControl(composite, text_.get("dialog.profile.favorite"), Profile::getFavorite, Profile::setFavorite);
		createLabel(composite);

		Label notesLabel = createLabel(composite, text_.get("dialog.profile.notes"));
		notes_ = createTextarea(composite, false, true, 5, 1, null);
		notes_.setFont(stringToFont(display_, settings_.getValues("gui", "notesfont"), notes_.getFont()));
		metaControls_.add(new MetaControl(notesLabel, notes_).connectProfile(Profile::getNotes, Profile::setNotes));

		Composite customComposite = createTabWithComposite(text_.get("dialog.profile.tab.custominfo"), 5);

		for (int i = 0; i < Profile.NR_OF_CUSTOM_STRING_DROPDOWNS; i++) {
			createLabelAndAutoSelectCombo(customComposite, settings_.getValue("gui", "custom" + (i + 1)), customList_.get(i), i, Profile::getCustomString, Profile::setCustomString);
		}
		for (int i = 0; i < Profile.NR_OF_CUSTOM_STRING_FIELDS_1; i++) {
			createLabelAndTextMetaControl(customComposite, settings_.getValue("gui", "custom" + (i + 1 + Profile.NR_OF_CUSTOM_STRING_DROPDOWNS)), 4, i + Profile.NR_OF_CUSTOM_STRING_DROPDOWNS,
				Profile::getCustomString, Profile::setCustomString);
		}
		Label custom9Label = createLabel(customComposite, settings_.getValue("gui", "custom9"));
		custom9_ = createScale(customComposite, false, new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));
		metaControls_.add(new MetaControl(custom9Label, custom9_).connectProfile(0, Profile::getCustomInt, Profile::setCustomInt));
		createLabelAndSpinnerMetaControl(customComposite, settings_.getValue("gui", "custom10"), Integer.MIN_VALUE, Integer.MAX_VALUE, 1, Profile::getCustomInt, Profile::setCustomInt);
		for (int i = Profile.NR_OF_LINK_TITLES / 2; i < Profile.NR_OF_LINK_TITLES; i++) {
			link_[i] = createLabelAndTextMetaControl(customComposite, text_.get("dialog.profile.link", new Object[] {(i + 1)}), 1, i, Profile::getLinkDestination, Profile::setLinkDestination);
			linkBrowseButton_[i] = new BrowseButton(customComposite);
			linkTitle_[i] = createLabelAndTextMetaControl(customComposite, text_.get("dialog.profile.linktitle"), 1, i, Profile::getLinkTitle, Profile::setLinkTitle);
		}
		for (int i = 0; i < Profile.NR_OF_CUSTOM_STRING_FIELDS_2; i++) {
			int columns = i % 2 == 0 ? 2: 1;
			createLabelAndTextMetaControl(customComposite,
				settings_.getValue("gui", "custom" + (i + 1 + Profile.NR_OF_CUSTOM_STRING_DROPDOWNS + Profile.NR_OF_CUSTOM_INTS + Profile.NR_OF_CUSTOM_STRING_FIELDS_1)), columns,
				i + Profile.NR_OF_CUSTOM_STRING_DROPDOWNS + Profile.NR_OF_CUSTOM_STRING_FIELDS_1, Profile::getCustomString, Profile::setCustomString);
		}

		return toolBar;
	}

	@Override
	protected Group createGeneralTab(String capturesText, String configFileText) {
		Group associationGroup = super.createGeneralTab(capturesText, configFileText);

		templateCombo_ = createLabelAndWideCombo(associationGroup, text_.get("dialog.profile.template"));
		templateCombo_.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 3, 1));
		templatesList_.forEach(x -> templateCombo_.add(x.getTitle()));
		templateCombo_.select(templateIndex_);
		Button templateReload = createButton(associationGroup, null, text_.get("dialog.profile.reloadsettings"), text_.get("dialog.profile.reloadsettings.tooltip"), new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				if (templateCombo_.getSelectionIndex() != -1) {
					if (setButton_.isEnabled()) {
						GeneralPurposeDialogs.initErrorDialog();
						GeneralPurposeDialogs.addError(text_.get("dialog.template.required.dosboxassociation"), setButton_, getTabItemByControl(setButton_));
						GeneralPurposeDialogs.displayErrorDialog(shell_);
						return;
					}
					doPerformDosboxConfAction(DosboxConfAction.RELOAD_TEMPLATE, dbversionsList_.get(dbversionCombo_.getSelectionIndex()));
				}
			}
		});
		templateReload.setEnabled(dbversionIndex_ != -1);

		return associationGroup;
	}

	@Override
	protected Group createMachineTab() {
		Group memoryGroup = super.createMachineTab();

		loadfix_ = createLabelAndCheckButtonAutoexecControl(memoryGroup, text_.get("dialog.profile.loadfix"), Autoexec::getLoadfix, Autoexec::setLoadfix);
		loadfix_.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				loadfixValue_.setEnabled(loadfix_.getSelection());
			}
		});
		loadfixValue_ = createEditableCombo(memoryGroup, settings_.getValues("profile", "loadfix_value"), 10);
		Label kbLabel = createLabel(memoryGroup, text_.get("dialog.profile.kb"));
		autoexecControls_.add(new MetaControl(kbLabel, loadfixValue_).connectAutoexec(Autoexec::getLoadfixValueAsString, Autoexec::setLoadfixValue));

		createLabelAndCheckButtonAutoexecControl(memoryGroup, text_.get("dialog.profile.loadhigh"), Autoexec::getLoadhigh, Autoexec::setLoadhigh);
		createLabel(memoryGroup, 2, 1);

		return memoryGroup;
	}

	@Override
	protected void createMountingTab(TemplateProfileBase configurable, boolean multiEdit) {
		super.createMountingTab(configurable, multiEdit);

		Composite booterComposite = (Composite)booterExpandItem_.getControl();
		booterComposite.setLayout(new GridLayout(4, false));

		img1_ = createLabelAndTextAutoexecControl(booterComposite, text_.get("dialog.profile.booterimage1"), Autoexec::getImg1, Autoexec::setImg1);
		new BrowseButton(booterComposite).connect(shell_, img1_, main_, BrowseType.FILE, CanonicalType.BOOTER, false, null);
		new GrabButton(booterComposite, img1_, mountingpointsList_, true);

		img2_ = createLabelAndTextAutoexecControl(booterComposite, text_.get("dialog.profile.booterimage2"), Autoexec::getImg2, Autoexec::setImg2);
		new BrowseButton(booterComposite).connect(shell_, img2_, img1_, BrowseType.FILE, CanonicalType.BOOTER, false, null);
		new GrabButton(booterComposite, img2_, mountingpointsList_, true);

		img3_ = createLabelAndTextAutoexecControl(booterComposite, text_.get("dialog.profile.booterimage3"), Autoexec::getImg3, Autoexec::setImg3);
		new BrowseButton(booterComposite).connect(shell_, img3_, img1_, BrowseType.FILE, CanonicalType.BOOTER, false, null);
		new GrabButton(booterComposite, img3_, mountingpointsList_, true);

		createLabelAndComboAutoexecControl(booterComposite, text_.get("dialog.profile.booterdriveletter"), new String[] {"", "A", "C", "D"}, 4, Autoexec::getImgDriveletter,
			Autoexec::setImgDriveletter);
		createLabel(booterComposite, 2, 1);

		booterExpandItem_.setHeight(booterComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT).y);

		Composite dosComposite = (Composite)dosExpandItem_.getControl();
		dosComposite.setLayout(new GridLayout(6, false));

		main_ = createLabelAndTextAutoexecControl(dosComposite, text_.get("dialog.profile.mainexe"), 3, Autoexec::getMain, Autoexec::setMain);
		new BrowseButton(dosComposite).connect(shell_, main_, null, BrowseType.FILE, CanonicalType.EXE, false, null);
		new GrabButton(dosComposite, main_, mountingpointsList_, false);

		createLabel(dosComposite);
		createLabelAndTextAutoexecControl(dosComposite, text_.get("dialog.profile.mainparameters"), 2, Autoexec::getParameters, Autoexec::setParameters);
		createLabel(dosComposite, 2, 1);

		setup_ = createLabelAndTextMetaControl(dosComposite, text_.get("dialog.profile.setupexe"), 3, Profile::getSetupString, Profile::setSetupFileLocation);
		new BrowseButton(dosComposite).connect(shell_, setup_, main_, BrowseType.FILE, CanonicalType.EXE, false, null);
		new GrabButton(dosComposite, setup_, mountingpointsList_, false);

		createLabel(dosComposite);
		createLabelAndTextMetaControl(dosComposite, text_.get("dialog.profile.setupparameters"), 2, Profile::getSetupParams, Profile::setSetupParams);
		Label alt1ParamsLabel = createLabel(dosComposite);
		Label alt2ParamsLabel = createLabel(dosComposite);

		Text alt1 = createLabelAndTextMetaControl(dosComposite, text_.get("dialog.profile.altexe", new Object[] {1}), 2, 0, Profile::getAltExeString, Profile::setAltExeFileLocation);
		Text alt1Params_ = createText(dosComposite);
		metaControls_.add(new MetaControl(alt1ParamsLabel, alt1Params_).connectProfile(0, Profile::getAltExeParam, Profile::setAltExeParam));
		new BrowseButton(dosComposite).connect(shell_, alt1, main_, BrowseType.FILE, CanonicalType.EXE, false, null);
		new GrabButton(dosComposite, alt1, mountingpointsList_, false);

		Text alt2 = createLabelAndTextMetaControl(dosComposite, text_.get("dialog.profile.altexe", new Object[] {2}), 2, 1, Profile::getAltExeString, Profile::setAltExeFileLocation);
		Text alt2Params_ = createText(dosComposite);
		metaControls_.add(new MetaControl(alt2ParamsLabel, alt2Params_).connectProfile(1, Profile::getAltExeParam, Profile::setAltExeParam));
		new BrowseButton(dosComposite).connect(shell_, alt2, main_, BrowseType.FILE, CanonicalType.EXE, false, null);
		new GrabButton(dosComposite, alt2, mountingpointsList_, false);

		dosExpandItem_.setHeight(dosComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT).y);

		for (int i = 0; i < Profile.NR_OF_LINK_DESTINATIONS; i++) {
			linkBrowseButton_[i].connect(shell_, link_[i], main_, BrowseType.FILE, CanonicalType.DOC, false, null);
		}

		if (multiEdit) {
			Stream.of(booterComposite.getChildren()).forEach(x -> x.setEnabled(false));
			Stream.of(dosComposite.getChildren()).forEach(x -> x.setEnabled(false));
		}
	}

	protected Text createLabelAndTextMetaControl(Composite composite, String labelText, Function<Profile, String> getMethod, BiConsumer<Profile, String> updateMethod) {
		Label label = createLabel(composite, labelText);
		Text text = createText(composite, new GridData(SWT.FILL, SWT.CENTER, true, false));
		metaControls_.add(new MetaControl(label, text).connectProfile(getMethod, updateMethod));
		return text;
	}

	protected Text createLabelAndTextMetaControl(Composite composite, String labelText, int horizontalSpan, Function<Profile, String> getMethod, BiConsumer<Profile, String> updateMethod) {
		Label label = createLabel(composite, labelText);
		Text text = createText(composite, new GridData(SWT.FILL, SWT.CENTER, true, false, horizontalSpan, 1));
		metaControls_.add(new MetaControl(label, text).connectProfile(getMethod, updateMethod));
		return text;
	}

	protected Text createLabelAndTextMetaControl(Composite composite, String labelText, int horizontalSpan, int index, BiFunction<Profile, Integer, String> getMethod,
			TriConsumer<Profile, Integer, String> updateMethod) {
		Label label = createLabel(composite, labelText);
		Text text = createText(composite, new GridData(SWT.FILL, SWT.CENTER, true, false, horizontalSpan, 1));
		metaControls_.add(new MetaControl(label, text).connectProfile(index, getMethod, updateMethod));
		return text;
	}

	protected AutoSelectCombo createLabelAndAutoSelectCombo(Composite composite, String labelText, List<ITitledEntity> values, Function<Profile, String> getMethod,
			BiConsumer<Profile, String> updateMethod) {
		Label developerLabel = createLabel(composite, labelText);
		AutoSelectCombo combo = new AutoSelectCombo(composite, new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1), values);
		metaControls_.add(new MetaControl(developerLabel, combo.getControl()).connectProfile(getMethod, updateMethod));
		return combo;
	}

	protected AutoSelectCombo createLabelAndAutoSelectCombo(Composite composite, String labelText, List<ITitledEntity> values, int index, BiFunction<Profile, Integer, String> getMethod,
			TriConsumer<Profile, Integer, String> updateMethod) {
		Label developerLabel = createLabel(composite, labelText);
		AutoSelectCombo combo = new AutoSelectCombo(composite, new GridData(SWT.FILL, SWT.CENTER, true, false, 4, 1), values);
		metaControls_.add(new MetaControl(developerLabel, combo.getControl()).connectProfile(index, getMethod, updateMethod));
		return combo;
	}

	protected Button createLabelAndCheckboxMetaControl(Composite composite, String labeltext, Function<Profile, String> getMethod, BiConsumer<Profile, String> updateMethod) {
		Label label = createLabel(composite, labeltext);
		Button checkButton = createCheckButton(composite, new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1), false);
		metaControls_.add(new MetaControl(label, checkButton).connectProfile(getMethod, updateMethod));
		return checkButton;
	}

	protected Spinner createLabelAndSpinnerMetaControl(Composite composite, String labelText, int min, int max, int index, BiFunction<Profile, Integer, String> getMethod,
			TriConsumer<Profile, Integer, String> updateMethod) {
		Label label = createLabel(composite, labelText);
		Spinner spinner = createSpinner(composite, min, max);
		metaControls_.add(new MetaControl(label, spinner).connectProfile(index, getMethod, updateMethod));
		return spinner;
	}
}
