package gui;

import java.io.IOException;
import java.io.InputStream;

import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.BooleanFieldEditor;
import org.eclipse.jface.preference.FieldEditor;
import org.eclipse.jface.preference.FieldEditorPreferencePage;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.IntegerFieldEditor;
import org.eclipse.jface.preference.PreferenceDialog;
import org.eclipse.jface.preference.PreferenceManager;
import org.eclipse.jface.preference.PreferenceNode;
import org.eclipse.jface.preference.PreferencePage;
import org.eclipse.jface.preference.PreferenceStore;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.FontMetrics;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;

import util.PmTransException;

public class Config extends PreferenceStore {

	private static Config instance = null;

	/**
	 * Non-configurable stuff
	 */
	// Config file path
	private static String CONFIG_PATH = "./config.properties";
	// State file path
	public static String STATE_PATH = "./state.transcriber";
	// Icon paths
	public static String ICON_PATH_PLAY = "/icon/start.png";
	public static String ICON_PATH_PAUSE = "/icon/pause.png";
	public static String ICON_PATH_RESTART = "/icon/restart.png";
	public static String ICON_PATH_OPEN_TRANSCRIPTION = "/icon/open.png";
	public static String ICON_PATH_OPEN_AUDIO = "/icon/openAudio.png";
	public static String ICON_PATH_SAVE_TRANSCRIPTION = "/icon/save.png";
	public static String ICON_PATH_LOOP = "/icon/loop.png";
	public static String ICON_PATH_ZOOM_IN = "/icon/zoom_in.png";
	public static String ICON_PATH_ZOOM_OUT = "/icon/zoom_out.png";
	public static String ICON_PATH_COPY = "/icon/copy.png";
	public static String ICON_PATH_CUT = "/icon/cut.png";
	public static String ICON_PATH_PASTE = "/icon/paste.png";
	public static String ICON_PATH_CROSS = "/icon/cross.png";
	public static String ICON_PATH_ADVANCED_SEARCH = "/icon/advancedSearch.png";
	public static String ICON_PATH_CHANGE_BACKGROUND_COLOR = "/icon/changeBackgroundColor.png";
	public static String ICON_PATH_CHANGE_FONT_COLOR = "/icon/changeFontColor.png";
	public static String ICON_PATH_SETTINGS = "/icon/settings.png";
	public static String ICON_PATH_CONTRIBUTE = "/icon/contribute.png";
	public static String DEFAULT_ACCELERATORS = "cxvfosa";

	// Main shell initial dimensions
	private int SHELL_HEIGHT_DEFAULT = 600;
	private int SHELL_LENGHT_DEFAULT = 600;
	public static String SHELL_HEIGHT = "window.height";
	public static String SHELL_LENGHT = "window.lenght";
	// Last directory paths for file dialogs
	private String LAST_OPEN_AUDIO_PATH_DEFAULT = "";
	public static String LAST_OPEN_AUDIO_PATH = "last.open.audio.path";
	private String LAST_OPEN_TEXT_PATH_DEFAULT = "";
	public static String LAST_OPEN_TEXT_PATH = "last.open.text.path";
	// Last directory path for the export dialog
	private String LAST_EXPORT_TRANSCRIPTION_PATH_DEFALUT = "";
	public static String LAST_EXPORT_TRANSCRIPTION_PATH = "last.export.transcription.path";

	// URLs
	public static String CONTRIBUTE_URL = "https://github.com/juanerasmoe/pmTrans/wiki/Contribute-to-pmTrans";
	
	/**
	 * Configurable stuff
	 */
	// Duration of the short rewind in seconds
	private int SHORT_REWIND_DEFAULT = 5;
	public static String SHORT_REWIND = "short.rewind.duration";
	// Duration of the long rewind in seconds
	private int LONG_REWIND_DEFAULT = 10;
	public static String LONG_REWIND = "long.rewind.duration";
	// Duration of the rewind-and-play
	private static int REWIND_AND_PLAY_DEFAULT = 2;
	public static String REWIND_AND_PLAY = "rewind.and.play.duration";
	// Max size of the previous-files list
	private static int AUDIO_FILE_CACHE_LENGHT_DEFAULT = 7;
	public static String AUDIO_FILE_CACHE_LENGHT = "audio.file.cache.lenght";
	private static int TEXT_FILE_CACHE_LENGHT_DEFAULT = 7;
	public static String TEXT_FILE_CACHE_LENGHT = "text.file.cache.lenght";
	private static int SLOW_DOWN_PLAYBACK_DEFAULT = -5;
	public static String SLOW_DOWN_PLAYBACK = "slow.down.playback";
	private static int SPEED_UP_PLAYBACK_DEFAULT = 5;
	public static String SPEED_UP_PLAYBACK = "speed.up.plaback";
	// Auto save
	private static boolean AUTO_SAVE_DEFAULT = true;
	public static String AUTO_SAVE = "auto.save";
	private static int AUTO_SAVE_TIME_DEFAULT = 2;
	public static String AUTO_SAVE_TIME = "auto.save.time";
	// Mini-mode dialog
	private static boolean SHOW_MINI_MODE_DIALOG_DEFAULT = true;
	public static String SHOW_MINI_MODE_DIALOG = "show.mini.mode.dialog";
	// Font and size
	private static String FONT_DEFAULT = "Courier New";
	public static String FONT = "font";
	private static int FONT_SIZE_DEFAULT = 10;
	public static String FONT_SIZE = "font.size";
	private static Color FONT_COLOR_DEFAULT = Display.getCurrent()
			.getSystemColor(SWT.COLOR_BLACK);
	public static String FONT_COLOR = "font.color";
	private static Color BACKGROUND_COLOR_DEFAULT = Display.getCurrent()
			.getSystemColor(SWT.COLOR_WHITE);
	public static String BACKGROUND_COLOR = "background.color";

	// CONFIGURABLE ACCELERATORS
	private String accelerators;
	// Pause
	private static String PAUSE_KEY_DEFAULT = " ";
	public static String PAUSE_KEY = "pause.key";
	// Short rewind
	private static String SHORT_REWIND_KEY_DEFAULT = "7";
	public static String SHORT_REWIND_KEY = "short.rewind.key";
	// Long rewind
	private static String LONG_REWIND_KEY_DEFAULT = "8";
	public static String LONG_REWIND_KEY = "long.rewind.key";
	// Speed up
	private static String SPEED_UP_KEY_DEFAULT = "4";
	public static String SPEED_UP_KEY = "speed.up.key";
	// Slow down
	private static String SLOW_DOWN_KEY_DEFAULT = "3";
	public static String SLOW_DOWN_KEY = "slow.down.key";
	// Audio loops
	private static String AUDIO_LOOPS_KEY_DEFAULT = "9";
	public static String AUDIO_LOOPS_KEY = "audio.loops.key";
	public static String LOOP_FRECUENCY = "loop.frecuency";
	private static int LOOP_FRECUENCY_DEFAULT = 5;
	public static String LOOP_LENGHT = "loop.lenght";
	private static int LOOP_LENGHT_DEFAULT = 2;
	// Timestamps
	private static String TIMESTAMP_KEY_DEFAULT = "t";
	public static String TIMESTAMP_KEY = "timestamp.key";

	private Config() {

		super(CONFIG_PATH);

		// Set up the defaults
		setDefault(SHORT_REWIND, SHORT_REWIND_DEFAULT);
		setDefault(LONG_REWIND, LONG_REWIND_DEFAULT);
		setDefault(REWIND_AND_PLAY, REWIND_AND_PLAY_DEFAULT);
		setDefault(SHELL_HEIGHT, SHELL_HEIGHT_DEFAULT);
		setDefault(SHELL_LENGHT, SHELL_LENGHT_DEFAULT);
		setDefault(TEXT_FILE_CACHE_LENGHT, TEXT_FILE_CACHE_LENGHT_DEFAULT);
		setDefault(AUDIO_FILE_CACHE_LENGHT, AUDIO_FILE_CACHE_LENGHT_DEFAULT);
		setDefault(SLOW_DOWN_PLAYBACK, SLOW_DOWN_PLAYBACK_DEFAULT);
		setDefault(SPEED_UP_PLAYBACK, SPEED_UP_PLAYBACK_DEFAULT);
		setDefault(AUTO_SAVE, AUTO_SAVE_DEFAULT);
		setDefault(AUTO_SAVE_TIME, AUTO_SAVE_TIME_DEFAULT);
		setDefault(SHOW_MINI_MODE_DIALOG, SHOW_MINI_MODE_DIALOG_DEFAULT);
		setDefault(FONT, FONT_DEFAULT);
		setDefault(FONT_SIZE, FONT_SIZE_DEFAULT);
		setDefault(FONT_COLOR, FONT_COLOR_DEFAULT);
		setDefault(BACKGROUND_COLOR, BACKGROUND_COLOR_DEFAULT);

		// Pause
		setDefault(PAUSE_KEY, PAUSE_KEY_DEFAULT);
		// Short rewind
		setDefault(SHORT_REWIND_KEY, SHORT_REWIND_KEY_DEFAULT);
		// Long rewind
		setDefault(LONG_REWIND_KEY, LONG_REWIND_KEY_DEFAULT);
		// Playback speed
		setDefault(SPEED_UP_KEY, SPEED_UP_KEY_DEFAULT);
		setDefault(SLOW_DOWN_KEY, SLOW_DOWN_KEY_DEFAULT);
		// Audio loops
		setDefault(AUDIO_LOOPS_KEY, AUDIO_LOOPS_KEY_DEFAULT);
		setDefault(LOOP_FRECUENCY, LOOP_FRECUENCY_DEFAULT);
		setDefault(LOOP_LENGHT, LOOP_LENGHT_DEFAULT);
		// Timestamp
		setDefault(TIMESTAMP_KEY, TIMESTAMP_KEY_DEFAULT);
		// Cache
		setDefault(LAST_OPEN_AUDIO_PATH, LAST_OPEN_AUDIO_PATH_DEFAULT);
		setDefault(LAST_OPEN_TEXT_PATH, LAST_OPEN_TEXT_PATH_DEFAULT);
		setDefault(LAST_EXPORT_TRANSCRIPTION_PATH,
				LAST_EXPORT_TRANSCRIPTION_PATH_DEFALUT);

		try {
			load();
		} catch (Exception e) {
			// The properties will start as default values
		}

		updateAccelerators();

		// Add the listeners
		addPropertyChangeListener(new IPropertyChangeListener() {
			@Override
			public void propertyChange(PropertyChangeEvent event) {
				try {
					updateAccelerators();
					save();
				} catch (IOException e) {
					// ignore
				}
			}
		});
	}

	public void showConfigurationDialog(Shell parent) throws PmTransException {
		// Create the preference manager
		PreferenceManager mgr = new PreferenceManager();

		// Create the nodes
		PreferenceNode playbackNode = new PreferenceNode("playbackPreferences");
		PreferencePage playbackPage = new FieldEditorPreferencePage() {
			@Override
			protected void createFieldEditors() {
				addField(new IntegerFieldEditor(SHORT_REWIND,
						"Short rewind duration (in sec)",
						getFieldEditorParent()));
				addField(new IntegerFieldEditor(LONG_REWIND,
						"Long rewind duration (in sec)", getFieldEditorParent()));
				addField(new IntegerFieldEditor(REWIND_AND_PLAY,
						"Rewind-and-resume duartion duration (in sec)",
						getFieldEditorParent()));
				addField(new IntegerFieldEditor(LOOP_FRECUENCY,
						"Loops frecuency (in seconds)", getFieldEditorParent()));
				addField(new IntegerFieldEditor(LOOP_LENGHT,
						"Loop rewind lenght (in seconds)",
						getFieldEditorParent()));
			}
		};
		playbackPage.setTitle("Playback preferences");
		playbackNode.setPage(playbackPage);

		PreferenceNode shortcutsNode = new PreferenceNode(
				"shortcutsPreferences");
		PreferencePage shortcutsPage = new FieldEditorPreferencePage() {
			@Override
			protected void createFieldEditors() {
				addField(new ShortcutFieldEditor(SHORT_REWIND_KEY,
						"Short rewind", getFieldEditorParent()));
				addField(new ShortcutFieldEditor(LONG_REWIND_KEY,
						"Long rewind", getFieldEditorParent()));
				addField(new ShortcutFieldEditor(PAUSE_KEY, "Pause and resume",
						getFieldEditorParent()));
				addField(new ShortcutFieldEditor(AUDIO_LOOPS_KEY,
						"Enable audio loops", getFieldEditorParent()));
				addField(new ShortcutFieldEditor(SLOW_DOWN_KEY,
						"Slow down audio playback", getFieldEditorParent()));
				addField(new ShortcutFieldEditor(SPEED_UP_KEY,
						"Speed up audio playback", getFieldEditorParent()));
				addField(new ShortcutFieldEditor(TIMESTAMP_KEY,
						"Insert timestamp", getFieldEditorParent()));
			}
		};
		shortcutsPage.setTitle("Shortcuts preferences");
		shortcutsNode.setPage(shortcutsPage);

		PreferenceNode generalNode = new PreferenceNode("generalPreferences");
		PreferencePage generalPage = new FieldEditorPreferencePage() {
			@Override
			protected void createFieldEditors() {
				addField(new IntegerFieldEditor(AUDIO_FILE_CACHE_LENGHT,
						"Max size of the \"recent audio files\" list",
						getFieldEditorParent()));
				addField(new IntegerFieldEditor(TEXT_FILE_CACHE_LENGHT,
						"Max size of the \"recent text files\" list",
						getFieldEditorParent()));
				// TODO add a separator here
				addField(new BooleanFieldEditor(AUTO_SAVE, "Auto save",
						getFieldEditorParent()));
				addField(new IntegerFieldEditor(AUTO_SAVE_TIME,
						"Auto save frecuency (in minutes)",
						getFieldEditorParent()));
			}
		};
		generalPage.setTitle("General preferences");
		generalNode.setPage(generalPage);

		mgr.addToRoot(playbackNode);
		mgr.addToRoot(shortcutsNode);
		mgr.addToRoot(generalNode);
		PreferenceDialog dlg = new PreferenceDialog(parent, mgr);
		dlg.setPreferenceStore(this);

		if (dlg.open() == PreferenceDialog.OK)
			try {
				save();
			} catch (IOException e) {
				throw new PmTransException("Unable to save preferences", e);
			}
	}

	private void updateAccelerators() {
		accelerators = "" + DEFAULT_ACCELERATORS;
		accelerators += getAcceleratorChar(AUDIO_LOOPS_KEY);
		accelerators += getAcceleratorChar(LONG_REWIND_KEY);
		accelerators += getAcceleratorChar(SHORT_REWIND_KEY);
		accelerators += getAcceleratorChar(PAUSE_KEY);
		accelerators += getAcceleratorChar(SPEED_UP_KEY);
		accelerators += getAcceleratorChar(SLOW_DOWN_KEY);
		accelerators += getAcceleratorChar(TIMESTAMP_KEY);
	}

	private char getAcceleratorChar(String action) {
		return (getString(action).equals("[space]") ? ' ' : getString(action)
				.charAt(0));
	}

	public static Config getInstance() {
		if (instance == null)
			instance = new Config();
		return instance;
	}

	public class ShortcutFieldEditor extends FieldEditor {

		String keyConst;

		Composite top;
		Label command;
		Label ctrl;
		Text character;

		public ShortcutFieldEditor(String key, String labelText,
				Composite parent) {
			super(key, labelText, parent);
			keyConst = key;
		}

		@Override
		protected void adjustForNumColumns(int numColumns) {
			((GridData) top.getLayoutData()).horizontalSpan = numColumns;
		}

		@Override
		protected void doFillIntoGrid(Composite parent, int numColumns) {
			top = parent;
			GridData gd = new GridData(GridData.FILL_HORIZONTAL);
			gd.horizontalSpan = numColumns;
			top.setLayoutData(gd);

			command = new Label(top, SWT.NORMAL);
			command.setText(getLabelText() + ":");
			GridData g = new GridData();
			g.grabExcessHorizontalSpace = true;
			g.horizontalAlignment = SWT.FILL;
			command.setLayoutData(g);
			ctrl = new Label(top, SWT.NORMAL);
			ctrl.setText("ctrl + ");
			character = new Text(top, SWT.NORMAL);
			g = new GridData();
			GC gc = new GC(character);
			FontMetrics fm = gc.getFontMetrics();
			g.widthHint = 10 * fm.getAverageCharWidth();
			gc.dispose();
			character.setLayoutData(g);
		}

		@Override
		protected void doLoad() {
			IPreferenceStore ps = getPreferenceStore();
			character.setText(ps.getString(keyConst).equals(" ") ? "[space]"
					: ps.getString(keyConst));
		}

		@Override
		protected void doLoadDefault() {
			IPreferenceStore ps = getPreferenceStore();
			character
					.setText(ps.getDefaultString(keyConst).equals(" ") ? "[space]"
							: ps.getDefaultString(keyConst));
		}

		@Override
		protected void doStore() {
			if (!character.getText().equals("[space]")
					&& character.getText().length() > 1) {
				MessageDialog
						.openError(
								Display.getCurrent().getActiveShell(),
								"Error",
								"The key "
										+ character.getText()
										+ " is not supported as a shortcut. Please use another one.");
				doLoadDefault();
				return;
			}
			String c = character.getText().equals("[space]") ? " " : character
					.getText();

			IPreferenceStore ps = getPreferenceStore();
			updateAccelerators();
			accelerators = accelerators.replace(ps.getString(keyConst), "");
			if (accelerators.contains(c)) {
				MessageDialog
						.openError(
								Display.getCurrent().getActiveShell(),
								"Error",
								"The key "
										+ c
										+ " is already used as a shortcut. Please use another one.");
				doLoad();
				updateAccelerators();
				return;
			}
			ps.setValue(keyConst, c);
		}

		@Override
		public int getNumberOfControls() {
			return 3;
		}
	}

	public Image getImage(String resourceName) {
		InputStream in = getClass().getResourceAsStream(resourceName);
		return new Image(Display.getCurrent(), in);
	}

	public Color getColor(String prop) {
		String color = getString(prop);
		if (color.isEmpty())
			return Display.getCurrent().getSystemColor(SWT.COLOR_WHITE);
		String rgb[] = color.split(";");
		return new Color(Display.getCurrent(), new RGB(
				Integer.parseInt(rgb[0]), Integer.parseInt(rgb[1]),
				Integer.parseInt(rgb[2])));
	}

	public void setDefault(String prop, Color color) {
		setDefault(prop, "" + color.getRed() + ";" + color.getGreen() + ";"
				+ color.getBlue());
	}

	public void setValue(String prop, Color color) {
		setValue(prop, "" + color.getRed() + ";" + color.getGreen() + ";"
				+ color.getBlue());
	}
}