/**
 * Password validator object.
*/
function PasswordStrengthValidator(tdIdPrefix, spanIdPrefix) {
	this.tdIdPrefix			= tdIdPrefix;
	this.spanIdPrefix		= spanIdPrefix;
	
	// constants which control object behaviour
	this.SMALL_LETTERS_CNT			= 3;
	this.CAPITAL_LETTERS_CNT		= 3;
	this.NUMBERS_CNT				= 3;
	this.SYMBOLS_CNT				= 3;
	this.MIN_LEVENSHTEIN_DISTANCE	= 3;
}
PasswordStrengthValidator.prototype.getCharCountBasedStrengthFactor =function(pwd){
	//use of lowercase in the password
	var woLower			= pwd.replace (/[a-z]/g, "");
	var numLower		= (pwd.length - woLower.length);
	var lowerStrength	= Math.min(1, numLower / this.SMALL_LETTERS_CNT);
		
	//use of uppercase in the password
	var woUpper			= pwd.replace (/[A-Z]/g, "");
	var numUpper		=(pwd.length - woUpper.length);
	var upperStrength 	= Math.min(1, numUpper / this.CAPITAL_LETTERS_CNT);
		
	//use of numbers in the password
	var woNumbers 		= pwd.replace (/[0-9]/g, "");
	var numNumeric 		= (pwd.length - woNumbers.length);
	var numericStrength = Math.min(1, numNumeric / this.NUMBERS_CNT);
	
	//use of symbols in the password
	var woSymbols		= pwd.replace (/\W/g, "");
	var numSymbols		=(pwd.length - woSymbols.length);
	var symbolsStrength = Math.min(1, numSymbols / this.SYMBOLS_CNT);
	
	// 
	var totalStrength = (lowerStrength + upperStrength + numericStrength + symbolsStrength) / 4.0;
	
	return totalStrength;
}

PasswordStrengthValidator.prototype.validateStrength = function(pwd){
	var strengthLevel = 0;
	var pwdAndLoginSimilar = false;
	if(null != pwd && pwd.trim().length > 0){
		
		if(arguments.length > 1){
			var login = arguments[1];
			if(null != login && login.trim().length > 0){
				var similarityCode = this.checkLoginAndPwdSimilarity(login, pwd);
				if(similarityCode > 0){
					//login and password are too similar, no matter to check
					// password for its own strength:
					pwdAndLoginSimilar = true;
					strengthLevel = 1;
				}
			}
		}
		if(!pwdAndLoginSimilar){
			var strengthFactor = this.getCharCountBasedStrengthFactor(pwd);
			strengthLevel = Math.ceil(strengthFactor * 4);
			if(0 == strengthLevel){
				strengthLevel = 1;
			}
		}
	}
	
	for(var i=0; i<5 ;i++){ 
		//$(this.spanIdPrefix + i).style.display=((i == strengthLevel)? 'inline' : 'none' );
		if(pwdAndLoginSimilar){
			$(this.spanIdPrefix + i).style.display=( 'none' );
			if(1 == i){
				$(this.spanIdPrefix + i + 'lpm').style.display=( 'inline' );
			}
		} else {
			$(this.spanIdPrefix + i).style.display=((i == strengthLevel)? 'inline' : 'none' );
			if(1 == i){
				$(this.spanIdPrefix + i + 'lpm').style.display=( 'none' );
			}
		}
	}
}

PasswordStrengthValidator.prototype.checkLoginAndPwdSimilarity=function(login, pwd){
	if(!login || login.trim().length < 1 || !pwd || pwd.trim().length < 1){
		// pwd or login or both is either empty or null
		return 1;
	}
	if(this.loginAndPwdTrimmedEqual(login, pwd)){
		return 2;
	}
	if(pwd.length>3 && this.stringIs2ndBased(login, pwd)){
		return 3;
	}
	// try vice versa:
	/*if(this.stringIs2ndBased(pwd, login)){
		return 4;
	}*/
	// measure levenshtein distance
	var ld = this.levenshteinDistance(pwd.trim(), login.trim());
	if(ld < this.MIN_LEVENSHTEIN_DISTANCE){
	// distance is too small
		return 5;
	}
	// success!
	return 0;
}
PasswordStrengthValidator.prototype.loginAndPwdTrimmedEqual=function(login, pwd){
	return this.normalizeString(login) == this.normalizeString(pwd);
}
PasswordStrengthValidator.prototype.normalizeString=function(inputStr){
	return inputStr.trim().toLowerCase();
}
PasswordStrengthValidator.prototype.stringIs2ndBased=function(str, baseStr){
	str = this.normalizeString(str);
	baseStr = this.normalizeString(baseStr);
	return str.indexOf(baseStr) >= 0;
}
PasswordStrengthValidator.prototype.Minimum=function(a, b, c){
	var mi = a;
	if (b < mi)
		mi = b;
	if (c < mi)
		mi = c;
	return mi;
}
PasswordStrengthValidator.prototype.levenshteinDistance=function(s, t) {
	var d = new Array();
	var n; // length of s
	var m; // length of t
	var i; // iterates through s
	var j; // iterates through t
	var s_i; // ith character of s
	var t_j; // jth character of t
	var cost; // cost

	// Step 1
	n = s.length;
	m = t.length;
	if (n == 0) {
		return m;
	}
	if (m == 0) {
		return n;
	}

	for(i=0; i<=n; i++)
		d[i] = new Array();

	// Step 2
	for (i = 0; i <= n; i++) {
		d[i][0] = i;
	}

	for (j = 0; j <= m; j++) {
		d[0][j] = j;
	}

	// Step 3
	for (i = 1; i <= n; i++) {
		s_i = s.charAt(i - 1);

		// Step 4
		for (j = 1; j <= m; j++) {

			t_j = t.charAt(j - 1);

			// Step 5
			if (s_i == t_j) {
				cost = 0;
			}
			else {
				cost = 1;
			}

			// Step 6
			d[i][j] = this.Minimum (d[i-1][j]+1, d[i][j-1]+1, d[i-1][j-1] + cost);
		}

	}

	// Step 7
	return d[n][m];
}
