//This file was automatically generated
//Do not edit this file directly.
///*{WS_PCGN_GENERATED_FILE}*/

const ameEhPreferenceCodegen = (function() {
	'use strict';

	function parseConfiguration(jsData) {
		//Copy the configuration data to a new object so that it doesn't unexpectedly
		//change if someone modifies the properties of the input object. This also
		//allows us to set defaults easily.
		return jQuery.extend({
				ajaxUrl: null,
				ajaxNonce: null,
				ajaxAction: null,
				name: '[UnnamedPreference]',
				data: {},
				defaults: {},
				autoSaveEnabled: false,
			},
			jsData
		);
	}

	function initialisePreference(instance, config, entityType) {
		'use strict';

		entityType = entityType || 'preference';

		let isAutoSaveEnabled = config.autoSaveEnabled || false;

		let rateLimitTimeout = 2000;
		let saveTimerId = null;

		Object.defineProperty(instance, 'isAutoSaveEnabled', {
			get: function() {
				return isAutoSaveEnabled;
			}
		});

		instance.scheduleSave = function() {
			//Debounce: Save the data only after no changes have happened
			//for X milliseconds.
			if (saveTimerId) {
				clearTimeout(saveTimerId);
			}
			saveTimerId = setTimeout(function() {
				saveTimerId = null;
				instance.save();
			}, rateLimitTimeout);
		}

		instance.save = function() {
			//When someone directly triggers a save, cancel any pending save timer.
			if (saveTimerId) {
				clearTimeout(saveTimerId);
				saveTimerId = null;
			}

			//This only works if AJAX is enabled.
			if (!config.ajaxAction) {
				if (console && console.warn) {
					console.warn(
						'Cannot save ' + entityType + ' "' + config.name + '" because ' +
						'AJAX configuration is not available.'
					);
				}
				return false;
			}

			//Save the value via AJAX.
			// noinspection JSUnusedGlobalSymbols
			jQuery.ajax(
				config.ajaxUrl, {
					method: 'POST',
					data: {
						"action": config.ajaxAction,
						"_ajax_nonce": config.ajaxNonce,
						"data": JSON.stringify(instance.toJs())
					},
					dataType: 'json',
					success: function(response) {
						if (response && response.error) {
							if (console && console.error) {
								console.error(
									'Error saving ' + entityType + ' "' + config.name + '": ' +
									response.error.message,
									response
								);
							}
						}
					},
					error: function(jqXHR, textStatus) {
						let data = jqXHR.responseText;
						if (typeof jqXHR['responseJSON'] !== 'undefined') {
							data = jqXHR['responseJSON'];
						} else if (typeof jqXHR['responseXML'] !== 'undefined') {
							data = jqXHR['responseXML'];
						}

						if (console && console.error) {
							console.error(
								'A network error happened while saving ' + entityType +
								' "' + config.name + '". ',
								data,
								'Status: ' + textStatus,
							);
						}
					}
				}
			);
		};

		instance.enableAutoSave = function(debounceTimeout) {
			if (debounceTimeout === undefined) {
				rateLimitTimeout = 2000;
			} else {
				rateLimitTimeout = debounceTimeout;
			}

			isAutoSaveEnabled = true;
		};

		let subscriptions = {
			'beforeChange': [],
			'afterChange': [],
		};

		/**
		 * @param {string} event
		 * @param callback
		 * @param {string} [preferenceName]
		 */
		instance.subscribe = function(event, callback, preferenceName) {
			if (typeof subscriptions[event] !== 'undefined') {
				subscriptions[event].push({
					callback: callback,
					preferenceName: preferenceName
				});
			} else {
				throw new Error('Cannot subscribe to unknown event type "' + event + '"');
			}
		};

		instance.triggerEvent = function(event, preferenceName) {
			if (typeof subscriptions[event] !== 'undefined') {
				for (let i = 0; i < subscriptions[event].length; i++) {
					let subscription = subscriptions[event][i];
					if (
						(typeof subscription.preferenceName === 'undefined') ||
						(subscription.preferenceName === preferenceName)
					) {
						subscription.callback(preferenceName);
					}
				}
			}
		};

		instance.beforeChange = function(preferenceName) {
			instance.triggerEvent('beforeChange', preferenceName);
		};
		instance.afterChange = function(preferenceName) {
			instance.triggerEvent('afterChange', preferenceName);
		};

		return instance;
	}

	function setupSinglePreference(jsData, validator) {
		'use strict';

		const config = parseConfiguration(jsData);
		let currentValue = config.value;

		const instance = function(newValue) {
			if (typeof newValue === 'undefined') {
				return currentValue;
			}

			const validationResult = instance.validate(newValue);
			if (validationResult.error) {
				throw new Error(config.name + ': ' + validationResult.error);
			} else {
				if (currentValue !== validationResult.value) {
					instance.beforeChange();
					currentValue = validationResult.value;
					instance.afterChange();
					//Auto-save the new value.
					if (instance.isAutoSaveEnabled) {
						instance.scheduleSave();
					}
				}
			}

			return instance;
		};

		instance.validate = function(inputValue) {
			return validator(inputValue);
		};

		instance.toJs = function() {
			return currentValue;
		};

		/**
		 * @param {KnockoutStatic} ko
		 * @returns {KnockoutComputed<any>}
		 */
		instance.observable = function(ko) {
			const dependencyTrigger = ko.observable(currentValue);

			const obs = ko.computed({
				read: function() {
					dependencyTrigger();
					return instance()
				},
				write: function(newValue) {
					instance(newValue);
				},
				deferEvaluation: true
			});

			instance.subscribe('afterChange', function() {
				dependencyTrigger(currentValue);
			});

			return obs;
		};

		initialisePreference(instance, config, 'preference');

		return instance;
	}

	function setupPreferenceGroup(instance, jsData, validators, availableProperties) {
		const config = parseConfiguration(jsData);

		let currentValues = jQuery.extend({}, config.data);
		const defaults = jQuery.extend({}, config.defaults);

		instance.toJs = function() {
			return jQuery.extend({}, currentValues);
		};

		/**
		 * @param {KnockoutStatic} ko
		 */
		instance.observableObject = function(ko) {
			/*
			Hack: For each property, we have a computed observable that reads/writes
			the property and a second observable that exists purely to make Knockout
			update stuff that depends on the computed observable (e.g. templates).

			Simply calling notifySubscribers() would not work because Knockout treats
			a computed observable without dependencies as inactive.

			So why not use one regular (not computed) observable instead? Because we
			want the property to remain the single source of truth for the value.
			Also, you would need extra logic to avoid infinite recursion.
			 */
			const observableProperties = {};
			const dependencyTriggers = {};

			availableProperties.forEach(function(preferenceName) {
				dependencyTriggers[preferenceName] = ko.observable(instance[preferenceName]);

				observableProperties[preferenceName] = ko.computed({
					read: function() {
						dependencyTriggers[preferenceName]();
						return instance[preferenceName];
					},
					write: function(newValue) {
						instance[preferenceName] = newValue;
					},
					deferEvaluation: true
				});
			});

			instance.subscribe('afterChange', function(preferenceName) {
				if (typeof preferenceName === 'undefined') {
					return;
				}
				if (typeof dependencyTriggers[preferenceName] !== 'undefined') {
					dependencyTriggers[preferenceName](instance[preferenceName]);
				}
			});

			return observableProperties;
		};

		function makeGetter(preferenceName) {
			return function() {
				if (currentValues.hasOwnProperty(preferenceName)) {
					return currentValues[preferenceName];
				} else if (defaults.hasOwnProperty(preferenceName)) {
					return defaults[preferenceName];
				}
				return null;
			};
		}

		function makeSetter(preferenceName) {
			return function(newValue) {
				const validationResult = validators[preferenceName](newValue);
				if (validationResult.error) {
					throw new Error(config.name + ': ' + validationResult.error);
				} else {
					if (currentValues[preferenceName] !== validationResult.value) {
						instance.beforeChange(preferenceName);
						currentValues[preferenceName] = validationResult.value;
						instance.afterChange(preferenceName);

						//Auto-save the new value.
						if (instance.isAutoSaveEnabled) {
							instance.scheduleSave();
						}
					}
				}
			};
		}

		availableProperties.forEach(function(preferenceName) {
			Object.defineProperty(instance, preferenceName, {
				get: makeGetter(preferenceName),
				set: makeSetter(preferenceName)
			});
		});

		initialisePreference(instance, config, 'preference group');
	}

	return {
		setupPreference: setupSinglePreference,
		setupGroup: setupPreferenceGroup
	}
})();

function AmeEhIsExplanationHidden(jsData) {
	"use strict";

	return ameEhPreferenceCodegen.setupPreference(
		jsData,
		function(inputValue) {
			let convertedValue;
			convertedValue = Number(inputValue);
			if (isNaN(convertedValue)) {
				return {
					"error": "Value must be a number",
					"value": "convertedValue"
				};
			}
			if (convertedValue !== Math.trunc(convertedValue)) {
				return {
					"error": "Value must be an integer",
					"value": "convertedValue"
				};
			}
			if (convertedValue < 0) {
				return {
					"error": "Value must be at least 0.",
					"value": convertedValue
				};
			}
			if (convertedValue > 1) {
				return {
					"error": "Value must be less than or equal to 1",
					"value": convertedValue
				};
			}
			return {
				"error": null,
				"value": convertedValue
			};
		}
	);
}

AmeEhIsExplanationHidden.pcgnIsConstructor = false;

function AmeEhUserPreferences(jsData) {
	'use strict';

	ameEhPreferenceCodegen.setupGroup(
		this,
		jsData, {
			'numberOfColumns': function(inputValue) {
				if (inputValue === null) {
					return {
						"error": null,
						"value": inputValue
					};
				}
				let convertedValue;
				convertedValue = Number(inputValue);
				if (isNaN(convertedValue)) {
					return {
						"error": "Value must be a number",
						"value": "convertedValue"
					};
				}
				if (convertedValue !== Math.trunc(convertedValue)) {
					return {
						"error": "Value must be an integer",
						"value": "convertedValue"
					};
				}
				if (convertedValue < 1) {
					return {
						"error": "Value must be at least 1.",
						"value": convertedValue
					};
				}
				if (convertedValue > 3) {
					return {
						"error": "Value must be less than or equal to 3",
						"value": convertedValue
					};
				}
				return {
					"error": null,
					"value": convertedValue
				};
			},
			'csExpandedCategories': function(inputValue) {
				let convertedValue;
				if (
					(typeof inputValue === 'object') ||
					(typeof inputValue === 'function')
				) {
					return {
						"error": "Value must be a string or a non-null scalar",
						"value": "inputValue"
					};
				}
				convertedValue = String(inputValue);
				if (convertedValue.length > 1000) {
					return {
						"error": "Value must be no more than 1000 characters long",
						"value": convertedValue
					};
				}
				return {
					"error": null,
					"value": convertedValue
				};
			},
		},
		["numberOfColumns", "csExpandedCategories"]
	);
}

AmeEhUserPreferences.pcgnIsConstructor = true;