/*******************************************************************************
 * Copyright 2018 The MIT Internet Trust Consortium
 *
 * Portions copyright 2011-2013 The MITRE Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *******************************************************************************/

Backbone.Model.prototype.fetchIfNeeded = function(options) {
	var _self = this;
	if (!options) {
		options = {};
	}
	var success = options.success;
	options.success = function(c, r) {
		_self.isFetched = true;
		if (success) {
			success(c, r);
		}
	};
	if (!this.isFetched) {
		return this.fetch(options);
	} else {
		return options.success(this, null);
	}
};

Backbone.Collection.prototype.fetchIfNeeded = function(options) {
	var _self = this;
	if (!options) {
		options = {};
	}
	var success = options.success;
	options.success = function(c, r) {
		_self.isFetched = true;
		if (success) {
			success(c, r);
		}
	};
	if (!this.isFetched) {
		return this.fetch(options);
	} else {
		return options.success(this, null);
	}
};

var URIModel = Backbone.Model
		.extend({

			validate: function(attrs) {

				var expression = /^(?:([a-z0-9+.-]+:\/\/)((?:(?:[a-z0-9-._~!$&'()*+,;=:]|%[0-9A-F]{2})*)@)?((?:[a-z0-9-._~!$&'()*+,;=]|%[0-9A-F]{2})*)(:(?:\d*))?(\/(?:[a-z0-9-._~!$&'()*+,;=:@\/]|%[0-9A-F]{2})*)?|([a-z0-9+.-]+:)(\/?(?:[a-z0-9-._~!$&'()*+,;=:@]|%[0-9A-F]{2})+(?:[a-z0-9-._~!$&'()*+,;=:@\/]|%[0-9A-F]{2})*)?)(\?(?:[a-z0-9-._~!$&'()*+,;=:\/?@]|%[0-9A-F]{2})*)?(#(?:[a-z0-9-._~!$&'()*+,;=:\/?@]|%[0-9A-F]{2})*)?$/i;
				var regex = new RegExp(expression);

				if (attrs.item == null || !attrs.item.match(regex)) {
					return "Invalid URI";
				}
			}

		});

/*
 * Backbone JS Reusable ListWidget Options { collection: Backbone JS Collection
 * type: ('uri'|'default') autocomplete: ['item1','item2'] List of auto complete
 * items }
 * 
 */
var ListWidgetChildView = Backbone.View.extend({

	tagName: 'tr',

	events: {
		"click .btn-delete-list-item": 'deleteItem',
		"change .checkbox-list-item": 'toggleCheckbox'
	},

	deleteItem: function(e) {
		e.preventDefault();
		e.stopImmediatePropagation();
		// this.$el.tooltip('delete');

		this.model.destroy({
			dataType: false,
			processData: false,
			error: app.errorHandlerView.handleError()
		});

	},

	toggleCheckbox: function(e) {
		e.preventDefault();
		e.stopImmediatePropagation();
		if ($(e.target).is(':checked')) {
			this.options.collection.add(this.model);
		} else {
			this.options.collection.remove(this.model);
		}

	},

	initialize: function(options) {
		this.options = {
			toggle: false,
			checked: false
		};
		_.extend(this.options, options);
		if (!this.template) {
			this.template = _.template($('#tmpl-list-widget-child').html());
		}
	},

	render: function() {

		var data = {
			model: this.model.toJSON(),
			opt: this.options
		};

		this.$el.html(this.template(data));

		$('.item-full', this.el).hide();

		if (this.model.get('item').length > 30) {
			this.$el.tooltip({
				title: $.t('admin.list-widget.tooltip')
			});

			var _self = this;

			// added span to enable checkbox also
			$(this.el).find('span').click(function(event) {
				event.preventDefault();
				$('.item-short', _self.el).hide();
				$('.item-full', _self.el).show();
				_self.$el.tooltip('destroy');
			});
		}

		$(this.el).i18n();
		return this;
	}
});

var ListWidgetView = Backbone.View.extend({

	tagName: "div",

	events: {
		"click .btn-add-list-item": "addItem",
		"keypress": function(e) {
			// trap the enter key
			if (e.which == 13) {
				e.preventDefault();
				this.addItem(e);
				$("input", this.$el).focus();
			}
		}
	},

	initialize: function(options) {
		this.options = options;

		if (!this.template) {
			this.template = _.template($('#tmpl-list-widget').html());
		}

		this.collection.bind('add', this.render, this);
		this.collection.bind('remove', this.render, this);
	},

	addItem: function(e) {
		e.preventDefault();

		var input_value = $("input", this.el).val().trim();

		if (input_value === "") {
			return;
		}

		var model;

		if (this.options.type == 'uri') {
			model = new URIModel({
				item: input_value
			});
		} else {
			model = new Backbone.Model({
				item: input_value
			});
			model.validate = function(attrs) {
				if (!attrs.item) {
					return "value can't be null";
				}
			};
		}

		// if it's valid and doesn't already exist
		if (model.get("item") != null && this.collection.where({
			item: input_value
		}).length < 1) {
			this.collection.add(model);
		} else {
			// else add a visual error indicator
			$(".control-group", this.el).addClass('error');
		}
	},

	render: function(eventName) {

		this.$el.html(this.template({
			placeholder: this.options.placeholder,
			helpBlockText: this.options.helpBlockText
		}));

		var _self = this;

		if (_.size(this.collection.models) == 0 && _.size(this.options.autocomplete) == 0) {
			$("tbody", _self.el).html($('#tmpl-list-widget-child-empty').html());
		} else {

			// make a copy of our collection to work from
			var values = this.collection.clone();

			// look through our autocomplete values (if we have them) and render
			// them all as checkboxes
			if (this.options.autocomplete) {
				_.each(this.options.autocomplete, function(option) {
					var found = _.find(values.models, function(element) {
						return element.get('item') == option;
					});

					var model = null;
					var checked = false;

					if (found) {
						// if we found the element, check the box
						model = found;
						checked = true;
						// and remove it from the list of items to be rendered
						// later
						values.remove(found, {
							silent: true
						});
					} else {
						model = new Backbone.Model({
							item: option
						});
						checked = false;
					}

					var el = new ListWidgetChildView({
						model: model,
						toggle: true,
						checked: checked,
						collection: _self.collection
					}).render().el;
					$("tbody", _self.el).append(el);

				}, this);
			}

			// now render everything not in the autocomplete list
			_.each(values.models, function(model) {
				var el = new ListWidgetChildView({
					model: model,
					collection: _self.collection
				}).render().el;
				$("tbody", _self.el).append(el);
			}, this);
		}

		$(this.el).i18n();
		return this;
	}

});

var BreadCrumbView = Backbone.View.extend({

	tagName: 'ul',

	initialize: function(options) {
		this.options = options;

		if (!this.template) {
			this.template = _.template($('#tmpl-breadcrumbs').html());
		}

		this.$el.addClass('breadcrumb');

		this.collection.bind('add', this.render, this);
	},

	render: function() {

		this.$el.empty();
		var parent = this;

		// go through each of the breadcrumb models
		_.each(this.collection.models, function(crumb, index) {

			// if it's the last index in the crumbs then render the link
			// inactive
			if (index == parent.collection.size() - 1) {
				crumb.set({
					active: true
				}, {
					silent: true
				});
			} else {
				crumb.set({
					active: false
				}, {
					silent: true
				});
			}

			this.$el.append(this.template(crumb.toJSON()));
		}, this);

		$('#breadcrumbs').html(this.el);
		$(this.el).i18n();
	}
});

// User Profile

var UserProfileView = Backbone.View.extend({
	tagName: 'span',

	initialize: function(options) {
		this.options = options;
		if (!this.template) {
			this.template = _.template($('#tmpl-user-profile-element').html());
		}
	},

	render: function() {

		$(this.el).html($('#tmpl-user-profile').html());

		var t = this.template;

		_.each(this.model, function(value, key) {
			if (key && value) {

				if (typeof (value) === 'object') {

					var el = this.el;
					var k = key;

					_.each(value, function(value, key) {
						$('dl', el).append(t({
							key: key,
							value: value,
							category: k
						}));
					});
				} else if (typeof (value) === 'array') {
					// TODO: handle array types
				} else {
					$('dl', this.el).append(t({
						key: key,
						value: value
					}));
				}
			}
		}, this);

		$(this.el).i18n();
		return this;
	}
});

// error handler
var ErrorHandlerView = Backbone.View.extend({

	initialize: function(options) {
		this.options = options;
		if (!this.template) {
			this.template = _.template($('#tmpl-error-box').html());
		}
		if (!this.headerTemplate) {
			this.headerTemplate = _.template($('#tmpl-error-header').html());
		}
	},

	reloadPage: function(event) {
		event.preventDefault();
		window.location.reload(true);
	},

	handleError: function(message) {

		if (!message) {
			message = {};
		}

		var _self = this;

		return function(model, response, options) {

			if (message.log) {
				console.log(message.log);
			}

			_self.showErrorMessage(_self.headerTemplate({
				message: message,
				model: model,
				response: response,
				options: options
			}), _self.template({
				message: message,
				model: model,
				response: response,
				options: options
			}));

			$('#modalAlert .modal-body .page-reload').on('click', _self.reloadPage);

		}
	},

	showErrorMessage: function(header, message) {
		// hide the sheet if it's visible
		$('#loadingbox').sheet('hide');

		$('#modalAlert').i18n();
		$('#modalAlert div.modal-header').html(header);
		$('#modalAlert .modal-body').html(message);

		$('#modalAlert').modal({
			'backdrop': 'static',
			'keyboard': true,
			'show': true
		});

	}
});

// Router
var AppRouter = Backbone.Router.extend({

	routes: {

		"": "root"

	},

	root: function() {
		if (isAdmin()) {
			this.navigate('admin/clients', {
				trigger: true
			});
		} else {
			this.navigate('user/approved', {
				trigger: true
			});
		}
	},

	initialize: function() {

		this.breadCrumbView = new BreadCrumbView({
			collection: new Backbone.Collection()
		});

		this.breadCrumbView.render();

		this.errorHandlerView = new ErrorHandlerView();

		// call all the extra initialization functions
		var app = this;
		_.each(ui.init, function(fn) {
			fn(app);
		});

	},

	notImplemented: function() {
		this.breadCrumbView.collection.reset();
		this.breadCrumbView.collection.add([{
			text: $.t('admin.home'),
			href: ""
		}]);

		this.updateSidebar('none');

		$('#content').html("<h2>Not implemented yet.</h2>");
	},

	updateSidebar: function(item) {
		$('.sidebar-nav li.active').removeClass('active');

		$('.sidebar-nav li a[href^="manage/#' + item + '"]').parent().addClass('active');
	}
});

// holds the global app.
// this gets init after the templates load
var app = null;

// main
$(function() {

	var loader = function(source) {
		return $.get(source, function(templates) {
			console.log('Loading file: ' + source);
			$('#templates').append(templates);
		});
	};

	// load templates and append them to the body
	$.when.apply(null, ui.templates.map(loader)).done(function() {
		console.log('done');
		$.ajaxSetup({
			cache: false
		});
		app = new AppRouter();

		_.each(ui.routes.reverse(), function(route) {
			console.log("Adding route: " + route.name);
			app.route(route.path, route.name, route.callback);
		});

		app.on('route', function(name, args) {
			// scroll to top of page on new route selection
			$("html, body").animate({
				scrollTop: 0
			}, "slow");
		});

		// grab all hashed URLs and send them through the app router instead
		$(document).on('click', 'a[href^="manage/#"]', function(event) {
			event.preventDefault();
			app.navigate(this.hash.slice(1), {
				trigger: true
			});
		});

		var base = $('base').attr('href');
		if (base.substr(-1) !== '/') {
			base += '/';
		}
		$.getJSON(base + '.well-known/openid-configuration', function(data) {
			app.serverConfiguration = data;
			var baseUrl = $.url(app.serverConfiguration.issuer);
			Backbone.history.start({
				pushState: true,
				root: baseUrl.attr('relative') + 'manage/'
			});
		});
	});

	window.onerror = function(message, filename, lineno, colno, error) {
		console.log(message);
		// Display an alert with an error message
		$('#modalAlert div.modal-header').html($.t('error.title'));
		$('#modalAlert div.modal-body').html($.t('error.message') + message + ' <br /> ' + [filename, lineno, colno, error]);

		$("#modalAlert").modal({ // wire up the actual modal functionality
									// and show the dialog
			"backdrop": "static",
			"keyboard": true,
			"show": true
		// ensure the modal is shown immediately
		});

	}
});
