/*
 * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.micode.notes.gtask.remote;//实现同步功能的主函数

import android.app.Activity;//在包里引用各种类
import android.content.ContentResolver;//引用各种类
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.util.Log;

import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.DataColumns;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.gtask.data.MetaData;
import net.micode.notes.gtask.data.Node;
import net.micode.notes.gtask.data.SqlNote;
import net.micode.notes.gtask.data.Task;
import net.micode.notes.gtask.data.TaskList;
import net.micode.notes.gtask.exception.ActionFailureException;
import net.micode.notes.gtask.exception.NetworkFailureException;
import net.micode.notes.tool.DataUtils;
import net.micode.notes.tool.GTaskStringUtils;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;


public class GTaskManager {//声明一个类，类名称的命名规范：所有单词的首字母大写
    private static final String TAG = GTaskManager.class.getSimpleName();// 定义了一系列静态变量来显示GTask当前的状态

    public static final int STATE_SUCCESS = 0;

    public static final int STATE_NETWORK_ERROR = 1;

    public static final int STATE_INTERNAL_ERROR = 2;

    public static final int STATE_SYNC_IN_PROGRESS = 3;

    public static final int STATE_SYNC_CANCELLED = 4;//声明了5个静态变量来分别表示5种状态

    private static GTaskManager mInstance = null;//private 定义一系列不可被外部的类访问的量

    private Activity mActivity;

    private Context mContext;

    private ContentResolver mContentResolver;

    private boolean mSyncing;

    private boolean mCancelled;

    private HashMap<String, TaskList> mGTaskListHashMap;

    private HashMap<String, Node> mGTaskHashMap;

    private HashMap<String, MetaData> mMetaHashMap;

    private TaskList mMetaList;

    private HashSet<Long> mLocalDeleteIdMap;

    private HashMap<String, Long> mGidToNid;

    private HashMap<Long, String> mNidToGid;

    private GTaskManager() {//方法：类的构造函数，对其内部变量进行初始化
        mSyncing = false;//正在同步标识，false代表未同步
        mCancelled = false;
        mGTaskListHashMap = new HashMap<String, TaskList>();//Hashmap的映射实现字符串到对象。
        mGTaskHashMap = new HashMap<String, Node>();//创建一个节点表
        mMetaHashMap = new HashMap<String, MetaData>();//创建一个删除本地ID的map
        mMetaList = null;//创建一个删除本地ID的map
        mLocalDeleteIdMap = new HashSet<Long>();
        mGidToNid = new HashMap<String, Long>();//建立一个google id到节点id的映射
        mNidToGid = new HashMap<Long, String>();
    }

    public static synchronized GTaskManager getInstance() {//用static synchronized控制类的所有实例的访问
        if (mInstance == null) {
            mInstance = new GTaskManager();
        }
        return mInstance;
    }

    public synchronized void setActivityContext(Activity activity) {//获取当前的操作并更新至GTask中
        // used for getting authtoken
        mActivity = activity;
    }

    public int sync(Context context, GTaskASyncTask asyncTask) {
        if (mSyncing) {
            Log.d(TAG, "Sync is in progress");
            return STATE_SYNC_IN_PROGRESS;
        }
        mContext = context;
        mContentResolver = mContext.getContentResolver();//对GTaskManager的属性进行更新
        mSyncing = true;
        mCancelled = false;
        mGTaskListHashMap.clear();//对各种结构进行清空
        mGTaskHashMap.clear();
        mMetaHashMap.clear();
        mLocalDeleteIdMap.clear();
        mGidToNid.clear();
        mNidToGid.clear();

        try {//异常处理程序
            GTaskClient client = GTaskClient.getInstance();//实例化一个GTask用户对象
            client.resetUpdateArray();//重置更新操作。

            // login google task
            if (!mCancelled) {//代码块：登陆google task，如果mActivity登录不上则抛出一个异常
                if (!client.login(mActivity)) {
                    throw new NetworkFailureException("login google task failed");
                }
            }

            // get the task list from google
            asyncTask.publishProgess(mContext.getString(R.string.sync_progress_init_list));
            initGTaskList();//调用下面自定义的方法，初始化GTaskList

            // do content sync work
            asyncTask.publishProgess(mContext.getString(R.string.sync_progress_syncing));
            syncContent();//进行同步操作
        } catch (NetworkFailureException e) {//处理各种异常
            Log.e(TAG, e.toString());
            return STATE_NETWORK_ERROR;
        } catch (ActionFailureException e) {//操作未完成异常
            Log.e(TAG, e.toString());
            return STATE_INTERNAL_ERROR;
        } catch (Exception e) {//内部错误异常
            Log.e(TAG, e.toString());
            e.printStackTrace();
            return STATE_INTERNAL_ERROR;
        } finally {//结束后清空环境。
            mGTaskListHashMap.clear();//同步结束后清空环境
            mGTaskHashMap.clear();
            mMetaHashMap.clear();
            mLocalDeleteIdMap.clear();
            mGidToNid.clear();
            mNidToGid.clear();
            mSyncing = false;
        }

        return mCancelled ? STATE_SYNC_CANCELLED : STATE_SUCCESS;//若在同步时操作未取消，则返回同步成功，否则返回同步操作取消
    }

    private void initGTaskList() throws NetworkFailureException {//初始化任务列表，其实就是从Google获取任务列表。
        if (mCancelled)//初始化任务列表，其实就是从Google获取任务列表。
            return;
        GTaskClient client = GTaskClient.getInstance();// 实例化一个GTask用户对象
        try {//获取任务列表
            JSONArray jsTaskLists = client.getTaskLists();//获取任务列表

            // init meta list first
            mMetaList = null;
            for (int i = 0; i < jsTaskLists.length(); i++) {//对于获取的每一个任务串jsonarray
                JSONObject object = jsTaskLists.getJSONObject(i);//获得任务串的每一个结点和id名字
                String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID);//获取它的id
                String name = object.getString(GTaskStringUtils.GTASK_JSON_NAME);//获取它的名字

                if (name
                        .equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META)) {//如果 name 等于 字符串 "[MIUI_Notes]" + "METADATA"，执行if语句块
                    mMetaList = new TaskList();//新建数组，并为新建的数组设定内容
                    mMetaList.setContentByRemoteJSON(object);

                    // load meta data
                    JSONArray jsMetas = client.getTaskList(gid);//读取元数据
                    for (int j = 0; j < jsMetas.length(); j++) {//把jsMetas里的每一个有识别码的metaData都放到哈希表中
                        object = (JSONObject) jsMetas.getJSONObject(j);
                        MetaData metaData = new MetaData();
                        metaData.setContentByRemoteJSON(object);
                        if (metaData.isWorthSaving()) {//判断前面获取的元数据有无价值加入列表中
                            mMetaList.addChildTask(metaData);
                            if (metaData.getGid() != null) {//操作getGid：取得组识别码函数
                                mMetaHashMap.put(metaData.getRelatedGid(), metaData);
                            }
                        }
                    }
                }
            }

            // create meta list if not existed
            if (mMetaList == null) {//如果元数据列表不存在，则在客户端创建一个
                mMetaList = new TaskList();
                mMetaList.setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX
                        + GTaskStringUtils.FOLDER_META);
                GTaskClient.getInstance().createTaskList(mMetaList);
            }

            // init task list
            for (int i = 0; i < jsTaskLists.length(); i++) {//初始化任务列表
                JSONObject object = jsTaskLists.getJSONObject(i);
                String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID);
                String name = object.getString(GTaskStringUtils.GTASK_JSON_NAME);

                if (name.startsWith(GTaskStringUtils.MIUI_FOLDER_PREFFIX)
                        && !name.equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX
                                + GTaskStringUtils.FOLDER_META)) {
                    TaskList tasklist = new TaskList();//创建一个新的任务列表
                    tasklist.setContentByRemoteJSON(object);//对任务列表的内容进行设置
                    mGTaskListHashMap.put(gid, tasklist);//对任务列表的内容进行设置
                    mGTaskHashMap.put(gid, tasklist);

                    // load tasks
                    JSONArray jsTasks = client.getTaskList(gid);//获取任务id号
                    for (int j = 0; j < jsTasks.length(); j++) {
                        object = (JSONObject) jsTasks.getJSONObject(j);
                        gid = object.getString(GTaskStringUtils.GTASK_JSON_ID);
                        Task task = new Task();
                        task.setContentByRemoteJSON(object);//设置任务内容
                        if (task.isWorthSaving()) {//判断该任务有无价值保存
                            task.setMetaInfo(mMetaHashMap.get(gid));
                            tasklist.addChildTask(task);
                            mGTaskHashMap.put(gid, task);
                        }
                    }
                }
            }
        } catch (JSONException e) {//初始化时捕捉异常
            Log.e(TAG, e.toString());//获取异常类型和异常详细消息
            e.printStackTrace();
            throw new ActionFailureException("initGTaskList: handing JSONObject failed");//抛出异常，提交 JSONObject 失败
        }
    }

    private void syncContent() throws NetworkFailureException {//实现内容同步的操作
        int syncType;
        Cursor c = null;
        String gid;
        Node node;

        mLocalDeleteIdMap.clear();

        if (mCancelled) {//对于本地已删除的便签采取的动作
            return;
        }

        // for local deleted note
        try {//代码块：对本地要删除的节点进行操作
            c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE,
                    "(type<>? AND parent_id=?)", new String[] {//定位要删除的节点位置
                            String.valueOf(Notes.TYPE_SYSTEM), String.valueOf(Notes.ID_TRASH_FOLER)
                    }, null);
            if (c != null) {//代码块：若获取到的待删除便签不为空，则进行同步操作
                while (c.moveToNext()) {// 通过while用指针遍历所有结点
                    gid = c.getString(SqlNote.GTASK_ID_COLUMN);
                    node = mGTaskHashMap.get(gid);
                    if (node != null) {//节点非空则从哈希表里删除，并进行同步操作
                        mGTaskHashMap.remove(gid);
                        doContentSync(Node.SYNC_ACTION_DEL_REMOTE, node, c);
                    }

                    mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN));//在本地删除记录中添加这一项记录
                }
            } else {//若c结点为空，即寻找错误的时候，报错
                Log.w(TAG, "failed to query trash folder");
            }
        } finally {//代码块：最后把c关闭并重置
            if (c != null) {
                c.close();
                c = null;
            }
        }

        // sync folder first
        syncFolder();

        // for note existing in database
        try {//代码块：对已存在于数据库的便签进行同步
            c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE,
                    "(type=? AND parent_id<>?)", new String[] {//c指针指向待操作的位置
                            String.valueOf(Notes.TYPE_NOTE), String.valueOf(Notes.ID_TRASH_FOLER)
                    }, NoteColumns.TYPE + " DESC");
            if (c != null) {//query语句的用法
                while (c.moveToNext()) {//遍历节点
                    gid = c.getString(SqlNote.GTASK_ID_COLUMN);
                    node = mGTaskHashMap.get(gid);//获取待操作的便签的节点
                    if (node != null) {//节点不为空则删除gid，重新建立节点到gid的联系
                        mGTaskHashMap.remove(gid);
                        mGidToNid.put(gid, c.getLong(SqlNote.ID_COLUMN));//建立google id到节点id的映射
                        mNidToGid.put(c.getLong(SqlNote.ID_COLUMN), gid);//建立Nid到Gid之间的映射表
                        syncType = node.getSyncAction(c);
                    } else {//若为空，先判断sqlnote的trim是否为0，是则确定同步方式为add_remote，否则为del_local
                        if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) {//若本地增加了内容，则远程也要增加内容
                            // local add
                            syncType = Node.SYNC_ACTION_ADD_REMOTE;// 远程增加内容
                        } else {//若本地删除了内容，则远程也要删除内容应的gid
                            // remote delete
                            syncType = Node.SYNC_ACTION_DEL_LOCAL;//本地删除
                        }
                    }
                    doContentSync(syncType, node, c);
                }
            } else {
                Log.w(TAG, "failed to query existing note in database");//查询失败时在日志中写回错误信息
            }

        } finally {//在最后关闭c并重置
            if (c != null) {
                c.close();
                c = null;
            }
        }

        // go through remaining items
        Iterator<Map.Entry<String, Node>> iter = mGTaskHashMap.entrySet().iterator();//扫描剩下的项目，逐个进行同步
        while (iter.hasNext()) {//迭代
            Map.Entry<String, Node> entry = iter.next();
            node = entry.getValue();
            doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null);
        }

        // mCancelled can be set by another thread, so we neet to check one by
        // one
        // clear local delete table
        if (!mCancelled) {//终止标识有可能被其他进程改变，因此需要一个个进行检查
            if (!DataUtils.batchDeleteNotes(mContentResolver, mLocalDeleteIdMap)) {
                throw new ActionFailureException("failed to batch-delete local deleted notes");//终止标识有可能被其他线程改变
            }
        }

        // refresh local sync id
        if (!mCancelled) {//更新同步表
            GTaskClient.getInstance().commitUpdate();
            refreshLocalSyncId();
        }

    }

    private void syncFolder() throws NetworkFailureException {//同步文件夹
        Cursor c = null;
        String gid;
        Node node;
        int syncType;

        if (mCancelled) {//判断是否取消该操作
            return;
        }

        // for root folder
        try {//对于根文件夹
            c = mContentResolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI,
                    Notes.ID_ROOT_FOLDER), SqlNote.PROJECTION_NOTE, null, null, null);
            if (c != null) {
                c.moveToNext();
                gid = c.getString(SqlNote.GTASK_ID_COLUMN);//获取指针指向内容对应的gid
                node = mGTaskHashMap.get(gid);
                if (node != null) {// 获取gid所代表的节点
                    mGTaskHashMap.remove(gid);
                    mGidToNid.put(gid, (long) Notes.ID_ROOT_FOLDER);//将该节点的gid到nid的映射加入映射表
                    mNidToGid.put((long) Notes.ID_ROOT_FOLDER, gid);
                    // for system folder, only update remote name if necessary
                    if (!node.getName().equals(//若当前访问的文件夹是系统文件夹则只需要更新
                            GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT))
                        doContentSync(Node.SYNC_ACTION_UPDATE_REMOTE, node, c);
                } else {
                    doContentSync(Node.SYNC_ACTION_ADD_REMOTE, node, c);//若非系统文件夹则在远程进行增加节点操作
                }
            } else {
                Log.w(TAG, "failed to query root folder");//警告，询问根目录文件夹失败
            }
        } finally {//结束操作之后，最后将指针关闭，并将指针置空。方法与之前相同
            if (c != null) {
                c.close();
                c = null;
            }
        }

        // for call-note folder
        try {
            c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, "(_id=?)",//使指针指向文件夹的位置
                    new String[] {
                        String.valueOf(Notes.ID_CALL_RECORD_FOLDER)//添加MIUI文件前缀
                    }, null);
            if (c != null) {
                if (c.moveToNext()) {
                    gid = c.getString(SqlNote.GTASK_ID_COLUMN);//获取指针指向内容对应的gid
                    node = mGTaskHashMap.get(gid);
                    if (node != null) {
                        mGTaskHashMap.remove(gid);
                        mGidToNid.put(gid, (long) Notes.ID_CALL_RECORD_FOLDER);//将该节点的gid到nid的映射加入映射表
                        mNidToGid.put((long) Notes.ID_CALL_RECORD_FOLDER, gid);
                        // for system folder, only update remote name if
                        // necessary
                        if (!node.getName().equals(//若当前访问的文件夹是系统文件夹则只需要更新
                                GTaskStringUtils.MIUI_FOLDER_PREFFIX
                                        + GTaskStringUtils.FOLDER_CALL_NOTE))
                            doContentSync(Node.SYNC_ACTION_UPDATE_REMOTE, node, c);
                    } else {
                        doContentSync(Node.SYNC_ACTION_ADD_REMOTE, node, c);//若非系统文件夹则在远程进行增加节点操作
                    }
                }
            } else {
                Log.w(TAG, "failed to query call note folder");//警告，询问通讯便签文件夹失败
            }
        } finally {
            if (c != null) {//代码块：结束操作之后，将指针指向内容关闭并将指针置空
                c.close();
                c = null;
            }
        }

        // for local existing folders
        try {//对于本地已存在的文件的操作
            c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE,
                    "(type=? AND parent_id<>?)", new String[] {
                            String.valueOf(Notes.TYPE_FOLDER), String.valueOf(Notes.ID_TRASH_FOLER)
                    }, NoteColumns.TYPE + " DESC");
            if (c != null) {
                while (c.moveToNext()) {//使指针遍历所有的文件夹
                    gid = c.getString(SqlNote.GTASK_ID_COLUMN);
                    node = mGTaskHashMap.get(gid);
                    if (node != null) {
                        mGTaskHashMap.remove(gid);
                        mGidToNid.put(gid, c.getLong(SqlNote.ID_COLUMN));
                        mNidToGid.put(c.getLong(SqlNote.ID_COLUMN), gid);
                        syncType = node.getSyncAction(c);
                    } else {//更新同步类型
                        if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) {
                            // local add
                            syncType = Node.SYNC_ACTION_ADD_REMOTE;
                        } else {//若远程删除了内容，则本地也要删除内容
                            // remote delete
                            syncType = Node.SYNC_ACTION_DEL_LOCAL;
                        }
                    }
                    doContentSync(syncType, node, c);//进行同步操作
                }
            } else {
                Log.w(TAG, "failed to query existing folder");
            }
        } finally {//代码块：结束操作之后，将指针指向内容关闭并将指针置空
            if (c != null) {
                c.close();
                c = null;
            }
        }

        // for remote add folders
        Iterator<Map.Entry<String, TaskList>> iter = mGTaskListHashMap.entrySet().iterator();//对于在远程增添的内容，将其在本地同步
        while (iter.hasNext()) {//使用迭代器对远程增添的内容进行遍历
            Map.Entry<String, TaskList> entry = iter.next();
            gid = entry.getKey();
            node = entry.getValue();
            if (mGTaskHashMap.containsKey(gid)) {//获取gid对应的节点
                mGTaskHashMap.remove(gid);
                doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null);
            }
        }

        if (!mCancelled)
            GTaskClient.getInstance().commitUpdate();//如果没有取消，在GTsk的客户端进行实例的提交更新
    }

    private void doContentSync(int syncType, Node node, Cursor c) throws NetworkFailureException {//内容同步
        if (mCancelled) {//如果已经取消同步，直接返回
            return;
        }

        MetaData meta;
        switch (syncType) {//根据同步类型选择操作
            case Node.SYNC_ACTION_ADD_LOCAL:
                addLocalNode(node);
                break;
            case Node.SYNC_ACTION_ADD_REMOTE://远程添加
                addRemoteNode(node, c);
                break;
            case Node.SYNC_ACTION_DEL_LOCAL://远程删除
                meta = mMetaHashMap.get(c.getString(SqlNote.GTASK_ID_COLUMN));
                if (meta != null) {
                    GTaskClient.getInstance().deleteNode(meta);
                }
                mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN));
                break;
            case Node.SYNC_ACTION_DEL_REMOTE:
                meta = mMetaHashMap.get(node.getGid());
                if (meta != null) {
                    GTaskClient.getInstance().deleteNode(meta);
                }
                GTaskClient.getInstance().deleteNode(node);
                break;
            case Node.SYNC_ACTION_UPDATE_LOCAL://更新远程数据
                updateLocalNode(node, c);
                break;
            case Node.SYNC_ACTION_UPDATE_REMOTE:
                updateRemoteNode(node, c);
                break;
            case Node.SYNC_ACTION_UPDATE_CONFLICT://这个case是更新冲突
                // merging both modifications maybe a good idea
                // right now just use local update simply
                updateRemoteNode(node, c);
                break;
            case Node.SYNC_ACTION_NONE://空操作
                break;
            case Node.SYNC_ACTION_ERROR://操作错误
            default:
                throw new ActionFailureException("unkown sync action type");//抛出异常，未知的同步行为类型
        }
    }

    private void addLocalNode(Node node) throws NetworkFailureException {//添加本地节点
        if (mCancelled) {
            return;
        }

        SqlNote sqlNote;
        if (node instanceof TaskList) {//若待增添节点为任务列表中的节点，进一步操作
            if (node.getName().equals(//根目录
                    GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT)) {
                sqlNote = new SqlNote(mContext, Notes.ID_ROOT_FOLDER);
            } else if (node.getName().equals(//判断是否在存电话的文件夹里
                    GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_CALL_NOTE)) {
                sqlNote = new SqlNote(mContext, Notes.ID_CALL_RECORD_FOLDER);
            } else {//若没有存放的文件夹，则将其放在根文件夹中
                sqlNote = new SqlNote(mContext);
                sqlNote.setContent(node.getLocalJSONFromContent());
                sqlNote.setParentId(Notes.ID_ROOT_FOLDER);
            }
        } else {//若待增添节点不是任务列表中的节点，进一步操作
            sqlNote = new SqlNote(mContext);
            JSONObject js = node.getLocalJSONFromContent();
            try {//异常判断
                if (js.has(GTaskStringUtils.META_HEAD_NOTE)) {
                    JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE);//获取对应便签的jsonobject对象
                    if (note.has(NoteColumns.ID)) {//判断便签中是否有条目
                        long id = note.getLong(NoteColumns.ID);
                        if (DataUtils.existInNoteDatabase(mContentResolver, id)) {//若便签条目的id已无效，在便签中将其删除
                            // the id is not available, have to create a new one
                            note.remove(NoteColumns.ID);
                        }
                    }
                }

                if (js.has(GTaskStringUtils.META_HEAD_DATA)) {//以下为判断便签中的数据条目
                    JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA);
                    for (int i = 0; i < dataArray.length(); i++) {//依次删除存在的data的ID
                        JSONObject data = dataArray.getJSONObject(i);
                        if (data.has(DataColumns.ID)) {
                            long dataId = data.getLong(DataColumns.ID);
                            if (DataUtils.existInDataDatabase(mContentResolver, dataId)) {
                                // the data id is not available, have to create
                                // a new one
                                data.remove(DataColumns.ID);
                            }
                        }
                    }

                }
            } catch (JSONException e) {//对于异常进行处理
                Log.w(TAG, e.toString());//获取异常类型和异常详细消息
                e.printStackTrace();
            }
            sqlNote.setContent(js);//将之前的操作获取到的js更新至该节点中

            Long parentId = mGidToNid.get(((Task) node).getParent().getGid());//找到父任务的ID号，并作为sqlNote的父任务的ID，没有父任务则报错
            if (parentId == null) {//当不能找到该任务上一级的id时报错
                Log.e(TAG, "cannot find task's parent id locally");
                throw new ActionFailureException("cannot add local node");
            }
            sqlNote.setParentId(parentId.longValue());
        }

        // create the local node
        sqlNote.setGtaskId(node.getGid());//用getGid函数获取node节点的Gid，用于设置本地Gtask的ID
        sqlNote.commit(false);//更新本地便签

        // update gid-nid mapping
        mGidToNid.put(node.getGid(), sqlNote.getId());//更新gid与nid的映射表
        mNidToGid.put(sqlNote.getId(), node.getGid());

        // update meta
        updateRemoteMeta(node.getGid(), sqlNote);//更新远程的数据
    }

    private void updateLocalNode(Node node, Cursor c) throws NetworkFailureException {//更新远程的数据
        if (mCancelled) {//如果正在取消同步，直接返回
            return;
        }

        SqlNote sqlNote;
        // update the note locally
        sqlNote = new SqlNote(mContext, c);//在指针指向处创建一个新的节点
        sqlNote.setContent(node.getLocalJSONFromContent());//利用待更新节点中的内容对数据库节点进行设置

        Long parentId = (node instanceof Task) ? mGidToNid.get(((Task) node).getParent().getGid())//设置父任务的ID
                : new Long(Notes.ID_ROOT_FOLDER);
        if (parentId == null) {//当不能找到该任务上一级的id时报错
            Log.e(TAG, "cannot find task's parent id locally");
            throw new ActionFailureException("cannot update local node");
        }
        sqlNote.setParentId(parentId.longValue());//设置该任务节点上一级的id 
        sqlNote.commit(true);

        // update meta info
        updateRemoteMeta(node.getGid(), sqlNote);//更新远程的节点信息
    }

    private void addRemoteNode(Node node, Cursor c) throws NetworkFailureException {//添加远程节点
        if (mCancelled) {//如果正在取消同步，直接返回
            return;
        }

        SqlNote sqlNote = new SqlNote(mContext, c);//新建一个sql节点并将内容存储进Node中
        Node n;

        // update remotely
        if (sqlNote.isNoteType()) {//若待增添的节点为任务节点，进一步操作
            Task task = new Task();
            task.setContentByLocalJSON(sqlNote.getContent());

            String parentGid = mNidToGid.get(sqlNote.getParentId());//当不能找到该任务上一级的id时报错
            if (parentGid == null) {
                Log.e(TAG, "cannot find task's parent tasklist");
                throw new ActionFailureException("cannot add remote task");
            }
            mGTaskListHashMap.get(parentGid).addChildTask(task);

            GTaskClient.getInstance().createTask(task);
            n = (Node) task;

            // add meta
            updateRemoteMeta(task.getGid(), sqlNote);
        } else {
            TaskList tasklist = null;

            // we need to skip folder if it has already existed
            String folderName = GTaskStringUtils.MIUI_FOLDER_PREFFIX;
            if (sqlNote.getId() == Notes.ID_ROOT_FOLDER)//按照文件夹的形式进行命名
                folderName += GTaskStringUtils.FOLDER_DEFAULT;
            else if (sqlNote.getId() == Notes.ID_CALL_RECORD_FOLDER)
                folderName += GTaskStringUtils.FOLDER_CALL_NOTE;//若为电话号码便签文件夹则按电话号码便签命名
            else
                folderName += sqlNote.getSnippet();

            Iterator<Map.Entry<String, TaskList>> iter = mGTaskListHashMap.entrySet().iterator();//使用iterator作为map接口，对map进行遍历
            while (iter.hasNext()) {//iterator迭代器，通过统一的接口迭代所有的map元素
                Map.Entry<String, TaskList> entry = iter.next();
                String gid = entry.getKey();
                TaskList list = entry.getValue();

                if (list.getName().equals(folderName)) {
                    tasklist = list;
                    if (mGTaskHashMap.containsKey(gid)) {
                        mGTaskHashMap.remove(gid);
                    }
                    break;
                }
            }

            // no match we can add now
            if (tasklist == null) {//若找不到任务列表，则创建一个新的任务列表
                tasklist = new TaskList();
                tasklist.setContentByLocalJSON(sqlNote.getContent());
                GTaskClient.getInstance().createTaskList(tasklist);
                mGTaskListHashMap.put(tasklist.getGid(), tasklist);
            }
            n = (Node) tasklist;
        }

        // update local note
        sqlNote.setGtaskId(n.getGid());//更新本地节点
        sqlNote.commit(false);
        sqlNote.resetLocalModified();
        sqlNote.commit(true);

        // gid-id mapping
        mGidToNid.put(n.getGid(), sqlNote.getId());//更新gid与nid的映射表
        mNidToGid.put(sqlNote.getId(), n.getGid());
    }

    private void updateRemoteNode(Node node, Cursor c) throws NetworkFailureException {//更新远程节点
        if (mCancelled) {//如果正在取消同步，直接返回
            return;
        }

        SqlNote sqlNote = new SqlNote(mContext, c);//在指针指向处创建一个新的节点

        // update remotely
        node.setContentByLocalJSON(sqlNote.getContent());//远程更新
        GTaskClient.getInstance().addUpdateNode(node);//更新节点数据

        // update meta
        updateRemoteMeta(node.getGid(), sqlNote);//更新数据

        // move task if necessary
        if (sqlNote.isNoteType()) {//判断节点类型是否符合要求
            Task task = (Task) node;
            TaskList preParentList = task.getParent();

            String curParentGid = mNidToGid.get(sqlNote.getParentId());
            if (curParentGid == null) {//找不到当前任务的上一级任务列表，报错
                Log.e(TAG, "cannot find task's parent tasklist");
                throw new ActionFailureException("cannot update remote task");
            }
            TaskList curParentList = mGTaskListHashMap.get(curParentGid);

            if (preParentList != curParentList) {//若两个上一级任务列表不一致，进行任务的移动，从之前的任务列表中移动到该列表中
                preParentList.removeChildTask(task);
                curParentList.addChildTask(task);
                GTaskClient.getInstance().moveTask(task, preParentList, curParentList);
            }
        }

        // clear local modified flag
        sqlNote.resetLocalModified();//清除本地的modified flag
        sqlNote.commit(true);
    }

    private void updateRemoteMeta(String gid, SqlNote sqlNote) throws NetworkFailureException {//更新远程的元数据节点
        if (sqlNote != null && sqlNote.isNoteType()) {//判断节点类型是否符合，类型符合时才进行更新操作
            MetaData metaData = mMetaHashMap.get(gid);
            if (metaData != null) {//若元数据组为空，则创建一个新的元数据组
                metaData.setMeta(gid, sqlNote.getContent());
                GTaskClient.getInstance().addUpdateNode(metaData);
            } else {//若元数据组不为空，则进行更新
                metaData = new MetaData();
                metaData.setMeta(gid, sqlNote.getContent());
                mMetaList.addChildTask(metaData);
                mMetaHashMap.put(gid, metaData);
                GTaskClient.getInstance().createTask(metaData);
            }
        }
    }

    private void refreshLocalSyncId() throws NetworkFailureException {//刷新本地便签id，从远程同步
        if (mCancelled) {
            return;
        }

        // get the latest gtask list
        mGTaskHashMap.clear();//获取最新的任务列表
        mGTaskListHashMap.clear();
        mMetaHashMap.clear();
        initGTaskList();

        Cursor c = null;
        try {
            c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE,
                    "(type<>? AND parent_id<>?)", new String[] {//c作为光标定位，用query进行查询
                            String.valueOf(Notes.TYPE_SYSTEM), String.valueOf(Notes.ID_TRASH_FOLER)
                    }, NoteColumns.TYPE + " DESC");
            if (c != null) {
                while (c.moveToNext()) {//用迭代器不断从GTask的哈希表中删除node节点，设置新的content和新的resolcer并更新
                    String gid = c.getString(SqlNote.GTASK_ID_COLUMN);
                    Node node = mGTaskHashMap.get(gid);
                    if (node != null) {
                        mGTaskHashMap.remove(gid);
                        ContentValues values = new ContentValues();
                        values.put(NoteColumns.SYNC_ID, node.getLastModified());//在ContentValues中创建键值对。准备通过contentResolver写入数据
                        mContentResolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI,
                                c.getLong(SqlNote.ID_COLUMN)), values, null, null);//进行更新操作
                    } else {//若发现节点为空则报错
                        Log.e(TAG, "something is missed");
                        throw new ActionFailureException(
                                "some local items don't have gid after sync");
                    }
                }
            } else {
                Log.w(TAG, "failed to query local note to refresh sync id");
            }
        } finally {//结束操作之后，将指针指向内容关闭并将指针置空
            if (c != null) {
                c.close();
                c = null;
            }
        }
    }

    public String getSyncAccount() {//获取同步帐户
        return GTaskClient.getInstance().getSyncAccount().name;
    }

    public void cancelSync() {//取消同步，置mCancelled为true
        mCancelled = true;
    }
}
