/*
 * =============================================================
 *
 * 验证手机号码的组件。
 * 说明：前台通过发送手机短信验证码或语言验证码来验证用户手机号码的正确性。
 * 依赖：jquery.js、json2.min.js、lbui.core.js
 * TODO：当同一个DOM对象中存在多个phoneValidator的封装时，各个实例之间
 * 			的隔离和共享。比如tips
 * ============================================================
 */
;
(function() {
	'use strict';

	/*
	 * ============================================================
	 * 修改
	 * ============================================================
	 */

	// 各种视图模板。先这样写到代码里面。
	var views = {

		// 发短信之前的要求输入图形验证码的弹窗。
		// 注意：HTML中通过元素class属性值中的“pv-”前缀来限定这是PhoneValidator模块用到的，防止污染其他上下文。
		dlgCaptcha: ['<div class="pv-dlg-captcha">',
			'<p class="tips-for-voice">点击确认按钮后我们将通过区号为010开头的固定电话，给您的手机拨打电话，自动播报您的验证码，请注意接听。</p>',
			'<p>请输入图片中的字符，以免费获取验证码：</p>',
			'<div class="pv-captcha">',
			'<input id="pv-txt-captcha" type="text" maxlength="4" size="4" require="not" />',
			'<img  src="/img_valid.jsp?param=" >', //点击后也需要同样刷新
			'<a href="#" >看不清换一张</a>', //点击后刷新图形验证码图片。
			'<b></b>',
			'</div>',
			'<p class="tips-for-captcha">请输入验证码</p>',
			'<div class="buttons">',
			'<a class="btn-ok" href="###">确认</a>',
			'<a class="btn-cancel" href="###">取消</a>',
			'</div>',
			'</div>'
		].join(''),
		// 提示浮层的模板
		tips: ['<div class="pv-tips" style="padding:10px">',
			'<div class="body"></div>',
			'<div class="arrow"><em></em><span style="width: 0px;"></span></div>',
			'</div>'
		].join('')

	};


	// 构造方法。
	// 注意：element只能是“发送短信验证码”的按钮元素的ID或jquery对象。
	var PhoneValidator = function(element, options) {

		/*******************************************************************
		 * 初始化
		 ******************************************************************/


		// 需要绑定的元素，即发送短信验证码的按钮
		this._$element = $(element);

		// 以下可能不需要
		//获得需要包装的元素的编号
		this._id = this._$element.attr('id');
		//如果该元素没有id属性值
		if ($.isEmpty(this._id)) {
			this._id = makeID();
			this._$element.attr('id', this._id);
		}

		// 创建一个随机的元素编号
		function makeID() {
			return "pv-" + (' ' + Math.random()).replace(/\D/g, "");
		}

		// -------------{ 初始化内部变量 }-------------------

		// 配置
		this._options = $.extend({}, $.fn.phoneValidator.defaults, options);
		// 是否正在倒数计时
		this._options.isCountdown = false;
		// 是否被禁用了
		this._options.enabled = true;
		// 当前否正在使用语音短信？
		// 初始状态时，需要根据这两个配置属性来判断。
		this._options.isVoice = this._options.onlyVoiceCode && this._options.allowVoiceCode;
		// 文字
		this._$element.text(this._options.isVoice ? '发送语音验证码' : '发送短信验证码');

		// 已经发送的次数
		this._options.attempts = {
			sms: 0, //短信
			voice: 0 //语音
		};
		// 当前对象指针
		var self = this;

		// -------------{ 初始化图形验证码对话框 }-------------------

		// 图形验证码确认对话框
		this._$dlgCaptcha = $(views.dlgCaptcha).hide();
		// 插入到element所在父元素的底部
		$(this._$element).parent().eq(0).append(this._$dlgCaptcha);

		// 绑定刷新验证码事件
		$('.pv-captcha img , .pv-captcha a', this._$dlgCaptcha).click(function() {
			self._refreshCaptcha();
			return false;
		});

		// 隐藏/关闭对话框
		$('.btn-cancel', this._$dlgCaptcha).click(function() {
			self._hideCaptchaDialog();
			return false;
		});

		// 绑定图形验证码输入框
		// $('#pv-txt-captcha', this._$dlgCaptcha).blur(checkCaptcha);

		// 确认按钮。
		$('.btn-ok', this._$dlgCaptcha).click(function() {

			var captcha = $('#pv-txt-captcha', self._$dlgCaptcha).val();
			// 校验图形验证码
			LBUI.validator.checkCaptcha(function(success, msg) {
					success ? self.sendPhoneValidateCode(captcha) : showCaptchaError(msg);
				},
				captcha,
				self._options.captchaActionCode);
			return false;
		});

		// 显示验证输入错误的提示信息
		function showCaptchaError(msg) {
			// 重新显示个新的图形验证码
			self._refreshCaptcha(true);
			// 否则显示输入错误提示信息
			// 添加error样式
			$('.pv-captcha', self._$dlgCaptcha).addClass('error');
			// 隐藏输入提示
			$('.pv-captcha b', self._$dlgCaptcha).show();
			// 清空输入框并聚焦
			$('.pv-captcha input', self._$dlgCaptcha).val('').focus();
			// 清空图形验证码校验提示文字
			$('.tips-for-captcha', self._$dlgCaptcha).html(msg || '');

		}


		// -------------{ 初始化提示框 }-------------------

		// 提示层
		this._$tips = $(views.tips).hide();
		// 插入到element所在父元素的底部
		$(this._$element).parent().eq(0).append(this._$tips);

		var self = this;

		// --------------{ 初始化手机验证码输入框 }------------------
		// 如果提供了有效的验证码输入框
		if (!$.isEmpty(this._options.phoneCodeField)) {
			this._$phoneCodeField = $(this._options.phoneCodeField).addClass('pv-txt-code');
			if ($.isFunction(this._options.phoneCodeValidateCallback)) {
				this._$phoneCodeField.blur(function() {
					// 失去焦点的时候检查手机验证码是否正确
					LBUI.validator.checkPhoneCode(
						self._options.phoneCodeValidateCallback,
						$(this).val(),
						self._$phoneNumberInput ? self._$phoneNumberInput.val() : '',
						$.isEmpty(self._options.userIDField) ? '' : $(self._options.userIDField).val(),
						self._options.isVoice ? 1 : 0,
						self._options.actionCode
					);
				});
			}
		}

		// -------------{ 绑定电话号码输入框 }-------------------

		// 如果输入的不是有效的手机号码，则禁用发送短信按钮
		function checkPhoneInput() {
			LBUI.utils.checkPhoneNumber(self._$phoneNumberInput.val()) ? self.enable() : self.disable();
		};
		// 开始绑定
		if (!$.isEmpty(this._options.phoneNumberInput)) {
			this._$phoneNumberInput = $(this._options.phoneNumberInput).keyup(checkPhoneInput).blur(checkPhoneInput);
			// 为手机号码输入框加入样式
			this._$phoneNumberInput.addClass('pv-txt-phone');

			// 调用一次
			checkPhoneInput();

		} else {
			// 启用
			this.enable();
		}


		// ------------{END: 初始化} --------------------

	};

	/*
	 * =============================================================
	 * 手机号码验证器的原型
	 * ============================================================
	 */
	PhoneValidator.prototype = {

		/*******************************************************************
		 * 构造方法
		 ******************************************************************/
		constructor: PhoneValidator,

		/*******************************************************************
		 * 下划线开头的都是私有方法（先这么简单处理，以后再封装）,请勿直接调用.
		 ******************************************************************/
		//初始化图形验证码确认对话框。
		_initCaptchaDialog: function() {
			// 绑定对话框相关事件

		},
		// 刷新图形验证码
		_refreshCaptcha: function(onlyCaptcha) {

			if (!onlyCaptcha) {
				// 移除error样式
				$('.pv-captcha', this._$dlgCaptcha).removeClass('error');
				// 隐藏输入提示
				$('.pv-captcha b', this._$dlgCaptcha).hide();
				// 清空图形验证码校验提示文字
				$('.tips-for-captcha', this._$dlgCaptcha).empty();
			}

			// 清空输入框并聚焦
			$('.pv-captcha input', this._$dlgCaptcha).val('').focus();

			// 刷新图片
			$('.pv-captcha img', this._$dlgCaptcha).attr('src', '/img_valid.jsp?param=' + new Date().getTime());
			// 启用确认按钮。
			return this;
		},
		// 检查最大发送次数
		_checkMaxAttempts: function() {
			// 检查是否已经达到发送上线

			if (this._options.attempts['voice'] >= this._options.maxAttempts['voice']) {
				this._options.enabled = false;
			}
			if (this._options.attempts['sms'] >= this._options.maxAttempts['sms']) {
				if (!this._options.allowVoiceCode) {
					this._options.enabled = false;
				}
			}

			// 如果还可以发。
			return true;
		},
		// 显示对话框
		_showCaptchaDialog: function() {

			this._checkMaxAttempts();

			// 如果已经被禁用了或者正在倒计时
			if (!this._options.enabled || this._options.isCountdown) {
				return this;
			};

			// 先隐藏tips
			this._hideTips();

			// 如果未启用图形验证码功能
			if (!this._options.allowCaptcha) {
				// 则直接发送
				this.sendPhoneValidateCode('');
				return this;
			};

			// 如果是使用语音验证码，则显示对应的提示信息。
			(this._options.isVoice || this._options.change2Voice) ? $('.tips-for-voice', this._$dlgCaptcha).show() : $('.tips-for-voice', this._$dlgCaptcha).hide();

			// 隐藏提示框

			// 设置新的图形验证码
			this._refreshCaptcha();
			// 聚焦输入框

			// 关闭对话框的时候需要enbaled电话号码输入框。
			if (this._$phoneNumberInput) {
				// 添加只读属性
				this._$phoneNumberInput.attr('disabled', 'disabled');
				this._$phoneNumberInput.attr('onselectstart', 'javascript:return false;');
				this._$phoneNumberInput.addClass('disabled');
			}

			// 提示曾的偏移
			var offset = this._$element.position();
			var dlgOffset = this._options.dlgCaptchaOffset || {
				top: 0,
				left: 0
			};
			dlgOffset.left = (dlgOffset.left || 0) + offset.left;
			dlgOffset.top = (dlgOffset.top || -20) + offset.top;

			// 显示
			this._$dlgCaptcha.css(dlgOffset).show();
			$('.pv-captcha input', this._$dlgCaptcha).focus();


			return this;
		},
		// 关闭图形验证码输入框
		_hideCaptchaDialog: function() {
			// 关闭对话框的时候需要enbaled电话号码输入框。
			if (this._$phoneNumberInput) {
				// 移除只读属性
				this._$phoneNumberInput.removeAttr('disabled');
				this._$phoneNumberInput.removeAttr('onselectstart');
				this._$phoneNumberInput.removeClass('disabled');
			}
			// 隐藏窗体；
			this._$dlgCaptcha.hide();

			return this;
		},
		// 显示提示信息浮层
		_showTips: function(content) {
			// @param content Object ：内容，可以是文本或jQuery对象。

			// 设置内容，如果指定了有效的conteng参数。否则body区域保持上次的内容。
			if (content) $('div.body', this._$tips).empty().append(content);


			// 提示层的偏移；
			var offset = this._$element.position();
			var tipsOffset = this._options.tipsOffset || {
				left: 0,
				top: 0
			};
			tipsOffset.left = (tipsOffset.left || 0) + offset.left;
			tipsOffset.top = (tipsOffset.top || -this._$tips.height() - 35) + offset.top;

			// 显示
			this._$tips.css(tipsOffset).fadeIn();

			return this;

		},
		// 隐藏tips浮层
		_hideTips: function(stay) {
			// 隐藏
			this._$tips.hide();

			// 保留内容，否则清空。
			!stay && $('div.body', this._$tips).empty();

			return this;
		},
		// 倒计时
		countdown: function() {

			// 倒计时过程中，按钮不可用，当倒计时结束后，继续启用按钮。
			this._options.isCountdown = true;
			this._checkMaxAttempts();


			// 先禁用
			this.disable();
			if (!this._options.enabled) {
				return this;
			};
			// 倒计时总时长(秒)。
			var time = this._options.attemptInterval || 60;
			var t = 0;
			var self = this;

			function clock() {
				t += 1;
				if (t >= time) {
					self._options.isCountdown = false;
					// 修改文字。
					// self._$element.text('重新发送');
					// 启用
					self.enable();
				} else {
					self._options.isCountdown = true;
					self._$element.text('重发(' + (time - t) + ')');
					setTimeout(clock, 1000);
				}

			}
			clock();

			return this;
		},
		// 获得验证码码
		sendPhoneValidateCode: function(captcha) {
			// @param captcha string ：图形验证码

			// 隐藏	
			this._hideCaptchaDialog();
			this._hideTips();

			// ------------------{ 在发送之前的一些检查 }--------------

			this._checkMaxAttempts();


			// 如果已经被禁用了。
			if (!this._options.enabled) {
				// 先禁用
				this.disable();
				return this;
			}


			// 从输入控件中/设置中获得手机号码
			var phone = '';

			// 图形验证码
			captcha = captcha || '';
			// 开始发送验证码
			var self = this;

			// 如果指定了手机号码输入框控件，则直接从空间中取值
			if (this._$phoneNumberInput || this._options.phoneNumber) {
				phone = this._$phoneNumberInput ? this._$phoneNumberInput.val() : this._options.phoneNumber;
				if (!LBUI.utils.checkPhoneNumber(phone)) {
					// 显示错误提示
					this._showTips(LBUI.constans.SEND_PHONE_CODE_RESULT['E1000']);
					return this;
				}
			}
			// 如果是刚切换到语音验证码状态的，那么直接标识语音状态。
			if (!self._options.isVoice && self._options.change2Voice) {
				self._options.isVoice = true;
				self._options.change2Voice = false;
			}

			// 调用发送短信的接口
			LBUI.service.Validator.sendPhoneCode(
				this._options.isVoice ? 1 : 0,
				phone,
				this._options.actionCode,
				captcha,
				this._options.phoneCodeData || ''
			).done(function(rst) {//DEBUG：测试用，正式发布时，请将fail修改done；

				// 如果发送成功
				if (rst && rst.errno == 0) {
					// 发送次数+1
					self._options.attempts[self._options.isVoice ? 'voice' : 'sms'] += 1;

					// 开始倒计时
					self.countdown();

					var tips = LBUI.constans.SEND_PHONE_CODE_RESULT[self._options.isVoice ? 'M0002' : 'M0001'];



					// TODO:如果超过了最大尝试次数

					// 如果超过了最大尝试次数
					if (self._options.attempts[self._options.isVoice ? 'voice' : 'sms'] >= self._options.maxAttempts[self._options.isVoice ? 'voice' : 'sms']) {
						// 提示信息
						var msg = LBUI.constans.SEND_PHONE_CODE_RESULT['M0003'] || '';
						msg = msg.replace('Y', self._options.isVoice ? '语音' : '短信');
						msg = msg.replace('X', self._options.maxAttempts[self._options.isVoice ? 'voice' : 'sms']);

						if (self._options.isVoice || (!self._options.allowVoiceCode)) {
							// 已经尝试到最大次数了，则禁用。
							tips += msg;
						}

					}

					if (!self._options.isVoice && self._options.allowVoiceCode && self._options.attempts.sms >= self._options.showVoiceAttemts) {
						tips += LBUI.constans.SEND_PHONE_CODE_RESULT['M0004'];
					}

					if (self._options.attempts['voice'] >= self._options.maxAttempts['voice']) {
						self._options.enabled = false;
					}

					if (self._options.attempts['sms'] >= self._options.maxAttempts['sms']) {
						if (!self._options.allowVoiceCode) {
							self._options.enabled = false;
						} else {
							// 切换到语音模式
							self._options.isVoice = true;
						}
					}

					self._showTips(tips);
					$('a.pv-btn-voice', self._$tips).click(function() {
						// 切换到语音模式
						self._options.change2Voice = true;
						self._showCaptchaDialog();
						return false;
					});

					// 记录userID
					if (self._options.userIDField && rst.data && rst.data.userID) {
						// 在注册页面需要该用户编号字段。
						// 注意：服务器端要再次检查！最好放入session
						$(self._options.userIDField).val(rst.data.userID);
					}
					// 聚焦手机验证码输入框
					if (self._$phoneCodeField) {
						self._$phoneCodeField.focus();
					}

				} else {
					// 发送失败
					self._showTips(LBUI.constans.SEND_PHONE_CODE_RESULT['E' + rst.errno] || LBUI.constans.SEND_PHONE_CODE_RESULT['E1010']);
				}

			}).fail(function(obj) {//DEBUG：测试用，正式发布时，请将done修改fail；

				self._showTips(LBUI.constans.SEND_PHONE_CODE_RESULT['E1010']);

			});


			return this;
		},
		// 禁用当前元素
		disable: function() {
			// 取消点击事件
			this._$element.unbind('click');
			// 样式改为禁用
			this._$element.attr('disabled', 'disabled');

			return this;
		},
		// 启用当前元素
		enable: function() {
			// 如果当前已经禁用了（比如过了最大发送次数了）或正在倒计时则不能再启用；
			if (this._options.isCountdown) {
				return this;
			}

			this._checkMaxAttempts();

			if (!this._options.enabled) {
				this.disable();
				return this;
			}

			// 按钮文本和tooltip
			var txt = '发送短信验证码';
			if (this._options.isVoice) {
				txt = this._options.attempts.voice > 0 ? '重发语音验证码' : '发送语音验证码';
			} else {
				txt = this._options.attempts.sms > 0 ? '重发短信验证码' : '发送短信验证码';
			}

			this._$element.text(txt).attr('title', '免费获取手机验证码');

			var self = this;
			this._$element.unbind('click');
			// 绑定点击事件
			this._$element.bind('click', function() {
				self._showCaptchaDialog();
				return false;
			});
			// 样式改为启用
			this._$element.removeAttr('disabled');

			return this;
		}

	};

	/*
	 * =============================================================
	 * jQuery插件
	 * ============================================================
	 */
	$.fn.phoneValidator = function(options) {

		// NOTE:
		// 1.只会对1个jQuery对象进行包装
		// 2.如果jQuery对象为空,则什么也不做.
		// WARNING:
		// 1、jQuery对象只能是“发送短信验证码”的按钮元素，最好是button，其他元素没有测试。
		// 2、请勿将多个jQuery对象包装秤phoneValidator。

		if (this.length == 0) {
			return;
		}

		// 只包装第一个
		var $this = this.eq(0);

		var data = $this.data('phone_validator');

		var option = typeof options == 'object' && options;

		// 将实例关联到当前jQuery对象
		if (!data) {
			$this.data('phone_validator', (data = new PhoneValidator(this, option)));
		}

		// 下面的暂时用不着。
		// 如果传入的是字符串,在作为方法的调用
		if (typeof options == 'string') {
			var arg = null;
			if (arguments.length > 1) {
				// 如果多余一个参数
				// NOTE:
				// 所有实例方法最多只需要1个参数,因此....你懂得.....
				arg = arguments[1];
			}

			// 注意,这里直接返回方法的执行结果.
			// 而非当前的jquery对象.
			return data[options](arg);
		}

		// 这里需要这样做么?
		// 是否破坏了方法调用的一致性?

		return data;

	};

	/*
	 * =============================================================
	 * 全局的配置，可以在初始化的通过options覆盖。
	 * ============================================================
	 */
	$.fn.phoneValidator.defaults = {
		// 手机号码输入框元素ID或jQuery对象。
		// 注意：如果是元素ID，必须加上#，如：“#txt-phone”
		phoneNumberInput: '',
		// 手机号码。
		// 如果phoneNumberInput对象未指定，则判断该属性值。否则忽略该属性值；
		phoneNumber: '',
		// 是否启用语音验证码功能；
		// 默认为启用。
		allowVoiceCode: true,
		// 当用户点击几次重发短信验证码按钮后就开始显示语音验证码的提示；
		// 0：表示点击“发送短信”按钮就显示；1：表示再点一次后才显示；最大不会超过maxAttempts.sms-1次
		showVoiceAttemts: 1,
		// 是否仅有/直接使用语音短信的功能？
		// 必须allowVoiceCode=true时，该属性才启用。
		onlyVoiceCode: false,
		// 每次尝试之间的间隔。语音和短信是一样的。
		// 单位为秒。
		attemptInterval: 60,
		// 是否启用图形验证码
		allowCaptcha: true,
		// 图形验证码的动作代码。每隔action之间的验证码不同通用。
		captchaActionCode: '',
		// 需要验证手机的动作代码。比如注册、下单等。
		// 每个action之间的验证码应该是不能通用的！！！
		actionCode: '',

		// 最大尝试次数
		// 这里只是客户端UI上的限制，在服务器端接口内部逻辑还是需要限制的
		maxAttempts: {
			sms: 3, //短信
			voice: 3 //语音
		},
		// 用于保存USERID值的隐藏域的ID或jQuery对象；
		// 如果是ID，则必须加上#，如：“#userID”
		// 该属性一般用于注册页面
		userIDField: false,
		// 用来输入手机验证码的输入控件的ID或jQueyr对象；
		// 如果是ID，则必须加上#，如：#phoneCode；
		// 如果为该属性指定了有效值，验证短信/语言发送成功后，则会自动聚焦并自动校验该控件的输入
		phoneCodeField: false,
		// 手机验证码校验后的回调
		phoneCodeValidateCallback: false,
		// 提示层相对于包装对象的偏移位置；
		// 该属性值结构如：{left:100,top:100}
		// 如果该属性值未设置，或其中的left/top未设置，则采用内部默认值。
		tipsOffset: false,

		// 图形验证码相对于包装对象左上角坐标的偏移位置；
		// 该属性结构如{left:100,top:100}，分别指定左上角left和top的偏移
		dlgCaptchaOffset: false,
		// 发送收件验证码的额外参数。可选。
		phoneCodeData: ''


	};

})();