'use strict';
/**
    数据管理模块
    
    Feartures : 
        1. dataServices :数据服务接口
        2. dataManager :基于策略的数据管理基类
        3. dataPolicyManager :数据策略管理器;

    Update Note:
        + 2014.7 :Created

    @module DataManager
*/
_stDefine('dataManager', function(st) {

	var dataManager, policyManager, dataServices,
		_config = {
			ignoreMerges: ["params", "filter", "_filterBuilder"],
			dmOp: {
				set: {},
				get: {}
			}
		},
		defFilterBuilder = st.filterBuilder(),
		promiseEvent = st.promiseEvent,
		isFunction = st.isFunction;

	/**
	   数据服务管理;定义了数据服务的接口和通用操作方法;不能直接使用,必须创建具体类型的数据服务; 
	   数据服务的定义就比较广了,可以是具体的对象方式locaStorage,IndexDB,或者是一些行为ajax,comet,websocket;也可以是根据业务规则定义的rest,cache等;
	   @class dataServices
	   @constructor
	   @extends factory
       @demo test/dataManager/demo.js [dataServices]
	 */
	dataServices = st.factory({
		name: "dataServices",
		proto: {
			/**
			 * 数据服务通用操作方法;直接执行到具体的数据服务的方法上
			 * @method  operate
			 * @param  {string} type 操作类型;1. search; 2. update
			 * @param  {object} op   参数;具体参数同数据服务
			 * @param  {object} op.dsType   数据服务类型
			 * @return {object|promise}      操作结果或者promise
			 */
			operate: function(type, op) {
				var ds = this.find(op.dsType);
				if (ds) {
					if (type !== 'initOptions') {
						ds.initOptions(op);
					}
					return ds[type](op);
				} else
					throw op.dsType + ",not defined in dataServices!";
			},
			/**
			 * 执行数据服务search操作方法
			 * @method  search
			 * @param  {object} op   参数;具体参数同数据服务
			 * @param  {object} op.dsType   数据服务类型
			 * @return {object}      操作结果
			 */
			search: function(op) {
				return this.operate('search', op);
			},
			/**
			 * 执行数据服务update操作方法
			 * @method  update
			 * @param  {object} op   参数;具体参数同数据服务
			 * @param  {object} op.dsType   数据服务类型
			 * @return {object}      操作结果
			 */
			update: function(op) {
				return this.operate('update', op);
			}
		},
		/**
		 * 数据服务基类
		 * @class baseDataService
		 */
		base: {
			/**
			 * 查询操作接口  **[接口方法]**
			 * @method  search
			 * @param  {object} op   参数;其他具体参数同见具体数据服务
			 *    @param  {object} op.filter   过滤器
			 *    @param  {object} op.success   成功之后执行的方法
			 *    @param  {object} op.error   失败之后执行的方法
			 * @return {object}      操作结果
			 */
			search: function(op) {},
			/**
			 * 更新操作接口 **[接口方法]**
			 * @method  update
			 * @param  {object} op   参数;其他具体参数同见具体数据服务
			 *    @param  {object} op.filter   过滤器
			 *    @param  {object} op.data   更新数据
			 *    @param  {object} op.success   成功之后执行的方法
			 *    @param  {object} op.error   失败之后执行的方法
			 * @return {object}      操作结果
			 */
			update: function(op) {},
			/**
			 * 通用初始化参数接口 **[接口方法]**
			 * @method  initOptions
			 * @param  {object} op   参数;其他具体参数同见具体数据服务
			 *    @param  {object} op.filter   过滤器
			 *    @param  {object} op.success   成功之后执行的方法
			 *    @param  {object} op.error   失败之后执行的方法
			 * @return {object}      参数
			 */
			initOptions: function(op) {}
		}
	})

	/**
	   数据管理器工厂; 更多数据管理的例子见smartjs其他数据管理项
	   @class dataManager
	   @constructor
	   @extends factory
       @demo test/dataManager/demo.js [dataManager]

	 */
	dataManager = st.factory({
		name: "dataManager",
		type: "class",
		proto: {
			/**
			 * 创建数据管理器
			 * @method create
			 * @param  {string} type 数据管理器类型
			 * @param  {object} op   数据管理参数设置
			 * @return {dataManager}     数据管理对象
			 */
			create: function(type, op) {
				var dm = this.find(type);
				if (dm)
					return new dm(op);
				else
					console.log(type + ",not defined in dataManager");
			}
		},
		/**
		 * 数据管理器基类
		 * @class baseDataManager
		 */
		base: {
			/**
			 * 是否过滤模式; true时,使用filterBuilder组织过滤
			 * @type {Boolean} _filterMode
			 * @default true
			 */
			_filterMode: true,
			//_operations : ["get","set"],
			/**
			 * 数据管理对象的类初始化方法;
			 * @method klassInit
			 * @final
			 * @param op {object}  数据管理设置参数
			 * @return {dataManager}   初始化完成的数据管理对象
			 */
			klassInit: function(op) {
				var dm = st.attachTrigger(this);

				op = dm.op = st.mix(op, _config.dmOp);

				initPolicy(dm, op.get, 'get');
				initPolicy(dm, op.set, 'set');

				initFlow(dm);
				policyManager.applyPolicy(dm, dm._Flow, op);
				this.init(op);
			},
			/**
			 * 数据管理对象的初始化接口方法 **[接口方法]**
			 * @method init
			 * @param  op {object} 数据管理设置参数
			 */
			init: function(op) {},
			/**
			 * 使用dataManager的数据通道进行获取数据
			 * @method get
			 * @param  conf {object} 获取设置参数
			 * @return {object|promise}   查询结果或者promise
			 */
			get: function(conf) {
				var dm = this;
				conf = initConf(dm, conf);
				return whenFlow(dm._Flow.boot(dm, dm.op, conf.policy), conf.success, conf.error);
			},
			/**
			 * 使用dataManager的数据通道进行设置数据
			 * @method set
			 * @param  conf {object} 设置参数
			 * @return {object|promise}   设置结果或者promise
			 */
			set: function(conf) {
				var dm = this;
				conf = initConf(dm, conf);
				return whenFlow(dm._Flow.bootWithStart("setData", [dm, dm.op, conf.policy]), conf.success, conf.error);
			},
			/**
			 * 使用dataManager内置查询(即只在dataManager内部查询,不查询dataService)接口. **[接口方法]**
			 * @method _innerSearch
			 * @param  conf {object} 获取设置参数
			 * @return {object}   查询结果
			 */
			_innerSearch: function(conf) {

			},
			/**
			 * 使用dataManager内置更新(即只在dataManager内部更新,不更新到dataService)接口. **[接口方法]**
			 * @method _innerUpdate
			 * @param  conf {object} 设置参数
			 * @return {object}   设置结果
			 */
			_innerUpdate: function(conf) {

			},
			/**
			 * 检查数据是否为空;数据策略的判断空数据会根据此方法的结果来判断;不同类型的数据管理的判断也不同。
			 * 如:object判断是否为undefined;table判断数据的长度是否大于0
			 * @method checkEmpty
			 * @param  data {object} 检查的数据
			 * @param  conf {object} 设置参数
			 * @return {[type]}  判断是否为空
			 */
			checkEmpty: function(data, conf) {
				return data === undefined;
			},
			//验证方法
			validate: function() {

			},
			/**
			 * 清空数据管理内的数据的方法. **[接口方法]**
			 * @method clear
			 */
			clear: function() {
			},
			/**
			 * 设置dataService的参数,在每次使用数据通道时执行. **[接口方法]**
			 * @method setDataSerive
			 * @param config {object} 设置dataService的参数
			 */
			setDataSerive: function(config) {},
			/**
			 * 初始化策略参数
			 * @method initPolicy
			 * @param  policy {object} 策略设置
			 * @param  type  {type}  操作类型. 
			 *  1. get; 
			 *  2. set;
			 */
			initPolicy: function(policy, type) {
				if (this._filterMode) {
					policy._filterBuilder = policy.filter ? st.filterBuilder(policy.filter) : defFilterBuilder;
				}
			},
			/**
			 * 生成传递的参数
			 * @method buildParam
			 * @param  policy {object}    策略设置
			 * @param  defPolicy {object} 默认的策略设置
			 */
			buildParams: function(policy, defPolicy) {
				buildParams(this, policy, defPolicy);
			},
			/**
			 * 生成策略,对策略参数进行初始化,生成传递参数,合并参数
			 * @method  buildPolicy
			 * @param  policy {object}    策略设置
			 * @param  defPolicy {object}  默认的策略设置
			 */
			buildPolicy: function(policy, defPolicy) {
				this.buildParams(policy, defPolicy)
				st.mix(policy, defPolicy, _config.ignoreMerges);
			}
		}
	});

	function initFlow(dm) {
		dm._Flow = st.flowController({
			flow: {
				buildGetPolicy: function(e, dm, op, policy, isTrigger) {
					//合并策略
					dm.buildPolicy(policy, op.get);
				},
				getData: function(e, dm, op, policy, isTrigger) {
					var result = searchDM(dm, policy);
					e.__getDone = true;
					if (checkEmpty(dm, result, policy)) {
						e.next("getFromDs");
					} else {
						e.next("getFromDm");
					}
					return result;
				},
				getFromDm: function(e, dm, op, policy, isTrigger) {
					var result = e.__getDone ? e.result : searchDM(dm, policy);
					if (!policy.update)
						e.end();

					return result;
				},
				getFromDs: function(e, dm, op, policy, isTrigger) {
					var success, ds = getDs(policy, op);
					if (ds) {
						success = function(result) {
							if (policy.update !== false) {
								dm.set(buildGetSetPolicy(dm, result, policy,
									function(result) {
										e.end().resolve(result);
									}, e.reject));

							} else {
								e.end().resolve(result);
							}
						}

						openDatatransfer('search', ds, dm, policy, success, e.reject);
						return e.promise();
					} else {
						e.end().resolve(searchDM(dm, policy));
					}
				},
				setData: function(e, dm, op, policy, isTrigger) {
					//合并策略
					dm.buildPolicy(policy, op.set);
					e.next(policy.way === 'ds' ? 'setToDs' : 'setToDm');
				},
				setToDm : function(e, dm, op, policy, isTrigger){
					if(policy.way !== 'dm')
						e.next('setToDs');
					return dm._innerUpdate(policy);;
				},
				setToDs: function(e, dm, op, policy, isTrigger) {
					var success, error, ds = getDs(policy, op),
						isPending = policy.pending !== false;

					if (ds) {
						if (isPending) {
							success = e.resolve;
							error = e.reject;
						} else {
							e.resolve(data);
						}

						openDatatransfer('update', ds, dm, policy, success, error);

						if (isPending)
							return e.promise();
					}
				}
			},
			order: ["buildGetPolicy", "getData", "setData"],
			trigger: true
		});
	}

	function initPolicy(dm, policy, type) {
		if (policy) {
			dm.initPolicy(policy, type);
			if (policy.get && (type === 'get' || type === 'trigger'))
				dm.initPolicy(policy.get, 'set');
		}
	}

	/*初始化dm的get,set配置*/
	function initConf(dm, conf) {
		if (!conf) {
			conf = {};
		}
		var success = conf.success,
			error = conf.error;

		conf.success = null;
		conf.error = null;

		return {
			policy: conf,
			success: success,
			error: error
		};
	}

	function checkEmpty(dm, data, policy) {
		return (dm.op.checkEmpty || dm.checkEmpty)(data, policy)
	}

	function whenFlow(fireResult, success, error) {
		var d = st.Deferred();
		st.when(fireResult).done(function(result) {
			success && success(result);
			d.resolve(result);
		}).fail(function(err) {
			error && error(err);
			d.resolve(err);
		})
		return d.promise();
	}

	function buildParams(dm, policy, mgPolicy) {
		if(policy._$builded)
			return;
		
		var mgParams, pType, params = policy.params;

		//条件参数处理
		if (isFunction(params)) {
			params = policy.params = params.apply(null, [dm, policy]);
		}

		if (mgPolicy && policy.mergeFilter !== false) {
			mgParams = mgPolicy.params;

			if (isFunction(mgParams)) {
				mgParams = mgParams.apply(null, [dm, policy]);
			}

			if (params) {
				pType = typeof params;
				if (pType === typeof mgParams && pType === 'object') {
					//合并条件参数
					st.mix(params, mgParams);
				}
			} else {
				policy.params = mgParams;
			}
		}
		if (dm._filterMode) {
			var filterBuilder = mgPolicy && mgPolicy._filterBuilder || defFilterBuilder;
			policy.filter = filterBuilder.buildFn(policy.params, policy.filter);
		}
		policy._$builded = true;
	}

	function buildGetSetPolicy(dm, data, policy, success, error) {
		var setPolicy = {
			data: data,
			filter: policy.filter,
			params: policy.params,
			way: 'dm',
			pending: false,
			success: success,
			error: error
		};

		if (policy.set) {
			dm.buildPolicy(policy.set, setPolicy);
			return policy.set
		}
		return setPolicy;
	}

	function searchDM(dm, policy) {
		return dm._innerSearch(policy);
	}

	function getDs() {
		var args = arguments,
			len = args.length,
			i = 0,
			ds,arg;

		for (; i < len; i++) {
			if ((arg = args[i]) && (ds = arg.dataServices))
				return ds;
		};
	}

	/*开启数据传输*/
	function openDatatransfer(type, ds, dm, policy, success, error) {
		var dsOp, fnDsQueue, i = 0;

		function buildDsOp(op) {
			var conf = st.mergeMulti(true,[{},op, policy]);
			conf.success = success;
			conf.error = error;
			dm.setDataSerive(conf);
			return conf;
		}

		if (st.isArray(ds)) {
			fnDsQueue = function() {
				if (dsOp = ds[i++]) {
					dsOp = buildDsOp(dsOp);
					dsOp.success = function(result) {
						checkEmpty(dm, result, policy) ? fnDsQueue() : success(result);
					}
					dataServices.operate(type, dsOp);
				} else
					success(data);
			}
			fnDsQueue();
		} else
			dataServices.operate(type, buildDsOp(ds));
	}

	//策略管理器
	policyManager = st.factory({
		name: "DataPolicyManager",
		type: 'copy',
		proto: {
			applyPolicy: function(dm, flow, op) {
				this.fire('init', [dm, flow, op]);
			}
		},
		base: {
			init: function(dm, flow, op) {

			}
		}
	});

	policyManager.add("getWay", {
		init: function(dm, flow, op) {
			flow.onBefore("getData", "checkGetWay", function(e, dm, op, policy) {
				var way = policy.way,
					node;
				if (way) {
					if (way === 'ds') {
						node = 'getFromDs';
					} else if (way === 'dm') {
						node = 'getFromDm';
					}
					node && e.next(node).stop();
				}
			})

		}
	});

	/*判断并设置定时器*/
	function checkTimer(id, timer, fn, dm) {
		if (!timer)
			return fn;

		var timers = dm.__timers;
		if (!__timers) {
			timers = dm.__timers = {};
			dm.stopTimer = function(id) {
				var ts = this.__timers,
					no;
				if (st.isEmptyObject(ts))
					return;

				if (id) {
					if (no = ts[id]) {
						ts[id] = null;
						clearInterval(no);
					}
				} else {
					st.each(ts, function(i, no) {
						no && clearInterval(no);
					});
					this.__timers = {};
				}

			}
		}
		return function() {
			timers[id] = setInterval(fn, timer)
		}
	}

	/*解析Trigger*/
	function compileTrigger(i, conf, dm, flow, op) {
		var flowNode, fnRemove, conf = initConf(dm,conf),
			trPolicy = conf.policy,
			isDef = trPolicy.def,
			setPolicy = trPolicy.set,
			pos = trPolicy.position,
			delay = trPolicy.delay || 0,
			timer = trPolicy.timer,
			userfulLife = trPolicy.userfulLife;

		initPolicy(dm, trPolicy, 'trigger');

		//判断注入的流程节点
		flowNode = trPolicy.def ? "buildGetPolicy" : (pos === "get" ? "getData" : (pos === "dm" ? "getFromDm" : "getFromDs"));

		//注入Handler
		// success = st.mergeFn(trPolicy.success, function(result) {
		// 	dm.fireHandler("trigger", [result, trPolicy]);
		// });

		//有效期
		if (userfulLife) {
			if (userfulLife === "once") {
				fnRemove = function() {
					return true;
				}
			} else if (isFunction(userfulLife)) {
				fnRemove = userfulLife;
			}
		}

		flow.on(flowNode, "trigger", function(e, dm, op, policy, isTrigger) {
			var fnRequest, ds, _policy, fnSuccess;
			if (isTrigger)
				return;

			//默认时与get动作policy合并
			if (isDef) {
				dm.buildPolicy(policy, trPolicy);
				return;
			}

			_policy = st.mergeObj({
				mergeFilter: false,
				way: 'ds',
			}, trPolicy);

			//合并filter
			buildParams(dm, _policy, policy);

			fnRequest = function() {
				whenFlow(dm._Flow.bootWithStart("getData", [dm, dm.op, _policy, true]), conf.success, conf.error).always(function(result) {
					dm.fireHandler("trigger", [result, _policy]);
				});
			}

			setTimeout(checkTimer(i, timer, fnRequest, dm), delay);
		})
	}

	//添加触发器
	policyManager.add("trigger", {
		init: function(dm, flow, op) {
			var trigger = op.get && op.get.trigger;
			if (trigger) {

				st.each(trigger, function(i, trPolicy) {
					compileTrigger(i, trPolicy, dm, flow, op);
				});
				op.get.trigger = null;
			}
		}
	});

	return {
		dataManager: dataManager,
		dataPolicyManager: policyManager,
		dataServices: dataServices
	};
})
    
Top