/*
 * This class is used to create device password based on security policies 
 
Copyright 2009 Palm, Inc.  All rights reserved.

*/
var SecuritypolicyAssistant = Class.create({

	initialize: function(params, onSuccess) {
		this.sceneResults = {};
		this.policies = params || {};
		if (this.policies.password === undefined) {
			this.policies.password = {};
		}

		this.onSuccess = onSuccess || Mojo.doNothing;
		this.password1 = "";
		this.password2 = "";
		this.completed = false;
	},

	setup: function() {
		this.passwords = { password1:"", password2:"" };
		this.password1Attributes.maxLength = 64;
		this.password1Attributes.modifierState = undefined;
		this.password2Attributes.maxLength = 64;
		this.password2Attributes.modifierState = undefined;
		
		this.controller.setupWidget('securitypolicy-password1', this.password1Attributes, this.passwords);
		this.controller.setupWidget('securitypolicy-password2', this.password2Attributes, this.passwords);

		this.controller.setupWidget('cancel-signin-button', {}, this.cancelSignInButtonModel);
		this.controller.listen('cancel-signin-button', Mojo.Event.tap, this.cancel.bind(this));

		this.tryPolicyButtonModel.disabled = false; // ensure the button starts out enabled
		this.controller.setupWidget('set-password-button', {}, this.tryPolicyButtonModel);
		this.controller.listen('set-password-button', Mojo.Event.tap, this.validatePassword.bind(this));

		this.boundHandlePropertyChange = this.handlePropertyChange.bindAsEventListener(this)
		this.controller.listen(this.controller.sceneElement, Mojo.Event.propertyChange, this.boundHandlePropertyChange);
		
		this.controller.get('password-instructions').update(SecuritypolicyAssistant.formatRequirementsString(this.policies.password));		
	},

	cleanup: function() {
		// If the user never completed setting up the account remove the pending policy
		if (!this.completed) {
			this.removePolicy();
		}
		this.controller.stopListening(this.controller.sceneElement, Mojo.Event.propertyChange, this.boundHandlePropertyChange);
	},

	handleCommand: function(event) {
		if (event.type === Mojo.Event.back) {
			event.stop();
			// If editing then save the account now
			Mojo.Log.info("----------- SecurityPolicyAssistant handleCommand back");
			this.cancel();
		}
	},

	showError: function(error) {
		this.controller.get("password-error-text").update(error);
		this.controller.get("password-error").show();
	},

	validatePassword: function() {
		if (this.passwords.password1 !== this.passwords.password2) {
			this.showError(SecuritypolicyAssistant.kPasswordsDontMatch);
			this.controller.get("securitypolicy-password1").mojo.focus.defer();
		} else {
			// Disable the "done" button so user can't tap it again while we're validating
			this.tryPolicyButtonModel.disabled = true;
			this.controller.modelChanged(this.tryPolicyButtonModel);
			var lockMode = "password";

			this.controller.serviceRequest(SecuritypolicyAssistant.kSystemManager, {
				method: 'setDevicePasscode', //validate password meets the security policy requirements
				parameters: { passCode: this.passwords.password1, lockMode: lockMode },
				onSuccess: this.passwordSuccess.bind(this),
				onFailure: this.passwordError.bind(this),
			});
		}
	},

	passwordSuccess: function(response) {
		Mojo.Log.info("passwordSuccess %j", response);
		//Tell the mailservice to create the account
		this.completed = true;
		this.onSuccess();
		this.popScene('create');
	},

	passwordError: function(response) {
		Mojo.Log.info("passwordError %j", response);
		this.showError(SecuritypolicyAssistant.kPasswordsNoGoodEnough);

		// Renable the "done" button
		this.tryPolicyButtonModel.disabled = false;
		this.controller.modelChanged(this.tryPolicyButtonModel);

		this.controller.get("securitypolicy-password1").mojo.focus();
	},

	cancel: function() {
		// Clear out the policy since the user cancelled
		this.removePolicy();
		this.popScene('back');
	},

	removePolicy: function() {
		this.controller.serviceRequest(SecuritypolicyAssistant.kSystemManager, {
			method: 'removeSecurityPolicy',
			parameters: { id: this.policies.id.toLowerCase() },
			onSuccess: Mojo.doNothing,
			onFailure: Mojo.doNothing,
		});
	},
	
	popScene: function(action) {
		Mojo.Log.info("----------- SecurityPolicyAssistant popping with result:", action);
		this.controller.stageController.popScene({ action: action });
	},

	handlePropertyChange: function(event) {
		// If the password field has focus and Enter is pressed then submit the passwords
		if (event && event.originalEvent && Mojo.Char.isEnterKey(event.originalEvent.keyCode)) {
			Event.stop(event);
			this.validatePassword();
		}
	},

	password1Attributes: {
		modelProperty: 'password1',
		focusMode: Mojo.Widget.focusSelectMode,
		textReplacement: false,
		maxLength: 64
	},

	password2Attributes: {
		modelProperty: 'password2',
		focusMode: Mojo.Widget.focusSelectMode,
		textReplacement: false,
		requiresEnterKey: true,
		changeOnKeyPress: true,
		maxLength: 64
	},

	cancelSignInButtonModel: {
		buttonClass:'primary',
		buttonLabel:$L('Cancel')
	},

	tryPolicyButtonModel: {
		buttonClass:'affirmative',
		buttonLabel:$L('Done')
	}
});


SecuritypolicyAssistant.kPasswordsDontMatch = $L("Passwords don't match");
SecuritypolicyAssistant.kPasswordsNoGoodEnough = $L("Password does not match security requirements");

SecuritypolicyAssistant.kSystemManager = "palm://com.palm.systemmanager";

/**
 * This function initiates the UI flow for password policy
 * @param {Object} controller
 * @param {Object} emailAddress - address for this account (used to verify that the current policy is for this account)
 * @param {Object} onSuccess - optional callback that will be invoked when the user completes the process
 * @param {Object} onCancel - optional callback that will be invoked if the user cancels out of the process
 */
SecuritypolicyAssistant.handlePasswordPolicy = function(controller, emailAddress, onSuccess, onCancel) {
	Mojo.Log.info("SecuritypolicyAssistant.handlePasswordPolicy addr", emailAddress);
	var retryCount = 0;
	var successHandler = function(response) {
		if (!response || !response.policy) {
			Mojo.Log.error("SecuritypolicyAssistant.handlePasswordPolicy(): no security policy found in response");
			return;
		}
	
		Mojo.Log.info("SecuritypolicyAssistant.successHandler %j", response);
		var status = response.policy.status;
		var passwordReq = response.policy.password;
		
		// if password is required and the security policy is not enforced yet,
		// prompt the user to enter password 
		if (status && status.enforced === false && passwordReq && passwordReq.enabled === true) {
			// policy id is no longer supplied by SystemManager, so we add it back
			// to policy object.
			response.policy.id = emailAddress;
			SecuritypolicyAssistant.showPasswordRequiredDlg(controller, response.policy, onSuccess, onCancel);
		} else {
			onSuccess();
		}
	};

	var failHandler = function(response) {
		++retryCount;
		Mojo.Log.info("SecuritypolicyAssistant.failHandler %d %j", retryCount, response);
		if (retryCount > 3) {
			Mojo.Log.error("SecuritypolicyAssistant.handlePasswordPolicy failed to password policies from systemmanager");
			controller.showAlertDialog({
				title:$L("Can't add account"),
				message:$L("Unable to retrieve security policies from the mail server."),
				choices:[ {label:$L('Done'), value:'done'} ],
				onChoose: function(value) { if (onCancel) onCancel(); }
			});
		} else {
			getPolicies.delay(0.5);
		}
	};

	var getPolicies = function() {
		controller.serviceRequest(SecuritypolicyAssistant.kSystemManager, {
			method: 'getSecurityPolicy',
			onSuccess: successHandler,
			onFailure: failHandler,
		});
	}
	getPolicies();
};

/*
 * Generate the password requirements to display to the user.
 */
SecuritypolicyAssistant.formatRequirementsString = function(passwordRequirements) {
	var requirements;
	var minLen = passwordRequirements.minLength || 1;
	if (passwordRequirements.alphaNumeric === false) {
		requirements = $L("Password must be #{minLength} or more characters long.").interpolate({'minLength': minLen});
	} else if (passwordRequirements.alphaNumeric && passwordRequirements.minLength > 0) {
		requirements = $L("Password must be #{minLength} or more characters long and contain both numbers and letters.").interpolate({'minLength': minLen});
	} else if (passwordRequirements.alphaNumeric) {
		requirements = $L("Password must contain both numbers and letters.");
	} else {
		requirements = "";
	}
	return requirements;
};

/*
 * Initial dialog informing the user that the account has password policy requirements.
 */
SecuritypolicyAssistant.showPasswordRequiredDlg = function(controller, policies, onSuccess, onCancel) {
	Mojo.Log.info("EAS policies requirement=%j", policies);
	var dialogMessage = "";
	var buttonChoices = [{label:$L('Set Password'), value:'set-password', type:'affirmative'}];
	
	if (policies.password.alphaNumeric) {
		dialogMessage = $L('To access this account, you must set a password for secure unlock. This will automatically turn on secure unlock.');
	} else {
		dialogMessage = $L('To access this account, you must set a password or a pin for secure unlock. This will automatically turn on secure unlock.');
		buttonChoices.push({label:$L('Set Pin'), value:'set-pin', type:'affirmative'})
	}
	buttonChoices.push({label:$L('Cancel Sign In'), value:'cancel'});
	
	controller.showAlertDialog({
		title: $L('Password Required'),
		message: dialogMessage,
		choices: buttonChoices,
		onChoose: function(value) {
			if (value === 'set-password') {
				controller.stageController.pushScene('securitypolicy', policies, onSuccess);
			} else if (value === 'set-pin') { 
				controller.stageController.pushScene({
					name: 'setpin', automaticFocusAdvance: false
				}, policies, onSuccess);
			} else {
				// Clear out the policy since the user cancelled
				controller.serviceRequest(SecuritypolicyAssistant.kSystemManager, {
					method: 'removeSecurityPolicy',
					parameters: { id: policies.id.toLowerCase() },
					onSuccess: Mojo.doNothing,
					onFailure: Mojo.doNothing,
				});
				
				if (onCancel) onCancel();
			}
		}
	});
};

/*
 * Check whether the security policy should be removed because the account that set it
 * was removed.
 */
SecuritypolicyAssistant.removeSecurityPolicies = function(accounts) {
	accounts.each(function(account){
		Mojo.Log.info("removeSecurityPolicy because account %s no longer exists", account.policy.id);
		// System manager will take care of the case that the policy doesn't exist
		// for a given id.  Email app doesn't need to check whether the account
		// policy exists or not before removing the account policy.
		var x = new Mojo.Service.Request(SecuritypolicyAssistant.kSystemManager, {
			method: 'removeSecurityPolicy',
			parameters: {
				id: account.originalLogin.toLowerCase()
			},
			onSuccess: Mojo.doNothing,
			onFailure: Mojo.doNothing,
		});
	});
};
