/*
 *  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.controls;

import java.io.File;
import org.apache.commons.lang3.StringUtils;
import org.dbgl.gui.abstractdialog.BaseDialog;
import org.dbgl.gui.dialog.BrowseArchiveDialog;
import org.dbgl.model.FileLocation;
import org.dbgl.model.ICanonicalize;
import org.dbgl.service.FileLocationService;
import org.dbgl.service.ITextService;
import org.dbgl.service.ImageService;
import org.dbgl.service.TextService;
import org.dbgl.util.FilesUtils;
import org.dbgl.util.StringRelatedUtils;
import org.dbgl.util.SystemUtils;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.DirectoryDialog;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;


public class BrowseButton {

	private class BrowseSelectionAdapter extends SelectionAdapter {

		private final Shell shell_;
		private final Text control_, altControl_;
		private final BrowseType browseType_;
		private final CanonicalType canonicalType_;
		private final boolean save_;
		private final Combo combo_;
		private final ICanonicalize canonicalizer_;

		public BrowseSelectionAdapter(Shell shell, Text control, Text altControl, BrowseType browse, CanonicalType canon, boolean save, Combo combo) {
			shell_ = shell;
			control_ = control;
			altControl_ = altControl;
			browseType_ = browse;
			canonicalType_ = canon;
			save_ = save;
			combo_ = combo;

			switch (getCanonicalType()) {
				case DOC:
				case DBGLZIP:
				case NATIVE_EXE:
					canonicalizer_ = FileLocationService.getInstance().dataRelative();
					break;
				case EXE:
				case INSTALLER:
				case ZIP:
				case BOOTER:
				case DOSROOT:
				case CDIMAGE:
					canonicalizer_ = FileLocationService.getInstance().dosrootRelative();
					break;
				case DOSBOX:
				case DOSBOXEXE:
				case DOSBOXCONF:
					canonicalizer_ = FileLocationService.getInstance().dosboxRelative();
					break;
				case DFEND:
				case NONE:
				default:
					canonicalizer_ = FileLocationService.standard();
					break;
			}
		}

		private CanonicalType getCanonicalType() {
			return (browseType_ == BrowseType.DIR && canonicalType_ == CanonicalType.NONE) ? CanonicalType.DOSROOT: canonicalType_;
		}

		private String filterPath() {
			final String fieldValue = StringUtils.isBlank(control_.getText()) && (altControl_ != null) ? altControl_.getText(): control_.getText();
			final String path;

			switch (getCanonicalType()) {
				case EXE:
					if (StringUtils.isNotBlank(fieldValue)) {
						File main = FilesUtils.determineMainFile(new File(fieldValue));
						if (main != null && main.getParentFile() != null && !main.getPath().equals(fieldValue) && !(main.getPath() + File.separator).equals(fieldValue)) {
							path = main.getParent();
							break;
						}
					}
				case DOC:
				case DBGLZIP:
				case NATIVE_EXE:
				case INSTALLER:
				case ZIP:
				case BOOTER:
				case DOSROOT:
				case DOSBOX:
				case DOSBOXEXE:
				case DOSBOXCONF:
					path = fieldValue;
					break;
				case CDIMAGE:
					String[] fPaths = StringRelatedUtils.textAreaToStringArray(fieldValue, control_.getLineDelimiter());
					path = (fPaths.length > 0) ? fPaths[0]: StringUtils.EMPTY;
					break;
				case DFEND:
					path = FileLocationService.DFEND_PATH_STRING;
					break;
				case NONE:
				default:
					path = StringUtils.EMPTY;
			}

			return new FileLocation(path, canonicalizer_).getCanonicalFile().getPath();
		}

		private String getResult(String result, File filterPath, String[] filenames) {
			if (canonicalType_ == CanonicalType.DOSBOX && SystemUtils.IS_OSX) {
				File f = new FileLocation(result, canonicalizer_).getFile();
				if (f.getName().endsWith(FileLocationService.DB_APP_EXT)) {
					File exe = new File(f, FileLocationService.DB_APP_EXE);
					if (FilesUtils.isExistingFile(exe)) {
						return exe.getParent();
					}
				} else if (f.getName().equals(FileLocationService.DOSBOX_EXE_STRING)) {
					return f.getParent();
				}
			} else if (canonicalType_ == CanonicalType.EXE || canonicalType_ == CanonicalType.INSTALLER) {
				if (FilesUtils.isArchive(result) || FilesUtils.isCdImageFile(result) || FilesUtils.isFatImage(result)) {
					BrowseArchiveDialog dialog = new BrowseArchiveDialog(shell_, result);
					String choice = dialog.open();
					return (choice == null) ? null: new FileLocation(choice, canonicalizer_).getFile().getPath();
				}
			} else if (canonicalType_ == CanonicalType.CDIMAGE) {
				File path = new FileLocation(filterPath.getPath(), canonicalizer_).getFile();
				StringBuffer images = new StringBuffer();
				for (String file: filenames) {
					images.append(FilesUtils.concat(path, file)).append(control_.getLineDelimiter());
				}
				return images.toString();
			}

			return new FileLocation(result, canonicalizer_).getFile().getPath();
		}

		public void widgetSelected(SelectionEvent event) {
			String result = null;
			File filterPath = null;
			String[] filenames = null;

			shell_.setEnabled(false);

			String rawFilterPath = filterPath();
			File fpath = new File(rawFilterPath);
			if (fpath != null && !fpath.isDirectory())
				fpath = fpath.getParentFile();

			if (browseType_ == BrowseType.DIR) {
				DirectoryDialog dialog = new DirectoryDialog(shell_);
				if (fpath != null)
					dialog.setFilterPath(fpath.getPath());
				result = dialog.open();
			} else if (browseType_ == BrowseType.FILE && ((canonicalType_ == CanonicalType.EXE) || (canonicalType_ == CanonicalType.INSTALLER))
					&& (FilesUtils.isArchive(rawFilterPath) || FilesUtils.isCdImageFile(rawFilterPath) || FilesUtils.isFatImage(rawFilterPath))) {
				result = rawFilterPath;
			} else if (browseType_ == BrowseType.FILE) {
				int style = save_ ? SWT.SAVE: (canonicalType_ == CanonicalType.CDIMAGE) ? SWT.OPEN | SWT.MULTI: SWT.OPEN;
				FileDialog dialog = new FileDialog(shell_, style);
				if (fpath != null)
					dialog.setFilterPath(fpath.getPath());

				ITextService text = TextService.getInstance();

				String[] filterNames = null;
				String[] filterExts = null;
				switch (canonicalType_) {
					case DOC:
						filterNames = new String[] {FilesUtils.ALL_FILTER};
						filterExts = new String[] {FilesUtils.ALL_FILTER};
						break;
					case EXE:
					case DOSBOXEXE:
					case INSTALLER:
						filterNames = new String[] {text.get("filetype.applicable"), text.get("filetype.exe"), text.get("filetype.cdimage"), text.get("filetype.floppyimage"),
								text.get("filetype.archive"), FilesUtils.ALL_FILTER};
						filterExts = new String[] {FilesUtils.EXE_FILTER + ";" + FilesUtils.CDI_FILTER + ';' + FilesUtils.FATI_FILTER, FilesUtils.EXE_FILTER, FilesUtils.CDI_FILTER,
								FilesUtils.FATI_FILTER, FilesUtils.ARC_FILTER, FilesUtils.ALL_FILTER};
						break;
					case DOSBOX: // only applicable on OSX
					case NATIVE_EXE:
						filterNames = new String[] {text.get("filetype.native_exe"), FilesUtils.ALL_FILTER};
						filterExts = new String[] {FilesUtils.NATIVE_EXE_FILTER, FilesUtils.ALL_FILTER};
						break;
					case ZIP:
						filterNames = new String[] {text.get("filetype.archive"), FilesUtils.ALL_FILTER};
						filterExts = new String[] {FilesUtils.ARC_FILTER, FilesUtils.ALL_FILTER};
						break;
					case DBGLZIP:
						filterNames = new String[] {text.get("filetype.gamepack"), FilesUtils.ALL_FILTER};
						filterExts = new String[] {FilesUtils.DBGLZIP_FILTER, FilesUtils.ALL_FILTER};
						break;
					case BOOTER:
						filterNames = new String[] {text.get("filetype.booterimage"), FilesUtils.ALL_FILTER};
						filterExts = new String[] {FilesUtils.BTR_FILTER, FilesUtils.ALL_FILTER};
						break;
					case DFEND:
						filterNames = new String[] {text.get("filetype.dfendprofile")};
						filterExts = new String[] {FileLocationService.DFEND_PROFILES_STRING};
						break;
					case CDIMAGE:
						filterNames = new String[] {text.get("filetype.applicable"), text.get("filetype.cdimage"), text.get("filetype.floppyimage"), FilesUtils.ALL_FILTER};
						filterExts = new String[] {FilesUtils.CDI_FILTER + ';' + FilesUtils.FATI_FILTER, FilesUtils.CDI_FILTER, FilesUtils.FATI_FILTER, FilesUtils.ALL_FILTER};
						break;
					case DOSBOXCONF:
						filterNames = new String[] {text.get("filetype.conf"), FilesUtils.ALL_FILTER};
						filterExts = new String[] {FilesUtils.CNF_FILTER, FilesUtils.ALL_FILTER};
						break;
					default:
				}

				if (filterNames != null)
					dialog.setFilterNames(filterNames);

				if (filterExts != null)
					dialog.setFilterExtensions(filterExts);

				if (canonicalType_ == CanonicalType.DFEND)
					dialog.setFileName(FileLocationService.DFEND_PROFILES_STRING);

				result = dialog.open();

				if (canonicalType_ == CanonicalType.CDIMAGE) {
					filterPath = new File(dialog.getFilterPath());
					filenames = dialog.getFileNames();
				}
			}

			if (result != null) {
				result = getResult(result, filterPath, filenames);
				if (result != null) {
					control_.setText(result);
					if ((canonicalType_ == CanonicalType.DOSBOX) && (altControl_ != null)) {
						File confFile = new File(result, FileLocationService.DOSBOX_CONF_STRING);
						String confText = new FileLocation(confFile.getPath(), canonicalizer_).getFile().getPath();
						altControl_.setText(confText);
					} else if ((canonicalType_ == CanonicalType.CDIMAGE) && (combo_ != null)) {
						if (FilesUtils.isCdImageFile(filenames[0])) {
							combo_.setText("iso");
						} else if (FilesUtils.isFatImage(filenames[0])) {
							combo_.setText("floppy");
						}
					} else if ((canonicalType_ == CanonicalType.NATIVE_EXE) && (altControl_ != null)) {
						String dir = new FileLocation(result, canonicalizer_).getFile().getParent();
						if (dir != null)
							altControl_.setText(dir);
					}
				}
			}

			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
			}
			while (shell_.getDisplay().readAndDispatch());

			shell_.setEnabled(true);
		}
	}

	public enum BrowseType {
		DIR, FILE
	}
	public enum CanonicalType {
		DOSROOT, DFEND, CDIMAGE, ZIP, DBGLZIP, DOSBOX, DOSBOXEXE, DOSBOXCONF, DOC, BOOTER, EXE, INSTALLER, NATIVE_EXE, NONE
	}

	private final Button button_;

	public BrowseButton(Composite composite) {
		button_ = BaseDialog.createImageButton(composite, TextService.getInstance().get("button.browse"), ImageService.IMG_FOLDER);
	}

	public ICanonicalize connect(Shell shell, Text control, Text altControl, BrowseType browse, CanonicalType canon, boolean save, Combo combo) {
		BrowseSelectionAdapter browseSelectionAdapter = new BrowseSelectionAdapter(shell, control, altControl, browse, canon, save, combo);
		button_.addSelectionListener(browseSelectionAdapter);
		return browseSelectionAdapter.canonicalizer_;
	}
}
