/*
 * 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.data;
//包含了一些用于处理GTask（Google Task）相关数据的类。具体来说，这些类实现了将本地笔记同步到Google Task中，以及从Google Task中同步笔记到本地的功能。

import android.database.Cursor;//引入Android类cursor（为Android提供接口用于访问数据源中的数据，并支持对数据进行查询、排序、更新等操作。）
import android.text.TextUtils;// 引入Android类TextUtilsAndroid提供的一个工具类，包含了一些字符串处理相关的方法，如检查字符串是否为空、去除字符串两端的空格等
import android.util.Log;//引入Android类Log是Android提供的一个工具类，用于在开发和调试过程中输出日志信息

import net.micode.notes.data.Notes;//是该应用程序中的一个关键类，定义了笔记和数据表的结构
import net.micode.notes.data.Notes.DataColumns;//笔记表子类，定义了笔记表中的各个字段
import net.micode.notes.data.Notes.DataConstants;//是一个接口，定义了一些常量，如便签类型、便签状态等
import net.micode.notes.data.Notes.NoteColumns;//笔记表子类，定义了笔记表中的各个字段
import net.micode.notes.gtask.exception.ActionFailureException;//是自定义异常类，表示某个操作失败了
import net.micode.notes.tool.GTaskStringUtils;//是一个字符串处理工具类，定义了一些与Google Task有关的字符串处理方法

import org.json.JSONArray;//是一个表示JSON数组的类，可以方便地读取和操作JSON数组中的元素。
import org.json.JSONException;//是一个异常类，表示在解析JSON数据时发生了错误。
import org.json.JSONObject;//是一个表示JSON对象的类，可以方便地读取和操作JSON对象中的属性和值。
public class Task extends Node//定义了一个Task类，继承自Node类
{
    private static final String TAG = Task.class.getSimpleName();

    private boolean mCompleted;//表示任务是否已完成

    private String mNotes;//表示任务的注释信息

    private JSONObject mMetaInfo;//表示任务的元信息，是一个JSONObject对象

    private Task mPriorSibling;//表示该任务在同级任务中的前一个任务对象

    private TaskList mParent;//表示该任务所属的任务列表对象

    public Task() {
        super();
        mCompleted = false;
        mNotes = null;
        mPriorSibling = null;
        mParent = null;
        mMetaInfo = null;
    }//无参构造函数，初始化所有属性值均为null或false

    public JSONObject getCreateAction(int actionId)
    //getCreateAction方法接收一个整型参数actionId，将该参数作为返回JSONObject对象的action_id属性值。
    {
        JSONObject js = new JSONObject();

        try {
            // action_type,创建操作
            js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE,
                    GTaskStringUtils.GTASK_JSON_ACTION_TYPE_CREATE);

            // action_id
            js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId);

            // index，父任务列表中的索引
            js.put(GTaskStringUtils.GTASK_JSON_INDEX, mParent.getChildTaskIndex(this));

            // entity_delta一个JSONObject对象
            JSONObject entity = new JSONObject();
            entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName());//得到任务名
            entity.put(GTaskStringUtils.GTASK_JSON_CREATOR_ID, "null");//对任务创建者id进行判空操作
            entity.put(GTaskStringUtils.GTASK_JSON_ENTITY_TYPE,//实体类数据task
                    GTaskStringUtils.GTASK_JSON_TYPE_TASK);
            if (getNotes() != null)
            //如果任务有注释，则设置notes属性表示任务注释信息。
            {
                entity.put(GTaskStringUtils.GTASK_JSON_NOTES, getNotes());
            }
            js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity);

            // parent_id
            js.put(GTaskStringUtils.GTASK_JSON_PARENT_ID, mParent.getGid());

            // dest_parent_type
            js.put(GTaskStringUtils.GTASK_JSON_DEST_PARENT_TYPE,
                    GTaskStringUtils.GTASK_JSON_TYPE_GROUP);

            // list_id
            js.put(GTaskStringUtils.GTASK_JSON_LIST_ID, mParent.getGid());

            // prior_sibling_id
            if (mPriorSibling != null) {
                js.put(GTaskStringUtils.GTASK_JSON_PRIOR_SIBLING_ID, mPriorSibling.getGid());
            }
            //如果该任务在兄弟任务中不是第一个，则设置prior_sibling_id属性值为前一个兄弟任务的gid。

        } catch (JSONException e) {
            Log.e(TAG, e.toString());
            e.printStackTrace();
            throw new ActionFailureException("fail to generate task-create jsonobject");
        }
        //如果在生成JSONObject对象时发生异常，则会抛出一个自定义异常ActionFailureException。
        return js;
    }

    public JSONObject getUpdateAction(int actionId)
  //getUpdateAction方法接收一个整型参数actionId，将该参数作为返回JSONObject对象的action_id属性值
    {
        JSONObject js = new JSONObject();

        try {
            // action_type
            js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE,
                    GTaskStringUtils.GTASK_JSON_ACTION_TYPE_UPDATE);
            //action_type属性值为update，表示更新操作

            // action_id
            js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId);
            //id属性值为该任务的gid。
            // id
            js.put(GTaskStringUtils.GTASK_JSON_ID, getGid());

            // entity_delta
            JSONObject entity = new JSONObject();
            entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName());//任务名称
            if (getNotes() != null) {
                entity.put(GTaskStringUtils.GTASK_JSON_NOTES, getNotes());
            }//如果该任务有注释，则设置notes属性表示任务注释信息
            entity.put(GTaskStringUtils.GTASK_JSON_DELETED, getDeleted());
            js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity);
        }//deleted属性表示任务是否被删除，如果该值为true，则该任务会被移除
        catch (JSONException e) {
            Log.e(TAG, e.toString());
            e.printStackTrace();
            throw new ActionFailureException("fail to generate task-update jsonobject");
        }//如果在生成JSONObject对象时发生异常，则会抛出一个自定义异常ActionFailureException。

        return js;
    }

    public void setContentByRemoteJSON(JSONObject js)
    //定义了一个setContentByRemoteJSON方法，该方法从远程JSON对象中获取任务的属性值，并将这些属性值设置到本地任务对象中
    //表示从远程服务器获取的任务信息
    {
        if (js != null) //首先检查传入的JSON对象是否为空，如果不是空对象，则依次从JSON对象中获取以下属性值
        {
            try {
                // id
                if (js.has(GTaskStringUtils.GTASK_JSON_ID))
                    //id属性值，并调用setGid方法将其赋值给本地任务对象的gid属性
                {
                    setGid(js.getString(GTaskStringUtils.GTASK_JSON_ID));
                }

                // last_modified
                if (js.has(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)) {
                    setLastModified(js.getLong(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED));
                }//last_modified属性值，并调用setLastModified方法将其赋值给本地任务对象的lastModified属性

                // name
                if (js.has(GTaskStringUtils.GTASK_JSON_NAME)) {
                    setName(js.getString(GTaskStringUtils.GTASK_JSON_NAME));
                }//name属性值，并调用setName方法将其赋值给本地任务对象的name属性

                // notes
                if (js.has(GTaskStringUtils.GTASK_JSON_NOTES)) {
                    setNotes(js.getString(GTaskStringUtils.GTASK_JSON_NOTES));
                }//notes属性值，并调用setNotes方法将其赋值给本地任务对象的notes属性

                // deleted
                if (js.has(GTaskStringUtils.GTASK_JSON_DELETED)) {
                    setDeleted(js.getBoolean(GTaskStringUtils.GTASK_JSON_DELETED));
                }//deleted属性值，并调用setDeleted方法将其赋值给本地任务对象的deleted属性

                // completed
                if (js.has(GTaskStringUtils.GTASK_JSON_COMPLETED)) {
                    setCompleted(js.getBoolean(GTaskStringUtils.GTASK_JSON_COMPLETED));
                }//completed属性值，并调用setCompleted方法将其赋值给本地任务对象的completed属性
            } catch (JSONException e) {
                Log.e(TAG, e.toString());
                e.printStackTrace();
                throw new ActionFailureException("fail to get task content from jsonobject");
            }//如果在解析JSON对象时发生异常，则会抛出一个自定义异常ActionFailureException
        }
    }

    public void setContentByLocalJSON(JSONObject js)
    //定义了一个setContentByLocalJSON方法，该方法从本地JSON对象中获取任务的属性值，并将这些属性值设置到本地任务对象中
           //setContentByLocalJSON方法接收一个JSONObject参数js，用来表示本地存储的任务信息
    {
        if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE)
                || !js.has(GTaskStringUtils.META_HEAD_DATA)) {
            Log.w(TAG, "setContentByLocalJSON: nothing is avaiable");
        }//该方法首先检查传入的JSON对象是否为空，以及是否包含必要属性。如果不满足这些条件，则打印一条警告日志并退出方法

        try {
            JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE);
            JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA);
//从JSON对象中获取META_HEAD_NOTE和META_HEAD_DATA属性的值，并分别赋给变量note和dataArray
            if (note.getInt(NoteColumns.TYPE) != Notes.TYPE_NOTE) {
                Log.e(TAG, "invalid type");
                return;
            }
//该方法检查获取到的note对象的type属性是否为Notes.TYPE_NOTE，如果不是则打印一条错误日志并退出方法
            for (int i = 0; i < dataArray.length(); i++) {
                JSONObject data = dataArray.getJSONObject(i);
                if (TextUtils.equals(data.getString(DataColumns.MIME_TYPE), DataConstants.NOTE)) {
                    setName(data.getString(DataColumns.CONTENT));
                    break;
                }
            }//该方法遍历dataArray数组中的每个元素，找到mime_type属性为DataConstants.NOTE的元素，并将其content属性值赋给本地任务对象的name属性

        } catch (JSONException e) {
            Log.e(TAG, e.toString());
            e.printStackTrace();
        }//如果在解析JSON对象时发生异常，则会打印一条错误日志
    }

    public JSONObject getLocalJSONFromContent()
    //定义了一个getLocalJSONFromContent方法，该方法从本地任务对象中获取任务的属性值，并将这些属性值构造成JSON对象返回
    {
        String name = getName();
        try {
            if (mMetaInfo == null)//获取任务的名称
                 {
                // new task created from web
                if (name == null) {
                    Log.w(TAG, "the note seems to be an empty one");
                    return null;
                }
//根据任务的元信息（即mMetaInfo属性），判断任务是从Web端创建还是同步而来。如果任务的元信息为空，则认为该任务是从Web端创建的。

                JSONObject js = new JSONObject();
                JSONObject note = new JSONObject();
                JSONArray dataArray = new JSONArray();
                JSONObject data = new JSONObject();
                data.put(DataColumns.CONTENT, name);
                dataArray.put(data);
                js.put(GTaskStringUtils.META_HEAD_DATA, dataArray);
                note.put(NoteColumns.TYPE, Notes.TYPE_NOTE);
                js.put(GTaskStringUtils.META_HEAD_NOTE, note);
                return js;//对于从Web端创建的任务，如果任务名称为空，则会打印一条警告日志并返回null。
                     // 否则，该方法构造一个JSONObject对象，其中包含META_HEAD_DATA和META_HEAD_NOTE两个属性
                     // 并将任务的名称设置为META_HEAD_DATA属性所表示的JSONArray对象中的data属性值
            } else {
                // synced task
                JSONObject note = mMetaInfo.getJSONObject(GTaskStringUtils.META_HEAD_NOTE);
                JSONArray dataArray = mMetaInfo.getJSONArray(GTaskStringUtils.META_HEAD_DATA);
                //对于同步而来的任务，该方法首先获取任务的META_HEAD_DATA和META_HEAD_NOTE属性的值，
                for (int i = 0; i < dataArray.length(); i++) {
                    JSONObject data = dataArray.getJSONObject(i);
                    if (TextUtils.equals(data.getString(DataColumns.MIME_TYPE), DataConstants.NOTE)) {
                        data.put(DataColumns.CONTENT, getName());
                        break;
                    }
                }//遍历META_HEAD_DATA属性值所表示的JSONArray对象中的所有元素，找到mime_type属性为DataConstants.NOTE的元素，并将其content属性值设置为本地任务对象的名称

                note.put(NoteColumns.TYPE, Notes.TYPE_NOTE);
                return mMetaInfo;
            }//最后，该方法将note对象的type属性设置为Notes.TYPE_NOTE，并返回元信息对应的JSONObject对象
        } catch (JSONException e) {
            Log.e(TAG, e.toString());
            e.printStackTrace();
            return null;
        }//如果在构造JSON对象时发生异常，则会打印一条错误日志，并返回null
    }

    public void setMetaInfo(MetaData metaData) //用于设置mMetaInfo属性
            //接收一个MetaData对象参数metaData
    {
        if (metaData != null && metaData.getNotes() != null) {
            try {
                mMetaInfo = new JSONObject(metaData.getNotes());
            }
            //metaData不为空并且其notes属性不为空字符串，则将notes属性值解析为JSON对象，并将其赋给mMetaInfo属性
            catch (JSONException e) {
                Log.w(TAG, e.toString());
                mMetaInfo = null;
            }//如果在解析JSON对象时发生异常，则会打印一条警告日志并将mMetaInfo属性赋为null
        }//如果metaData对象或者其notes属性为空，则不会对mMetaInfo属性做任何更改
    }

    public int getSyncAction(Cursor c)
    //用于返回同步任务的操作类型
    {
        try {
            JSONObject noteInfo = null;
            if (mMetaInfo != null && mMetaInfo.has(GTaskStringUtils.META_HEAD_NOTE)) {
                noteInfo = mMetaInfo.getJSONObject(GTaskStringUtils.META_HEAD_NOTE);
            }//mMetaInfo属性,中获取与任务相关联的笔记信息

            if (noteInfo == null) {
                Log.w(TAG, "it seems that note meta has been deleted");
                return SYNC_ACTION_UPDATE_REMOTE;
            }//// 如果不存在，则会打印一条警告日志并返回SYNC_ACTION_UPDATE_REMOTE操作类型

            if (!noteInfo.has(NoteColumns.ID)) {
                Log.w(TAG, "remote note id seems to be deleted");
                return SYNC_ACTION_UPDATE_LOCAL;
            }//检查笔记信息中是否存在NoteColumns.ID属性，
            // 如果不存在，则会打印一条警告日志并返回SYNC_ACTION_UPDATE_LOCAL操作类型

            // validate the note id now
            if (c.getLong(SqlNote.ID_COLUMN) != noteInfo.getLong(NoteColumns.ID)) {
                Log.w(TAG, "note id doesn't match");
                return SYNC_ACTION_UPDATE_LOCAL;
            }//获取查询结果集c中的本地笔记的id属性值，并将其与笔记信息中的NoteColumns.ID属性值进行比较。
            // 如果两者不相等，则会打印一条警告日志并返回SYNC_ACTION_UPDATE_LOCAL操作类型。

            if (c.getInt(SqlNote.LOCAL_MODIFIED_COLUMN) == 0) {
                // there is no local update
                if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) {
                    // no update both side
                    return SYNC_ACTION_NONE;
                } else {
                    // apply remote to local
                    return SYNC_ACTION_UPDATE_LOCAL;
                }//根据本地笔记对象的LOCAL_MODIFIED_COLUMN属性值是否为0来确定是否存在本地更新。如果该属性值为0，则表示没有本地更新。
                // 如果本地笔记对象的SYNC_ID_COLUMN属性值等于当前任务对象的最后修改时间，则表示两者都没有更新
                // 返回SYNC_ACTION_NONE操作类型；否则，返回SYNC_ACTION_UPDATE_LOCAL操作类型。
            } else {
                // validate gtask id
                if (!c.getString(SqlNote.GTASK_ID_COLUMN).equals(getGid())) {
                    Log.e(TAG, "gtask id doesn't match");
                    return SYNC_ACTION_ERROR;
                }
                if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) {
                    // local modification only
                    return SYNC_ACTION_UPDATE_REMOTE;
                } else {
                    return SYNC_ACTION_UPDATE_CONFLICT;
                }//如果本地笔记对象的LOCAL_MODIFIED_COLUMN属性值不为0，则表示存在本地更新。
                // 此时，检查SqlNote.GTASK_ID_COLUMN属性值是否与当前任务对象的Google Task ID相等。
                // 如果不相等，打印一条错误日志并返回SYNC_ACTION_ERROR操作类型。
                // 接着，比较本地笔记对象的SYNC_ID_COLUMN属性值与getLastModified()方法返回值是否相等。
                // 如果相等，则表示只存在本地更新，返回SYNC_ACTION_UPDATE_REMOTE操作类型；
                // 否则，返回SYNC_ACTION_UPDATE_CONFLICT操作类型
            }
        } catch (Exception e) {
            Log.e(TAG, e.toString());
            e.printStackTrace();
        }

        return SYNC_ACTION_ERROR;
    }//如果在执行过程中发生异常，则会打印一条错误日志并返回SYNC_ACTION_ERROR操作类型。

    public boolean isWorthSaving() {
        return mMetaInfo != null || (getName() != null && getName().trim().length() > 0)
                || (getNotes() != null && getNotes().trim().length() > 0);
    }//isWorthSaving()方法用于判断当前任务对象是否值得保存，返回一个布尔值。
    //该方法会判断以下三种情况，只要其中任意一种情况成立，则返回true，表示该任务对象值得保存：
    //mMetaInfo属性不为null，即当前任务对象存在元信息。
    //getName()方法返回值不为null且去除空格后长度大于0，表示当前任务对象的名称不为空。
    //getNotes()方法返回值不为null且去除空格后长度大于0，表示当前任务对象的笔记内容不为空。
    //如果上述所有条件都不成立，则返回false，表示当前任务对象不值得保存

    public void setCompleted(boolean completed) {
        this.mCompleted = completed;
    }

    public void setNotes(String notes) {
        this.mNotes = notes;
    }

    public void setPriorSibling(Task priorSibling) {
        this.mPriorSibling = priorSibling;
    }

    public void setParent(TaskList parent) {
        this.mParent = parent;
    }

    public boolean getCompleted() {
        return this.mCompleted;
    }

    public String getNotes() {
        return this.mNotes;
    }

    public Task getPriorSibling() {
        return this.mPriorSibling;
    }

    public TaskList getParent() {
        return this.mParent;
    }

}
