var cenWgUtil = (function () {
	// public module members
	var my = {};

	my.currentWindowConfig = "";

	my.createWg = function () {
		return new cenWgConfig({
			title: "Untitled",
			identifier: null,
			visible: true,
			windows: []
		});
	};

	my.generateDefaultConfig = function () {
		var config = my.getStartupConfig();

		if (!config) {
			var windowGroup = my.createWg();

			var filename = "";

			if (cenAXUI && cenAXUI.currentEmulatedTPX)
				filename = cenAXUI.currentEmulatedTPX.filename;

			windowGroup.addWindow(
				cenAX.getWindowPosX(),
				cenAX.getWindowPosY(),
				cenAX.getWindowWidth(),
				cenAX.getWindowHeight(),
				filename
			);

			windowGroup.setTitle("Default Window Group");
			windowGroup.saveConfig();
			windowGroup.saveWindowConfigs();
			windowGroup.setStartupConfig();
		}
	};

	my.init = function () {
		my.unsetSaveBeforeClose();

		my.generateDefaultConfig();

		var params = cenAX.getParams();

		var wg = my.loadStartupConfig();

		if (!params.currentWindowConfig) {
			if (params.addWindow) {
				var w = wg.addWindow(
					cenAX.getWindowPosX(),
					cenAX.getWindowPosY(),
					cenAX.getWindowWidth(),
					cenAX.getWindowHeight(),
					params.loadConfigFilename
				);

				my.currentWindowConfig = w.getIdentifier();

				wg.saveWindowConfig(w);
				wg.saveConfig();
			}
			else {
				var wg = my.loadStartupConfig();

				wg.load();
			}
		}
		else {
			my.currentWindowConfig = params.currentWindowConfig;

			var wg = cenWgUtil.createWg().loadConfig();

			var w = wg.getWindowByIdentifier(my.currentWindowConfig);

			if (w != null) {
				window.resizeTo(w.getWinWidth(), w.getWinHeight());
				window.moveTo(w.getWinPosX(), w.getWinPosY());
			}
		}

		// Setup timer that checks on an interval whether or not to shut down.
		setInterval(function () {
			var close = localStorage.getItem(wgSaveBeforeCloseKey);

			if (close) {
				window.close();
			}
		}, 500);
	};

	my.setupWindowEvents = function () {
		window.onbeforeunload = function () {
			var saveSetting = localStorage.getItem(wgSaveBeforeCloseKey);

			var wg = my.loadStartupConfig();

			if (wg != null) {
				var w = wg.getWindowByIdentifier(my.currentWindowConfig);

				if (w != null) {
					if (saveSetting) {
						w.setWinPosX(cenAX.getWindowPosX());
						w.setWinPosY(cenAX.getWindowPosY());
						w.setWinWidth(cenAX.getWindowWidth());
						w.setWinHeight(cenAX.getWindowHeight());
						w.setTpxConfig(cenAXUI.currentEmulatedTPX.filename);

						wg.saveWindowConfig(w);
					}
					else {
						if (wg.getNumWindows() > 1) {
							wg.delWindowByIdentifier(my.currentWindowConfig);

							wg.saveConfig();	
						}
					}
				}
				else {
					alert('Error updating window');
				}
			}
		};
	};

	my.debug = function () {
		var wg = cenWgUtil.createWg().loadConfig();

		var windows = wg.getWindows();

		var str = "";

		for (var i = 0; i < windows.length; i++) {
			var w = windows[i];

			str += "\n" +
				'Identifier: ' + w.getIdentifier() + '\n' +
				'Width: ' + w.getWinWidth() + '\n' +
				'Height: ' + w.getWinHeight() + '\n' +
				'PosX: ' + w.getWinPosX() + '\n' +
				'PosY: ' + w.getWinPosY() + '\n' +
				'Tpx: ' + w.getTpxConfig() + '\n';
		}

		log.debug(str);
	},

	my.saveAndQuit = function () {
		my.setSaveBeforeClose();
	};

	my.setSaveBeforeClose = function () {
		localStorage.setItem(wgSaveBeforeCloseKey, true);
	};

	my.unsetSaveBeforeClose = function () {
		localStorage.removeItem(wgSaveBeforeCloseKey);
	};

	my.getWgList = function () {
		var wgList = [];

		for (var key in localStorage) {
			if (my.isWgFile(key)) {
				var wg = my.createWg();

				wg.loadConfig(key);

				wgList.push(wg);
			}
		}

		return wgList;
	};

	my.loadStartupConfig = function () {
		var config = my.getStartupConfig();

		if (config) {
			var wg = my.createWg();

			wg.loadConfig(config);

			return wg;
		}

		return null;
	};

	my.getStartupConfig = function () {
		return localStorage.getItem(wgStartupConfig);
	};

	my.initializeWindows = function (configName) {
		if (configName) {
			if (typeof configName !== "string")
				throw "[cenWgUtil.initializeWindows]: Argument (configName) must be of type string.";

			var wg = my.createWg();

			wg.loadConfig(configName);
			wg.load();
		}
		else {
			var startupConfig = my.getStartupConfig();

			my.initializeWindows(startupConfig);
		}
	};

	my.isWgFile = function (fileName) {
		if (!fileName)
			throw "[cenWgUtil.isWgConfigFile]: Argument (fileName) is required.";

		if (typeof fileName !== "string")
			throw "[cenWgUtil.isWgConfigFile]: Argument (fileName) must be of type string.";

		return fileName.startsWith(wgConfigPrefix) && fileName.match(/\.[0-9]+$/);
	};

	my.deleteWgFile = function (fileName) {
		if (!fileName)
			throw "[cenWgUtil.deleteWgFile]: Argument (fileName) is required.";

		if (typeof fileName !== "string")
			throw "[cenWgUtil.deleteWgFile]: Argument (fileName) must be of type string.";

		if (!my.isWgFile(fileName))
			throw "[cenWgUtil.deleteWgFile]: Argument (fileName) is not a wg file.";

		var item = localStorage.getItem(fileName);

		if (item) {
			/* If the file that is requested to be deleted is the startup config,
			 * if possible, set the startup config to be the first available one.
			 * Otherwise delete the startup config setting, if this happens,
			 * the default window group will be regenerated. */
			var wg = my.createWg().loadConfig(fileName);

			if (wg.isStartupConfig()) {
				var wgList = my.getWgList();

				if (wgList.length > 1) {
					for (var i = 0; i < wgList.length; i++) {
						if (wgList[i].getIdentifier() != wg.getIdentifier()) {
							wgList[i].setStartupConfig();

							break;
						}
					}
				}
				else {
					localStorage.removeItem(wgStartupConfig);
				}
			}

			localStorage.removeItem(fileName);

			return true;
		}

		return false;
	};

	// private module members
	var wgConfigPrefix = "com.censoft.ax.wg.config";

	var wgWindowPrefix = "com.censoft.ax.wg.window";

	var wgStartupConfig = "com.censoft.ax.wg.startupConfig";

	var wgSaveBeforeCloseKey = "com.censoft.ax.wg.saveBeforeClose";

	function jsonHandler (key, value) {
		if (value && typeof value === "object") {
			if (!(value instanceof Array)) {
				var result = {};

				for (var pkey in value) {
					if (value.hasOwnProperty(pkey)) {
						var item = value[pkey];

						if (typeof item === "function" && item.serialize) {
							result[item.serializationKey || pkey] = item();
						}
						else {
							result[pkey] = item;
						}
					}
				}

				return result;
			}
		}

		return value;
	}

	function generateConfigIdentifier() {
		var maxNumber = -1;

		for (var key in localStorage) {
			if (key.startsWith(wgConfigPrefix)) {
				var number = key.match(/\.([0-9]+$)/);

				if (number) {
					number = parseInt(number[1]);

					if (number > maxNumber) {
						maxNumber = number;
					}
				}
			}
		}

		maxNumber += 1;

		return wgConfigPrefix + "." + maxNumber;
	}

	// century window prototype
	function cenWinConfig(wSettings) {
		this.getWinPosX = function () {
			return wSettings.posX;
		};
		this.getWinPosX.serialize = true;
		this.getWinPosX.serializationKey = 'posX';

		this.setWinPosX = function (x) {
			if (x == null || typeof x !== "number")
				throw "[cenWinConfig.setWinPosX]: Argument (x) must be of type number.";

			wSettings.posX = x;
		};

		this.getWinPosY = function () {
			return wSettings.posY;
		};
		this.getWinPosY.serialize = true;
		this.getWinPosY.serializationKey = 'posY';

		this.setWinPosY = function (x) {
			if (x == null || typeof x !== "number")
				throw "[cenWinConfig.setWinPosY]: Argument (x) must be of type number.";

			wSettings.posY = x;
		};

		this.getWinWidth = function () {
			return wSettings.width;
		};
		this.getWinWidth.serialize = true;
		this.getWinWidth.serializationKey = 'width';

		this.setWinWidth = function (x) {
			if (x == null || typeof x !== "number")
				throw "[cenWinConfig.setWinWidth]: Argument (x) must be of type number.";

			wSettings.width = x;
		};

		this.getWinHeight = function () {
			return wSettings.height;
		};
		this.getWinHeight.serialize = true;
		this.getWinHeight.serializationKey = 'height';

		this.setWinHeight = function (x) {
			if (x == null || typeof x !== "number")
				throw "[cenWinConfig.setWinHeight]: Argument (x) must be of type number.";

			wSettings.height = x;
		};

		this.getTpxConfig = function () {
			return wSettings.tpxConfig;
		}
		this.getTpxConfig.serialize = true;
		this.getTpxConfig.serializationKey = 'tpxConfig';

		this.setTpxConfig = function (configName) {
			if (configName == null || typeof configName !== "string")
				throw "[cenWinConfig.setTpxConfig]: Argument (tpxConfig) must be of type string.";

			wSettings.tpxConfig = configName;
		};

		this.getIdentifier = function () {
			return wSettings.identifier;
		};
		this.getIdentifier.serialize = true;
		this.getIdentifier.serializationKey = 'identifier';

		this.getJSON = function () {
			return JSON.stringify(wSettings, jsonHandler);
		};
	}

	// century window group prototype
	function cenWgConfig(wgSettings) {
		// private members
		var loaded = false;

		var windows = [];

		function reset() {
			wgSettings.identifier = null;
			wgSettings.windows = [];

			windows = [];

			loaded = false;
		}

		function generateWindowIdentifier() {
			var maxId = -1;

			for (var key in localStorage) {
				if (key.startsWith(wgWindowPrefix)) {
					var r = key.match(/\.([0-9]+)$/);

					var id = parseInt(r[1]);

					if (id >= maxId) {
						maxId = id;
					}
				}
			}

			maxId += 1;

			return wgWindowPrefix + "." + maxId;
		}

		// public members
		this.addWindow = function (posX, posY, width, height, tpxConfig, identifier) {
			var wSettings = new cenWinConfig({
				posX: posX,
				posY: posY,
				width: width,
				height: height,
				tpxConfig: tpxConfig,
				identifier: identifier || generateWindowIdentifier()
			});

			var ident = wSettings.getIdentifier();

			wgSettings.windows.push(ident);

			windows.push(wSettings);

			// This object is now loaded with data and can be serialized.
			loaded = true;

			return wSettings;
		};

		this.getNumWindows = function () {
			return wgSettings.windows.length;
		};

		this.getWindowByIdentifier = function (ident) {
			var windows = this.getWindows();

			for (var i = 0; i < windows.length; i++) {
				if (windows[i].getIdentifier() == ident) {
					return windows[i];
				}
			}

			return null;
		};

		this.delWindowByIdentifier = function (ident) {
			var windows = this.getWindows();

			for (var i = 0; i < windows.length; i++) {
				if (windows[i].getIdentifier() == ident) {
					wgSettings.windows.splice(i, 1);
					windows.splice(i, 1);

					return true;
				}
			}

			return false;
		};

		this.isVisible = function () {
			return wgSettings.visible;
		};

		this.setVisible = function (visible) {
			if (typeof visible !== "boolean")
				throw "[cenWgConfig.setVisible]: Argument (visible) must be of type bool.";

			wgSettings.visible = visible;
		};

		this.getWindows = function () {
			return windows;
		};

		this.getTitle = function () {
			return wgSettings.title;
		};

		this.setTitle = function (title) {
			if (!title || typeof title !== "string")
				throw "[cenWgConfig.setTitle]: Argument (title) must be of type string.";

			wgSettings.title = title;

			return this;
		};

		this.getJSON = function () {
			return JSON.stringify(wgSettings, jsonHandler);
		};

		this.loadConfig = function (configName) {
			if (configName) {
				if (typeof configName !== "string")
					throw "[cenWgConfig.loadConfig]: Argument (configName) must be of type string.";

				var data = localStorage.getItem(configName);

				if (!data)
					throw "[cenWgConfig.loadConfig]: Configuration not found.";

				reset();

				var dataObj = JSON.parse(data);

				wgSettings.identifier = configName;

				this.setTitle(dataObj.title);
				this.setVisible(dataObj.visible);

				for (var i = 0; i < dataObj.windows.length; i++) {
					var ident = dataObj.windows[i];

					var json = localStorage.getItem(ident);

					var item = JSON.parse(json);

					this.addWindow(
						item.posX,
						item.posY,
						item.width,
						item.height,
						item.tpxConfig,
						item.identifier
					);
				}
			}
			else {
				var defaultConfig = my.getStartupConfig();

				if (!defaultConfig)
					throw "[cenWgConfig.loadConfig]: Startup configuration not found.";

				this.loadConfig(defaultConfig);
			}

			return this;
		};

		this.saveConfig = function (configName) {
			if (!loaded)
				throw "[cenWgConfig.saveConfig]: Configuration not loaded.";

			if (configName) {
				if (typeof configName !== "string")
					throw "[cenWgConfig.saveConfig]: Argument (configName) must be of type string.";

				localStorage.setItem(configName, this.getJSON());
			}
			else {
				var configName = this.getIdentifier();

				this.saveConfig(configName);
			}

			return this;
		};

		this.saveWindowConfigs = function () {
			var windows = this.getWindows();

			for (var i = 0; i < windows.length; i++) {
				this.saveWindowConfig(windows[i]);
			}
		};

		this.saveWindowConfig = function (windowConfig) {
			if (!windowConfig)
				throw "[cenWgConfig.saveWindowConfig]: Argument (windowConfig) is required.";

			var w = null;

			if (typeof windowConfig === "string") 
				w = this.getWindowByIdentifier(windowConfig);
			else if (windowConfig instanceof cenWinConfig) 
				w = windowConfig;

			if (!w)
				throw "[cenWgConfig.saveWindowconfig]: Window configuration not found.";

			localStorage.setItem(w.getIdentifier(), w.getJSON());
		};

		this.forkConfig = function () {
			var identifier = generateConfigIdentifier();

			wgSettings.identifier = identifier;

			this.saveConfig(identifier);
			this.setStartupConfig();

			return this;
		};

		this.isStartupConfig = function () {
			var config = localStorage.getItem(wgStartupConfig);

			return config && config == this.getIdentifier();
		};

		this.setStartupConfig = function () {
			localStorage.setItem(wgStartupConfig, this.getIdentifier());

			return this;
		};

		this.getIdentifier = function () {
			if (!wgSettings.identifier)
				wgSettings.identifier = generateConfigIdentifier();

			return wgSettings.identifier;
		};

		this.load = function () {
			if (!loaded)
				throw "[cenWgConfig.load]: Cannot load window group without a loaded configuration.";

			var windows = this.getWindows();

			for (var i = 0; i < windows.length; i++) {
				if (i == 0) {
					window.onbeforeunload = null;
					window.resizeTo(windows[i].getWinWidth(), windows[i].getWinHeight());
					window.moveTo(windows[i].getWinPosX(), windows[i].getWinPosY());
					window.location = this.getLaunchUrl(windows[i]);
				}
				else {
					this.loadWindow(windows[i]);
				}
			}

			return this;
		};
	}

	cenWgConfig.prototype.getLaunchUrl = function (winConfig) {
		if (!winConfig)
			throw "[cenWgConfig.loadWindow]: Argument (winConfig) is required.";

		if (!(winConfig instanceof cenWinConfig))
			throw "[cenWgConfig.loadWindow]: Argument (winConfig) must be fo type cenWinConfig.";

		return cenAX.baseURL() + "?loadConfigFilename=" + encodeURIComponent(winConfig.getTpxConfig()) +
			"&currentWindowConfig=" + encodeURIComponent(winConfig.getIdentifier());
	};

	cenWgConfig.prototype.loadWindow = function (winConfig) {
		if (!winConfig)
			throw "[cenWgConfig.loadWindow]: Argument (winConfig) is required.";

		if (!(winConfig instanceof cenWinConfig))
			throw "[cenWgConfig.loadWindow]: Argument (winConfig) must be of type cenWinConfig.";

		var specs = "toolbar=no,menubar=no,resizable=yes,location=no" +
			",height=" + winConfig.getWinHeight() +
			",width=" + winConfig.getWinWidth() +
			",left=" + winConfig.getWinPosX() +
			",top=" + winConfig.getWinPosY();

		var launchUrl = this.getLaunchUrl(winConfig);

		var newWindow = window.open(launchUrl, "_blank", specs);

		return this;
	};

	return my;
})();
