/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
(function(){
	"use strict";
	
	let scope;
	if ((typeof exports) !== "undefined"){
		scope = exports;
	}
	else {
		scope = require.register("./settingContainers", {});
	}
	
	const logging = require("./logging");
	
	scope.urlContainer = null;
	scope.hideContainer = null;
	scope.expandContainer = null;
	
	scope.getUrlValueContainer = function(name, url){
		const matching = scope.urlContainer.get().filter(function(urlSetting){
			return urlSetting.hasOwnProperty(name);
		}).filter(function(urlSetting){
			return urlSetting.match(url);
		});
		if (matching.length){
			return matching[0];
		}
		else {
			return null;
		}
	};
	scope.setUrlValue = function(name, value, url){
		const urlContainerValue = scope.urlContainer.get();
		let matching = urlContainerValue.filter(function(urlSetting){
			return urlSetting.match(url);
		});
		if (!matching.length){
			let newEntry = {url};
			newEntry[name] = value;
			urlContainerValue.push(newEntry);
			initializeUrlSetting(newEntry);
			matching = [newEntry];
		}
		matching[0][name] = value;
		return scope.urlContainer.set(urlContainerValue);
	};
	scope.resetUrlValue = function(name, url){
		let urlContainerValue = scope.urlContainer.get();
		urlContainerValue.filter(function(urlSetting){
			return urlSetting.match(url);
		}).forEach(function(match){
			delete match[name];
			if (Object.keys(match).every(function(key){return key === "url";})){
				urlContainerValue = urlContainerValue.filter(function(urlSetting){
					return urlSetting !== match;
				});
			}
		});
		scope.urlContainer.set(urlContainerValue);
	};
	
	function processHideContainer(settingDefinition){
		scope.hideContainer = settingDefinition;
		let changeListeners = {};
		settingDefinition.setHideByName = function(name, value){
			logging.verbose("set hide of", name, "to", value);
			const hideStore = settingDefinition.get();
			hideStore[name] = value;
			settingDefinition.set(hideStore);
			(changeListeners[name] || []).forEach(function(listener){
				listener(value);
			});
		};
		settingDefinition.getHideByName = function(name){
			const hideStore = settingDefinition.get();
			return hideStore[name] || false;
		};
		settingDefinition.onHideChange = function(name, listener){
			if (!changeListeners[name]){
				changeListeners[name] = [];
			}
			changeListeners[name].push(listener);
		};
		settingDefinition.on(function(event){
			const value = event.newValue;
			Object.keys(value).forEach(function(name){
				(changeListeners[name] || []).forEach(function(listener){
					listener(value[name]);
				});
			});
			const oldValue = event.oldValue;
			Object.keys(oldValue).filter(function(name){
				return !value.hasOwnProperty(name);
			}).forEach(function(name){
				(changeListeners[name] || []).forEach(function(listener){
					listener(false);
				});
			});
		});
		settingDefinition.hideAble = false;
	}
	
	function processExpandContainer(settingDefinition){
		scope.expandContainer = settingDefinition;
		let changeListeners = {};
		settingDefinition.setExpandByName = function(name, value){
			logging.verbose("set expand of", name, "to", value);
			const expandStore = settingDefinition.get();
			expandStore[name] = value;
			settingDefinition.set(expandStore);
			(changeListeners[name] || []).forEach(function(listener){
				listener(value);
			});
		};
		settingDefinition.getExpandByName = function(name, defaultValue = false){
			const expandStore = settingDefinition.get();
			if ((typeof expandStore[name]) !== "undefined"){
				return expandStore[name] || false;
			}
			else {
				return defaultValue;
			}
		};
		settingDefinition.onExpandChange = function(name, listener){
			if (!changeListeners[name]){
				changeListeners[name] = [];
			}
			changeListeners[name].push(listener);
		};
		settingDefinition.on(function(event){
			const value = event.newValue;
			Object.keys(value).forEach(function(name){
				(changeListeners[name] || []).forEach(function(listener){
					listener(value[name]);
				});
			});
			const oldValue = event.oldValue;
			Object.keys(oldValue).filter(function(name){
				return !value.hasOwnProperty(name);
			}).forEach(function(name){
				(changeListeners[name] || []).forEach(function(listener){
					listener(false);
				});
			});
		});
	}
	
	scope.check = function(settingDefinition){
		if (settingDefinition.isUrlContainer){
			scope.urlContainer = settingDefinition;
			settingDefinition.refresh = function(){
				settingDefinition.set(settingDefinition.get());
			};
		}
		
		if (settingDefinition.isHideContainer){
			processHideContainer(settingDefinition);
		}
		
		if (settingDefinition.isExpandContainer){
			processExpandContainer(settingDefinition);
		}
	};
	
	function initializeUrlSetting(urlSetting){
		let regExp;
		const domain = !!urlSetting.url.match(/^[A-Za-z0-9_.-]+$/);
		if (domain){
			regExp = new RegExp(
				"(?:^|\\.)" + urlSetting.url.replace(/([\\+*?[^\]$(){}=!|.])/g, "\\$1") + "\\.?$",
				"i"
			);
		}
		else {
			try {
				regExp = new RegExp(urlSetting.url, "i");
			}
			catch (error){
				logging.error("Error in regular expression", urlSetting.url, error);
				regExp = new RegExp(
					"(?:^|\\.)" + urlSetting.url.replace(/([\\+*?[^\]$(){}=!|.])/g, "\\$1") + "\\.?$",
					"i"
				);
			}
		}
		const match = function(url){
			if (!url){
				return false;
			}
			else if (
				url instanceof String ||
				(typeof url) === "string"
			){
				return url === urlSetting.url;
			}
			else if (domain){
				return (url.hostname || "").match(regExp);
			}
			else {
				return url.href.match(regExp);
			}
		};
		Object.defineProperty(
			urlSetting,
			"match",
			{
				enumerable: false,
				writable: true,
				configurable: true,
				value: match
			}
		);
	}
	
	scope.initializeUrlContainer = function(eventHandler){
		if (!scope.urlContainer){
			return;
		}
		scope.urlContainer.on(function({newValue, oldValue}){
			newValue.forEach(initializeUrlSetting);
			
			const newUrls = newValue.map(function(entry){return entry.url;});
			const oldUrls = oldValue.map(function(entry){return entry.url;});
			const matching = {};
			newUrls.forEach(function(url, i){
				matching[url] = {new: i, old: oldUrls.indexOf(url)};
			});
			oldUrls.forEach(function(url, i){
				if (!matching[url]){
					matching[url] = {new: -1, old: i};
				}
			});
			Object.keys(matching).forEach(function(url){
				const oldEntry = oldValue[matching[url].old] || {};
				const newEntry = newValue[matching[url].new] || {};
				scope.urlContainer.entries.forEach(function(settingDefinition){
					const name = settingDefinition.name;
					const oldValue = oldEntry[name];
					const newValue = newEntry[name];
					
					if (oldValue !== newValue){
						((eventHandler[name] || {})[url] || []).forEach(function(callback){
							callback({name, newValue, oldValue, url});
						});
					}
				});
			});
		});
	};
}());