var async = require('async');

var db = require('./db');
var Common = require('./common');
var User = require('./user');
var Tag = require('./tag');
var Tags = db.tags;
var Api = require('./api');
var Notebook = require('./notebook');
var Note = require('./note');
var Web = require('./web');

function log(o) {
	// console.log(o);
}

// 同步服务
/*
syncProgress 设置
两阶段, 各个50%
1. <-
	notebook 10 -> 10%
	note 30 -> 40%
	tag 10 -> 50%

2. ->
	notebook 10 -> 60%
	note 30 -> 90%
	tag 10 -> 100%
 */
var Sync = {
	// 同步的信息, 返回给调用者
	_syncInfo: {
		notebook: {changeAdds: [], changeConflicts: [], adds: [], deletes: [], updates: [], changeUpdates: []}, 
		note: {changeAdds: [], changeConflicts: [], adds: [], deletes: [], updates: [], conflicts: [], errors: []}, 
		tag: {}
	}, 
	/*
	_sendInfo: {
		notebook: {adds: [], deletes: [], updates: []}, 
		note: {adds: [], deletes: [], updates: [], conflicts: []}, 
		tag: {}
	}, 
	*/
	_needIncrSyncAgain: false,

	// notebook
	_syncNotebookIsLastChunk: false,
	_totalSyncNotebookNum: 0, // 需要同步的数量
	_tocalHasSyncNotebookNum: 0, // 已同步的数量
	_notebookMaxEntry: 200,

	// note
	_syncNoteIsLastChunk: false,
	_totalSyncNoteNum: 0, // 需要同步的数量
	_noteMaxEntry: 200,

	// tag
	_syncTagIsLastChunk: false,
	_totalSyncTagNum: 0, // 需要同步的数量
	_tagMaxEntry: 200,

	_initSyncInfo: function() { 
		var me = this;

		// notebook
		me._syncNotebookIsLastChunk = false;
		me._totalSyncNotebookNum = 0;
		me._totalHasSyncNotebookNum = 0;
		me._lockNotebook = 1;

		// note
		me._syncNoteIsLastChunk = false;
		me._totalSyncNoteNum = 0;
		me._totalHasSyncNoteNum = 0;
		me._lockNote = 1;

		// tag
		me._syncTagIsLastChunk = false;
		me._totalSyncTagNum = 0;
		me._totalHasSyncTagNum = 0;
		me._lockTag = 1;

		// 同步信息
		me._syncInfo = {
			notebook: {ok: false, changeAdds: [], changeConflicts: [], changeNeedAdds: [], adds: [], deletes: [], updates: [], changeUpdates: []}, 
			note: {ok: false, adds: [], changeAdds: [], changeConflicts: [], changeUpdates:[], changeNeedAdds: [], deletes: [], updates: [], conflicts: [], errors: []}, 
			tag: {ok: false, adds: [], changeAdds: [], changeConflicts: [], changeNeedAdds: [], deletes: [], updates: [], conflicts: []},
		};

		// 发送改变信息
		/*
		me._sendInfo = {
			notebook: {ok: false, adds: [], deletes: [], updates: []}, 
			note: {ok: false, adds: [], deletes: [], updates: [], conflicts: []}, 
			tag: {ok: false}
		};
		*/
		// 是否还要来一次增量同步 ?
		me._needIncrSyncAgain = false;
	},

	// 停止同步
	// 第一次，note.html -> login.html(使得_stop: true), -> note.html fullSync，一看，是stop
	_stop: false,
	stop: function() {
		var me = this;
		me._stop = true;
	},
	isStop: function() {
		var me = this;
		if(me._stop) {
			me._stop = false;
			return true;
		}
		return false;
	},

	//---------------
	// notebook
	//---------------
	
	// 增加, 有锁
	_lockNotebook: 1,
	_addSyncNotebookNum: function() {
		var me = this;
		if(me._lockNotebook) {
			me._lockNotebook = 0;
			me._totalHasSyncNotebookNum++;
			me._lockNotebook = 1;
		} else {
			me._addSyncNotebookNum();
		}
	},

	// 同步笔记本
	_syncNotebookToLocal: function(notebooks, callback) { 
		var me = this;

		function canCall() {
			// 是最后一块, 且
			me._addSyncNotebookNum();
			// console.log(me._syncNotebookIsLastChunk);
			// console.log(me._totalHasSyncNotebookNum + ' ' + me._totalSyncNotebookNum);
			// console.log(me._syncInfo.notebook.ok);
			if(me._syncNotebookIsLastChunk && 
				me._totalHasSyncNotebookNum >= me._totalSyncNotebookNum) {
				// 防止多次callback
				if(!me._syncInfo.notebook.ok) {
					me._syncInfo.notebook.ok = true;
					callback && callback(true);
				}
			}
		}

		if(!notebooks || notebooks.length == 0) {
			return canCall();
		}

		for(var i in notebooks) {
			var notebook = notebooks[i];
			// 得到本地的, 与之对比

			Web.syncProcess('notebook', notebook.Title);

			(function(notebook) {

				var usn = notebook.Usn;
				var notebookId = notebook.NotebookId; // 服务器端的

				// 1) 服务器端删除了, 本地肯定删除
				if(notebook.IsDeleted) {
					// console.log('delete: ');
					// console.log(notebook);
					Notebook.getNotebookIdByServerNotebookId(notebookId, function(localNotebookId) {
						Notebook.deleteNotebookForce(notebookId, function() { 
							// me._syncInfo.notebook.deletes.push(localNotebookId);
							me.fixSynced('notebook', 'deletes', localNotebookId);
							canCall();
						})
					});
					return;
				}
				// 2) 查看本地的, 与本地合并
				Notebook.getNotebookByServerNotebookId(notebookId, function(notebookLocal) {
					// 2.1 本地没有, 表示是新建
					if(!notebookLocal) {
						console.log('	add addNotebookForce...', notebook.Title)
						// TODO
						Notebook.addNotebookForce(notebook, function(notebook) {
							// 要最后一起添加, 因为有层级关系
							console.log('	', notebook.Title)
							me._syncInfo.notebook.adds.push(notebook);
							// me.fixSynced('notebook', 'adds', notebook);
							canCall();
						});
					} else {
						// 如果Usn一样, 表示服务器端并没有修改
						if(notebookLocal.Usn === notebook.Usn) {
							console.log('notebookLocal 如果Usn一样, 表示服务器端并没有修改');
							return canCall();
						}
						
						// 2.2 本地是否修改了, 需要合并, 使用服务端的数据
						if(notebook.IsDirty) {
							console.log('冲突....')
						// 2.3 服务器是最新的, 用服务器的
						} else {
						}
						// 这里都是用服务器端的数据, 不处理冲突
						Notebook.updateNotebookForce(notebook, notebookLocal, function(notebook) {
							if(notebook) {
								// 前端一起渲染
								me._syncInfo.notebook.updates.push(notebook);
								// me.fixSynced('notebook', 'updates', notebook);
							}
							canCall();
						})
					}
				});
			})(notebook);
		}
	},

	syncNotebook: function(afterUsn, callback) {
		var me = this;
		if(me.isStop()) {
			return;
		}
		Api.getSyncNotebooks(afterUsn, me._notebookMaxEntry, function(notebooks) {
			console.log('	syncNotebook', notebooks)
			// console.log(notebooks);
			if(Common.isOk(notebooks)) {
				me._totalSyncNotebookNum += notebooks.length;
				// 证明可能还有要同步的
				if(notebooks.length == me._notebookMaxEntry) {
					me._syncNotebookToLocal(notebooks, callback);
					var last = notebooks[notebooks.length-1];
					me.syncNotebook(last.Usn, callback);

					// 更新Usn
					me.updateSyncUsn('NotebookUsn', last.Usn);

				} else {
					console.log('	no more notebooks');
					me._syncNotebookIsLastChunk = true;
					me._syncNotebookToLocal(notebooks, callback);

					if (notebooks.length) {
						var last = notebooks[notebooks.length-1];
						me.updateSyncUsn('NotebookUsn', last.Usn);
					}
				}
			} else {
				// 同步失败
				me._syncInfo.notebook.ok = false;
				me._syncInfo.notebook.msg = "cann't get all chunks";
				callback && callback(false);
			}
		});
	},

	//-------------
	// note
	//-------------

	// 增加, 有锁
	_lockNote: 1,
	_addSyncNoteNum: function() {
		var me = this;
		if(me._lockNote) {
			me._lockNote = 0;
			me._totalHasSyncNoteNum++;
			me._lockNote = 1;
		} else {
			me._addSyncNoteNum();
		}
	},

	// 同步笔记到本地
	_syncNoteToLocal: function(notes, callback) { 
		var me = this;
		
		function canCall(isEmpty) {
			// 为空时来判断是最后一次了, 可以之前的还没处理完
			if(isEmpty && me._totalHasSyncNoteNum <  me._totalSyncNoteNum) {
				return;
			}

			// 是最后一块, 且
			me._addSyncNoteNum();
			// log('notes: ' + me._totalHasSyncNoteNum + ' ' + me._totalSyncNoteNum + ' ' + me._syncNoteIsLastChunk);
			if(me._syncNoteIsLastChunk && me._totalHasSyncNoteNum >= me._totalSyncNoteNum) {
				// 防止多次callback
				if(!me._syncInfo.note.ok) {
					log('note->next');
					me._syncInfo.note.ok = true;
					callback && callback(true);
				}
			}
		}

		// 为什么会出现最后 > 的情况, 是因为这里length == 0 也判断了
		if(!notes || notes.length == 0) {
			return canCall(true);
		}

		for(var i in notes) {
			var note = notes[i];
			// 得到本地的, 与之对比
			Web.syncProcess('note', note.Title);

			(function(note) {

				var usn = note.Usn;
				var noteId = note.NoteId;

				// 1) 服务器端删除了, 本地肯定删除
				if(note.IsDeleted) {
					console.log('	delete: ', note);
					Note.getNoteIdByServerNoteId(noteId, function(localNoteId) { 
						Note.deleteNoteForce(noteId, function() { 
							// me._syncInfo.note.deletes.push(localNoteId);
							me.fixSynced('note', 'deletes', localNoteId);
							canCall();
						});
					});
					return;
				}
				// 2) 查看本地的, 与本地合并
				Note.getNoteByServerNoteId(noteId, function(noteLocal) {
					// 2.1 本地没有, 表示是新建
					if(!noteLocal) {
						console.log('	add note', note);
						Note.addNoteForce(note, function(note) {
							// me._syncInfo.note.adds.push(note);
							me.fixSynced('note', 'adds', note);
							return canCall();
						});
					} else {
						// 如果Usn一样, 表示服务器端并没有修改
						if(noteLocal.Usn === note.Usn) {
							console.log('	note 如果Usn一样, 表示服务器端并没有修改');
							return canCall();
						}

						// 2.2 本地是否修改了, 冲突, 报告给前端, 前端处理
						// 冲突, 将本地修改的笔记复制一份(设置冲突字段, ConflictNoteId), 远程的覆盖本地的
						// 新方法: 冲突后, 得到最新内容, 看是否与本地内容一致, 如果一致, 则不冲突, 其它数据用服务器上的
						if(noteLocal.IsDirty) {
							console.log('	note 冲突 serverNoteId: ' + noteId, note.Title);
							// console.log(noteLocal.NoteId);
							// console.log(noteLocal.IsDirty);
							// console.log(noteLocal);
							// note.ServerNoteId = note.NoteId;
							// note.NoteId = noteLocal.NoteId;

							Note.getNoteContentFromServer(noteId, function (content) {
								// 表示没有获取到content, 则只能标志为冲突了
								// 内容不一样, 也标为冲突
								if (content === false || content != noteLocal.Content) {
									me._syncInfo.note.conflicts.push({server: note, local: noteLocal});
									// me.fixSynced('note', 'conflicts', {server: note, local: noteLocal});
								}
								// 否则, 内容一样, 标为不冲突, 需要更新
								else {

									// 2.3 服务器是最新的, 用服务器的
									// 服务器是最新的, 本地没动过, 则覆盖之
									Note.updateNoteForce(note, function(note) {
										if(note) {
											// me._syncInfo.note.updates.push(note);
											me.fixSynced('note', 'updates', note);
										}
										canCall();
									}, false);
								}
							});

							return canCall();
						// 2.3 服务器是最新的, 用服务器的
						} else {
							// 服务器是最新的, 本地没动过, 则覆盖之
							Note.updateNoteForce(note, function(note) {
								if(note) {
									// me._syncInfo.note.updates.push(note);
									me.fixSynced('note', 'updates', note);
								}
								canCall();
							});
						}
					}
				});
			})(note);
		}
	},

	syncNote: function(afterUsn, callback) {
		var me = this;
		if(me.isStop()) {
			return;
		}

		console.log('	pull notes from server...');
		Api.getSyncNotes(afterUsn, me._noteMaxEntry, function(notes) {
			// console.log('syncNote---');
			console.log('	notes:', notes);
			if(Common.isOk(notes)) {
				me._totalSyncNoteNum += notes.length;
				// 证明可能还有要同步的
				if(notes.length == me._noteMaxEntry) {
					me._syncNoteToLocal(notes, callback);
					var last = notes[notes.length-1];

					// 500ms延迟
					setTimeout(function () {
						me.syncNote(last.Usn, callback);
					}, 500);

					// 更新Usn
					me.updateSyncUsn('NoteUsn', last.Usn);

				} else {
					console.log('	no more notes');
					me._syncNoteIsLastChunk = true;
					me._syncNoteToLocal(notes, callback);

					if (notes.length) {
						var last = notes[notes.length-1];
						me.updateSyncUsn('NoteUsn', last.Usn);
					}
				}
			} else {
				// 同步失败
				me._syncInfo.note.ok = false;
				me._syncInfo.note.msg = "cann't get all chunks";
				console.error('	pull notes error', notes)
				callback && callback(false);
			}
		});
	},


	//----------------
	// 同步标签
	// ---------------
	// 增加, 有锁
	_lockTag: 1,
	_addSyncTagNum: function() {
		var me = this;
		if(me._lockTag) {
			me._lockTag = 0;
			me._totalHasSyncTagNum++;
			me._lockTag = 1;
		} else {
			me._addSyncTagNum();
		}
	},

	// 同步标签到本地
	_syncTagToLocal: function(tags, callback) { 
		var me = this;
		function canCall(isEmpty) {
			// 为空时来判断是最后一次了, 可以之前的还没处理完
			if(isEmpty && me._totalHasSyncTagNum <  me._totalSyncTagNum) {
				return;
			}

			// 是最后一块, 且
			me._addSyncTagNum();
			// log('tags: ' + me._totalHasSyncNoteNum + ' ' + me._totalSyncNoteNum + ' ' + me._syncNoteIsLastChunk);
			if(me._syncTagIsLastChunk && me._totalHasSyncTagNum >= me._totalSyncTagNum) {
				// 防止多次callback
				if(!me._syncInfo.tag.ok) {
					log('tag->next');
					me._syncInfo.tag.ok = true;
					callback && callback(true);
				}
			}
		}

		// 为什么会出现最后 > 的情况, 是因为这里length == 0 也判断了
		if(!tags || tags.length == 0) {
			return canCall(true);
		}

		for(var i in tags) {
			var tag = tags[i];
			// 得到本地的, 与之对比
			Web.syncProcess('tag', tag.Tag);

			(function(tag) {

				var usn = tag.Usn;
				var tagId = tag.TagId;

				// 1) 服务器端删除了, 本地肯定删除
				if(tag.IsDeleted) {
					console.log('	delete tag: ', tag);
					Tag.deleteTag(tag.Tag, function() {
						// me._syncInfo.tag.deletes.push(tag.Tag);
						me.fixSynced('tag', 'deletes', tag.Tag);
						canCall();
					}, true, me.fullSyncStart);
					return;
				}
				// 2) 查看本地的, 与本地合并
				Tag.getTag(tag.Tag, function(tagLocal) {
					// 2.1 本地没有, 表示是新建
					if(!tagLocal) {
						console.log('	add tag: ...')
						Tag.addOrUpdateTag(tag.Tag, function(tagAdded) {
							// me._syncInfo.tag.adds.push(tagAdded);
							me.fixSynced('tag', 'adds', tagAdded);
							return canCall();
						}, true, usn);
					} else {
						// 本地有, 不用操作
						Tag.setNotDirtyAndUsn(tag.Tag, tag.Usn);
						return canCall();
					}
				});
			})(tag);
		}
	},
	syncTag: function(afterUsn, callback) {
		var me = this;
		if(me.isStop()) {
			return;
		}
		Api.getSyncTags(afterUsn, me._tagMaxEntry, function(tags) {
			// console.log(afterUsn + ' syncTags------------------------------------')
			// console.log(tags);
			if(Common.isOk(tags)) {
				me._totalSyncTagNum += tags.length;
				// 证明可能还有要同步的
				if(tags.length == me._tagMaxEntry) {
					me._syncTagToLocal(tags, callback);
					var last = tags[tags.length-1];
					me.syncTag(last.Usn, callback);

					// 更新Usn
					me.updateSyncUsn('TagUsn', last.Usn);
				} else {
					log('no more');
					me._syncTagIsLastChunk = true;
					me._syncTagToLocal(tags, callback);

					if (tags.length) {
						var last = tags[tags.length-1];
						me.updateSyncUsn('TagUsn', last.Usn);
					}
				}
			} else {
				// 同步失败
				me._syncInfo.tag.ok = false;
				me._syncInfo.tag.msg = "cann't get all chunks";
				console.error('	sync tags error...')
				// console.log(tags);
				callback && callback(false);
			}
		});
	},

	//----------

	// 记录LastSyncUsn, LastUpdateTime 同步时间
	updateLastSyncState: function(callback) {
		var me = this;
		User.updateLastSyncState(function() {
			callback();
		});
	},

	// 为了避免全都重新来过, 这里保存每次
	updateSyncUsn: function (type, usn) {
		// console.error('---')
		// console.log(type + ' = ' + usn);
		User.updateEachSyncState(type, usn, function() {});
	},

	// 全量同步
	// callback(error, info)
	fullSync: function(callback) {
		var me = this;
		me._stop = false;
		me._initSyncInfo();
		me.fullSyncStart = true;

		User.getAllLastSyncState(function(lastUsn, notebookUsn, noteUsn, tagUsn) {
			// 不可能会有lastUsn吧
			if (lastUsn && lastUsn > 0) {
				notebookUsn = -1;
				noteUsn = -1;
				tagUsn = -1;
			}

			if (!notebookUsn) {
				notebookUsn = -1;
			}
			if (!noteUsn) {
				noteUsn = -1;
			}
			if (!tagUsn) {
				tagUsn = -1;
			}

			console.log('fullSync ' + notebookUsn + ' ' + noteUsn + ' ' + tagUsn);

			Api.getLastSyncState(function(serverState) {
				if(!Common.isOk(serverState)) {
					console.error('	get Server LastSyncState error!!');
					callback && callback(serverState, null);
					return;
				}

				// Web.syncNotebookFinish();
				// 同步笔记本
				me.syncNotebook(notebookUsn, function(ok) {
					if(ok) {
						// Web.syncNoteFinish();
						// console.log('------------------')
						// 同步笔记
						me.syncNote(noteUsn, function(ok) {
							if(ok) {
								// Web.syncTagFinish();
								// 同步标签
								me.syncTag(tagUsn, function(ok) { 
									if (ok) {
										me.fullSyncStart = false;
										// 更新上次同步时间
										me.updateLastSyncState(function() {
											// send changes
											// me.sendChanges();
											callback && callback(false, me._syncInfo);
										});
									}
									else {
										me.fullSyncStart = false;
										console.error('syncTag error....');
										callback && callback({}, me._syncInfo);
									}
								});
							} else {
								me.fullSyncStart = false;
								console.error('syncNote error.... 跳过tag');
								callback && callback({}, me._syncInfo);
							}
						});
					} else {
						me.fullSyncStart = false;
						console.error('syncNotebook error.... 跳过note,tag');
						callback && callback({}, me._syncInfo);
					}
				});

			});

		});
	},

	// 处理同步好的之后的
	// mainType == notebook, note, tag
	// type = changeAdds, changeConflicts, adds, deletes, updates, conflicts, errors,
	fixSynced: function (mainType, type, data) {
		if (this.fullSyncStart) {
			return;
		}
		var o = {};
		if (!Common.isArray(data)) {
			data = [data];
		}
		o[type] = data;
		if (mainType == 'notebook') {
			Notebook.fixConflicts(o);
		} else if (mainType == 'note') {
			Note.fixConflicts(o);
		} else {
			Web.addOrDeleteTagFromSync(o);
		}
	},

	// 前端重新渲染
	fixSyncedNotebook: function () {
		var me = this;
		if(me.incrSyncStart) {
			console.log('	fixSyncedNotebook')
			Notebook.fixConflicts(me._syncInfo.notebook);
		}
	},

	// 处理冲突
	fixConflicts: function(callback) {
		var me = this;
		var afterInfo = me._syncInfo;
		// log('处理冲突....');
		// log(me._syncInfo);
		// log(me._syncInfo.tag);
		var tag = me._syncInfo.tag;

		// 如果是incSync, 则要前端处理
		// 不是fullSync
		if(me.incrSyncStart) {

			// Notebook.fixConflicts(me._syncInfo.notebook);

			Note.fixConflicts(me._syncInfo.note, function() {
				// 避免无限循环, 别send changes了
				if(!me._needIncrSyncAgain) {
					// alert("?")
					console.log("	not needIncrSyncAgain")
					// send changes
					callback && callback();
				} else {

				}
			});

			// 添加或删除一些tag
			// console.log('怎么可能?')
			// console.error(me._syncInfo.tag); // 为空, 不知道原因
			// console.error();
			// console.error(tag);
			Web.addOrDeleteTagFromSync(tag);
		}
	},

	fixConflictsForSendChanges: function(callback) { 
		var me = this;
		me.fixConflicts(function() {
			callback();
			// 已结束
			me.setSyncFinished();
		})
	},
	// 同步状态
	syncProcessStatus: 0,
	// 添加
	addSyncProcessStatus: function(n) {
		var me = this;
		me.syncProcessStatus += n;
		if(me.syncProcessStatus >= 100) {
			me.syncProcessStatus = 98;
		}
		Web.syncProgress(me.syncProcessStatus);
	},
	// 增量同步
	incrSyncStart: false,
	// 如果第一次insync, 网络错误导致incrSyncStart不结束, 第二次就会永远转动
	setSyncFinished: function(hasError) { 
		var me = this;
		// unconnect也会调, 所以, 一旦所有都是unconnect, 那就时不时会有进度条到100
		if (!me.incrSyncStart || !me.incrSyncStart) {
			return;
		}
		me.incrSyncStart = false;
		me.fullSyncStart = false;
		// Web.syncProgress(0);
		Web.syncFinished(hasError);
	},
	incrSync: function(again) {
		if (User.isLocal()) {
			console.log('no sync for local account');
			return;
		}
		var me = this;
		me._stop = false;
		me._initSyncInfo();

		// again表示重来
		if(!again && me.incrSyncStart) {
			console.log('上一sync未结束!!');
			return;
		}

		me.incrSyncStart = true;
		me.syncProcessStatus = 0;

		console.log('inc sync start');
		if(again) {
			console.log('again >>');
		}

		// 得到当前LastSyncUsn
		User.getLastSyncState(function(lastSyncUsn, lastSyncTime) {
			console.log('%cstep1 getLastSyncState', 'color: #68bc7a')

			// 没有上次同步的时间, 则需要进行一次全量同步, 不可能会发生
			if(!lastSyncUsn) {
				console.error('	getLastSyncState error!!');
				me.setSyncFinished();
				return;
			}

			// 先从服务器上得到usn, 与本地的判断, 是否需要pull
			Api.getLastSyncState(function(serverState) {
				if(!Common.isOk(serverState)) {
					console.error('	get Server LastSyncState error!!');
					me.setSyncFinished(true);
					return;
				}
				console.log('	get Server LastSyncState ret', serverState.LastSyncUsn + ' ' + lastSyncUsn);
				if(serverState.LastSyncUsn > lastSyncUsn) {
					// 需要同步笔记本
					console.log('%cstep2 pull', 'color: #68bc7a')
					// 同步笔记本
					me.syncNotebook(lastSyncUsn, function(ok) {
						if(ok) {
							me.fixSyncedNotebook();
							console.log('	incr notebook ok', lastSyncUsn);

							me.addSyncProcessStatus(10);
							console.log('	incr note start');

							// 同步笔记
							me.syncNote(lastSyncUsn, function(ok) {
								if(ok) {
									console.log('	incr note ok', lastSyncUsn);
									me.addSyncProcessStatus(30);
									// 同步标签
									me.syncTag(lastSyncUsn, function() { 
										console.log('	incr tag ok', lastSyncUsn);
										me.addSyncProcessStatus(10);
										// 更新上次同步时间
										me.updateLastSyncState(function() {
											// send changes
											me.sendChanges(again);
										});
									});
								} else {
									console.log('	incr note not ok')
									me.fixConflicts();
								}
							});
						} else {
							me.fixConflicts();
						}
					});

				} else {
					console.log('%cstep2 不需要pull, skip', 'color: #68bc7a')
					me.addSyncProcessStatus(50);
					me.sendChanges(again);
				}
			});
		});
	},

	//---------
	// 发送改变
	//---------

	// 发送笔记本的更改
	sendNotebookChanges: function(callback) { 
		var me = this;
		console.log('	3.1: sendNotebookChanges')
		// 获取所有笔记本的更改
		Notebook.getDirtyNotebooks(function(notebooks) {
			if (!Common.isEmpty(notebooks)) {
				console.log('	dirty notebooks:', notebooks);
			} else {
				console.log('	no dirty notebooks');
			}
			if(!notebooks) {
				callback && callback();
			} else {
				// 调api, 所有执行后再callback();
				// 一个一个同步执行, 因为要有
				async.eachSeries(notebooks, function(notebook, cb) {
					var api = Api.updateNotebook;

					// 本地既是新的, 又是删除的, 删除本地的, 不要同步
					// 5/4
					if(notebook.LocalIsNew && notebook.LocalIsDelete) {
						console.log('	笔记本既是新的, 又是删除的, 不同步, 直接删除本地的');
						Notebook.deleteLocalNotebook(notebook.NotebookId);
						return cb();
					}

					if(notebook.LocalIsNew) {
						api = Api.addNotebook;
					} else if(notebook.LocalIsDelete) {
						api = Api.deleteNotebook;
					}

					api.call(Api, notebook, function(newNotebook) {
						// 更新失败
						if(!newNotebook) {
							return cb();
						}

						// 删除操作
						if(notebook.LocalIsDelete) {
							return cb();
						}

						// 更新成功, 是否有冲突? 
						// newNotebook是服务器上的笔记本
						// 没有更新成功
						if(!newNotebook.NotebookId) {
							if(newNotebook.Msg == 'conflict') {
								// 没用, 前端不会处理的, 按理不会出现这种情况, 因为先sync
								// me._syncInfo.notebook.conflicts.push(newNotebook);
								me.fixSynced('notebook', 'conflicts', newNotebook);
							} else if(newNotebook.Msg == 'notExists') {
								// 服务器端没有, 那么要作为添加
								// 可能服务器上已删除, 此时应该要作为添加而不是更新
								// me._syncInfo.notebook.changeNeedAdds.push(notebook);
								me.fixSynced('notebook', 'changeNeedAdds', notebook);
							}

							// me.checkNeedIncSyncAgain(newNotebook.Usn);
							return cb();
						}
						else {
							// 更新
							// TODO 后端updateNotebook只要传Usn回来即可
							console.log("	返回来的notebook " + newNotebook.Title)
							
							Notebook.updateNotebookForceForSendChange(notebook.NotebookId, newNotebook, function() { 
								if(notebook.LocalIsNew) {
									// me._syncInfo.notebook.changeAdds.push(newNotebook);
									me.fixSynced('notebook', 'changeAdds', newNotebook);
								} else {
									// me._syncInfo.notebook.changeUpdates.push(newNotebook);
									me.fixSynced('notebook', 'changeUpdates', newNotebook);
								}

								// 这里才cb(), 因为先添加父, 再添加子
								me.checkNeedIncSyncAgain(newNotebook.Usn);
								cb();
							});
						}
						
					});
				}, function() {
					callback && callback();
				});
			}
		});
	},

	checkNeedIncSyncAgain: function(usn) {
		var me = this;
		// 如果之前都很正常
		if(!me._needIncrSyncAgain) {
			// 检查是否有问题
			if(User.getLastSyncUsn() + 1 == usn) { 
				// 更新到本地lastSyncUsn
				User.updateLastSyncUsn(usn);
			} else {
				// newNote.Usn > User.getLastSyncUsn + 1, 表示服务器端在本次同步后, sendChanges之前有更新
				// 那么, 还需要来一次incrSync, 之后
				if(User.getLastSyncUsn() > usn) { // 添加标签时如果标签已经存在, 则返回的是旧的
					return;
				}
				console.error('---?? checkNeedIncSyncAgain ??------' + usn)
				console.trace();
				me._needIncrSyncAgain = true;
			}
		}
	},

	// 发送笔记改变
	// 发送笔记本的更改
	sendNoteChanges: function(callback) { 
		var me = this;
		console.log('	3.2: sendNoteChanges');
		// 获取所有笔记本的更改
		Note.getDirtyNotes(function(notes) {
			if (!Common.isEmpty(notes)) {
				console.log('	dirty notes:', notes);
			} else {
				console.log('	no dirty notes');
			}
			if(!notes) {
				callback && callback();
			} else {
				// 调api, 所有执行后再callback();
				// 一个一个同步执行, 因为要有
				async.eachSeries(notes, function(note, cb) {
					
					if (note.InitSync) {
						console.log('	InitSync is Dirty', note);
						return cb();
					}

					if (note.ConflictNoteId && !note.ConflictFixed) {
						console.log('	未解决的冲突不同步', note.Title);
						return cb();
					}

					if(note.LocalIsNew) {
						// 是新的, 且不是trash和删除的
						if(!note.IsTrash && !note.LocalIsDelete) { 
							// 添加, newNote的返回不会很多值(server端)
							Api.addNote(note, function(err, newNote) {
								if(err || !Common.isOk(newNote)) {
									console.log('	添加笔记失败!', err, newNote);
									me._syncInfo.note.errors.push({err: err, ret: newNote, note: note});
									return cb();
								}
								console.log('	添加笔记成功!', note.Title);

								newNote.ServerNoteId = newNote.NoteId;
								newNote.NoteId = note.NoteId;

								newNote.IsBlog = note.IsBlog; // 前端要用

								// me._syncInfo.note.changeAdds.push(newNote);
								me.fixSynced('note', 'changeAdds', newNote);

								Note.updateNoteForceForSendChange(newNote, true);

								// 这里
								me.checkNeedIncSyncAgain(newNote.Usn);

								cb();
							});
						}
						// 5/4
						// 本地已经删除了, 则彻底删除之
						else if(note.LocalIsDelete) {
							console.log('	既是新的, 又是删除的, 则删除笔记的');
							Note.deleteLocalNote(note.NoteId);
							return cb();
						}
						// isTrash, 不同步, 不删除
						else {
							console.log('	既是新的, 又是trash的, 不要同步');
							return cb();
						}
					}
					else if(note.LocalIsDelete) {
						// 删除, 不管它了
						// TODO
						Api.deleteTrash(note, function(ret) {
							if(Common.isOk(ret)) {
								me.checkNeedIncSyncAgain(ret.Usn);

							// 本地删除了的, 服务端没有, 直接删除本地的
							} else if (typeof ret == 'object' && ret.Msg == 'notExists') {
								console.log(	'本地删除了的, 服务端没有, 直接删除本地的');
								Note.deleteLocalNote(note.NoteId);
							}
							return cb();
						});
					}
					else {
						// 更新
						Api.updateNote(note, function(err, ret) {
							if(err || !Common.isOk(ret)) {
								console.log('	update error:' + note.Title, ret);
								if(typeof ret == 'object') {
									if(ret.Msg == 'conflict') {
										console.error('	updateNote 冲突');

										// 这种情况有很少见, 原因是先pull, 肯定会pull过来
										// 处理方法和pull一样, 看内容是否一样
										// 如果一样, 则不标志为冲突, 修改本Usn为serverUsn, 等下次再send changes
										Note.getNoteContentFromServer(note.ServerNoteId, function (content) {
											if (content === false || content != note.LocalContent) {
												me._syncInfo.note.changeConflicts.push(note);
												cb();
											}
											else {
												// 不冲突, 修改之Usn
												Api.getNote(note.ServerNoteId, function (serverNote) {
													// 取不到, 当作冲突
													if (!serverNote) {
														me._syncInfo.note.changeConflicts.push(note);
														cb();
													}
													else {
														Note.updateNoteUsn(note.NoteId, serverNote.Usn);
														cb();
													}
												});
											}
										});
									}
									else if(ret.Msg == 'notExists') {
										Note.setError(note.NoteId, err, ret);
										me.fixSynced('note', 'errors', {err: err, ret: ret, note: note});
										// me._syncInfo.note.errors.push({err: err, ret: ret, note: note});
										// 可能服务器上已删除, 此时应该要作为添加而不是更新
										// me._syncInfo.note.changeNeedAdds.push(note);
										cb();
									}
									// 其它错误, 不加, notImage 导致不能终止
									else {
										Note.setError(note.NoteId, err, ret);
										console.error('	updateNote error', err, ret);
										me.fixSynced('note', 'errors', {err: err, ret: ret, note: note});
										cb();
									}
								}
								// 更新失败了, 服务器返回异常
								else {
									Note.setError(note.NoteId, err, ret);
									console.error('	updateNote error', err);
									me.fixSynced('note', 'errors', {err: err, ret: ret, note: note});
									cb();
								}
							}
							// 更新成功
							else {
								console.log('	更新成功: ', note.Title);
								ret.ServerNoteId = ret.NoteId;
								ret.NoteId = note.NoteId;
								Note.updateNoteForceForSendChange(ret, false);
								// me._syncInfo.note.changeUpdates.push(note);
								me.fixSynced('note', 'changeUpdates', note);

								me.checkNeedIncSyncAgain(ret.Usn);

								return cb();
							}
						});
					}
				}, function() {
					callback && callback();
				});
			}
		});
	},
	// 发送标签改变
	sendTagChanges: function(callback) {
		console.log('	3.3: sendTagChanges');
		var me = this;
		// 获取所有笔记本的更改
		Tag.getDirtyTags(function(tags) {
			if (!Common.isEmpty(tags)) {
				console.log('	dirty tags:', tags);
			} else {
				console.log('	no dirty tags');
			}
			if(!tags) {
				callback && callback();
			} else {
				// 调api, 所有执行后再callback();
				// 一个一个同步执行, 因为要有
				async.eachSeries(tags, function(tag, cb) {
					if(tag.IsDirty) {
						if(!tag.LocalIsDelete) {
							// 添加
							Api.addTag(tag.Tag, function(newTag) {
								if(!Common.isOk(newTag)) {
									return cb();
								}
								// 更新, 添加usn
								Tag.setNotDirtyAndUsn(tag.Tag, newTag.Usn);

								me._syncInfo.tag.changeAdds.push(newTag); // 之前是note.changeAdds
								// Tag.updateTagForce(newTag);
								me.checkNeedIncSyncAgain(newTag.Usn);
								cb();
							});
						} else {
							// 删除, 不管它了
							Api.deleteTag(tag, function(ret) {
								if(Common.isOk(ret)) {
									Tag.setNotDirty(tag.Tag);
									me.checkNeedIncSyncAgain(ret.Usn);
								// 本地删除了的, 服务端没有, 直接删除本地的
								} else if (typeof ret == 'object') {
									if (ret.Msg == 'notExists') {
										console.log(	'tag本地删除了的, 服务端没有, 直接删除本地的');
										Tag.deleteLocalTag(tag.Tag);
									} else if(ret.Msg == 'conflict') {
										Tag.setNotDirty(tag.Tag);
									}
								}
								return cb();
							});
						}
					}
				}, function() {
					callback && callback();
				});
			}
		});
	},

	// again, 再一次sync, 不要send changes
	sendChanges: function(again) { 
		var me = this;
		console.log('%cstep3 send changes', 'color: #68bc7a')
		// 先处理冲突, 可以同时进行
		if(again) {
			console.log('	send changes again....');
			me.fixConflictsForSendChanges(function(){});
			return;
		}
		me.fixConflicts(function() {
			// send changes
			console.log('	send changes');
			me._initSyncInfo(); // 重新初始化[]
			async.series([
				function(cb) {
					me.sendNotebookChanges(cb);
				},
				function(cb) {
					me.addSyncProcessStatus(10);
					me.sendNoteChanges(cb);
				},
				function(cb) {
					me.addSyncProcessStatus(30);
					me.sendTagChanges(cb);
				}
			], function() {
				me.addSyncProcessStatus(10);

				// 重新再来一次增量同步
				if(me._needIncrSyncAgain) {
					console.error('	needIncrSyncAgain')
					me.fixConflictsForSendChanges(function() {
						me.incrSync(true);
					});
				} else {
					console.log('	send changes 后解决冲突');
					me.fixConflictsForSendChanges(function() {
					});
				}
			});
		});
	}
};

module.exports = Sync;