/*
 * 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;

import android.database.Cursor;
import android.util.Log;

import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.gtask.exception.ActionFailureException;
import net.micode.notes.tool.GTaskStringUtils;

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

import java.util.ArrayList;


public class TaskList extends Node {
    // 定义一个 TaskList 类，继承自 Node 类

    private static final String TAG = TaskList.class.getSimpleName();
    // 定义一个 TAG 常量，类型为字符串，值为 TaskList 类的简单名称

    private int mIndex;
    // 定义一个整型变量 mIndex，用于存储任务列表的索引值

    private ArrayList<Task> mChildren;
    // 定义一个 ArrayList 类型的 mChildren 变量，用于存储该任务列表的子任务列表

    public TaskList() {
        // 默认构造方法

        super();
        // 调用父类的默认构造方法

        mChildren = new ArrayList<Task>();
        // 初始化 mChildren 变量为一个新的空的 ArrayList

        mIndex = 1;
        // 初始化 mIndex 变量为 1
    }

    public JSONObject getCreateAction(int actionId) {
        JSONObject js = new JSONObject(); // 创建一个新的 JSONObject 对象

        try {
            // action_type
            js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE,
                    GTaskStringUtils.GTASK_JSON_ACTION_TYPE_CREATE); // 添加 action_type 键值对，值为 "create"

            // action_id
            js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); // 添加 action_id 键值对，值为传入的参数 actionId

            // index
            js.put(GTaskStringUtils.GTASK_JSON_INDEX, mIndex); // 添加 index 键值对，值为成员变量 mIndex 的值

            // entity_delta
            JSONObject entity = new JSONObject(); // 创建一个新的 JSONObject 对象，用于表示实体的 delta
            entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); // 添加 name 键值对，值为 getName() 方法的返回值
            entity.put(GTaskStringUtils.GTASK_JSON_CREATOR_ID, "null"); // 添加 creator_id 键值对，值为 "null"
            entity.put(GTaskStringUtils.GTASK_JSON_ENTITY_TYPE,
                    GTaskStringUtils.GTASK_JSON_TYPE_GROUP); // 添加 entity_type 键值对，值为 "GROUP"
            js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity); // 添加 entity_delta 键值对，值为上面创建的 entity 对象

        } catch (JSONException e) { // 如果在添加键值对的过程中出现 JSONException 异常
            Log.e(TAG, e.toString()); // 打印异常堆栈跟踪信息
            e.printStackTrace();
            throw new ActionFailureException("fail to generate tasklist-create jsonobject"); // 抛出一个自定义异常 ActionFailureException，并传递一个异常信息字符串
        }

        return js; // 返回创建的 JSONObject 对象
    }

    public JSONObject getUpdateAction(int actionId) {
        JSONObject js = new JSONObject(); // 创建一个新的 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); // 添加 action_id 键值对，值为传入的参数 actionId

            // id
            js.put(GTaskStringUtils.GTASK_JSON_ID, getGid()); // 添加 id 键值对，值为调用 getGid() 方法的返回值，表示任务列表的全局唯一标识符

            // entity_delta
            JSONObject entity = new JSONObject(); // 创建一个新的 JSONObject 对象，用于表示实体的 delta
            entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); // 添加 name 键值对，值为 getName() 方法的返回值，表示任务列表的名称
            entity.put(GTaskStringUtils.GTASK_JSON_DELETED, getDeleted()); // 添加 deleted 键值对，值为 getDeleted() 方法的返回值，表示任务列表是否被删除
            js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity); // 添加 entity_delta 键值对，值为上面创建的 entity 对象

        } catch (JSONException e) { // 如果在添加键值对的过程中出现 JSONException 异常
            Log.e(TAG, e.toString()); // 打印异常堆栈跟踪信息
            e.printStackTrace();
            throw new ActionFailureException("fail to generate tasklist-update jsonobject"); // 抛出一个自定义异常 ActionFailureException，并传递一个异常信息字符串
        }

        return js; // 返回创建的 JSONObject 对象
    }

    public void setContentByRemoteJSON(JSONObject js) {
        if (js != null) { // 如果传入的 JSONObject 对象不为 null
            try {
                // id
                if (js.has(GTaskStringUtils.GTASK_JSON_ID)) { // 如果 JSONObject 对象中包含 id 键值对
                    setGid(js.getString(GTaskStringUtils.GTASK_JSON_ID)); // 将 id 键对应的值作为参数调用 setGid() 方法，将任务列表的全局唯一标识符设置为该值
                }

                // last_modified
                if (js.has(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)) { // 如果 JSONObject 对象中包含 last_modified 键值对
                    setLastModified(js.getLong(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)); // 将 last_modified 键对应的值作为参数调用 setLastModified() 方法，将任务列表的最后修改时间设置为该值
                }

                // name
                if (js.has(GTaskStringUtils.GTASK_JSON_NAME)) { // 如果 JSONObject 对象中包含 name 键值对
                    setName(js.getString(GTaskStringUtils.GTASK_JSON_NAME)); // 将 name 键对应的值作为参数调用 setName() 方法，将任务列表的名称设置为该值
                }

            } catch (JSONException e) { // 如果在读取键值对的过程中出现 JSONException 异常
                Log.e(TAG, e.toString()); // 打印异常堆栈跟踪信息
                e.printStackTrace();
                throw new ActionFailureException("fail to get tasklist content from jsonobject"); // 抛出一个自定义异常 ActionFailureException，并传递一个异常信息字符串
            }
        }
    }

    public void setContentByLocalJSON(JSONObject js) {
        if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE)) { // 如果传入的 JSONObject 对象为 null，或者它不包含 META_HEAD_NOTE 键值对
            Log.w(TAG, "setContentByLocalJSON: nothing is avaiable"); // 打印一条警告日志，表示没有可用的信息
        }

        try {
            JSONObject folder = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); // 获取 META_HEAD_NOTE 键对应的值，该值应该是一个 JSON 对象，表示笔记的元信息

            if (folder.getInt(NoteColumns.TYPE) == Notes.TYPE_FOLDER) { // 如果元信息中的 TYPE 键对应的值为 TYPE_FOLDER，表示这是一个文件夹笔记
                String name = folder.getString(NoteColumns.SNIPPET); // 获取元信息中的 SNIPPET 键对应的值，该值应该是文件夹的名称
                setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + name); // 将文件夹名称加上一个前缀字符串，作为任务列表的名称
            } else if (folder.getInt(NoteColumns.TYPE) == Notes.TYPE_SYSTEM) { // 如果元信息中的 TYPE 键对应的值为 TYPE_SYSTEM，表示这是一个系统笔记
                if (folder.getLong(NoteColumns.ID) == Notes.ID_ROOT_FOLDER) // 如果元信息中的 ID 键对应的值为 ID_ROOT_FOLDER，表示这是根文件夹笔记
                    setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT); // 将默认的任务列表名称设置为 MIUI_FOLDER_PREFFIX + FOLDER_DEFAULT
                else if (folder.getLong(NoteColumns.ID) == Notes.ID_CALL_RECORD_FOLDER) // 如果元信息中的 ID 键对应的值为 ID_CALL_RECORD_FOLDER，表示这是一条来电记录笔记
                    setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_CALL_NOTE); // 将来电记录的任务列表名称设置为 MIUI_FOLDER_PREFFIX + FOLDER_CALL_NOTE
                else
                    Log.e(TAG, "invalid system folder"); // 如果元信息中的 TYPE 和 ID 值都不符合要求，打印一条错误日志
            } else {
                Log.e(TAG, "error type"); // 如果元信息中的 TYPE 键对应的值既不是 TYPE_FOLDER，也不是 TYPE_SYSTEM，打印一条错误日志
            }
        } catch (JSONException e) { // 如果在读取元信息的过程中出现 JSONException 异常
            Log.e(TAG, e.toString()); // 打印异常堆栈跟踪信息
            e.printStackTrace();
        }
    }

    public JSONObject getLocalJSONFromContent() {
        try {
            JSONObject js = new JSONObject(); // 创建一个新的 JSONObject 对象
            JSONObject folder = new JSONObject(); // 创建一个新的 JSONObject 对象，表示笔记的元信息

            String folderName = getName(); // 获取任务列表名称
            if (getName().startsWith(GTaskStringUtils.MIUI_FOLDER_PREFFIX)) // 如果任务列表名称以 MIUI_FOLDER_PREFFIX 开头
                folderName = folderName.substring(GTaskStringUtils.MIUI_FOLDER_PREFFIX.length(), folderName.length()); // 将 MIUI_FOLDER_PREFFIX 从任务列表名称中去掉
            folder.put(NoteColumns.SNIPPET, folderName); // 将去掉 MIUI_FOLDER_PREFFIX 的任务列表名称存入元信息中的 SNIPPET 键

            if (folderName.equals(GTaskStringUtils.FOLDER_DEFAULT) || folderName.equals(GTaskStringUtils.FOLDER_CALL_NOTE)) // 如果任务列表名称是默认任务列表或来电记录任务列表
                folder.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); // 将元信息中的 TYPE 键设置为 TYPE_SYSTEM
            else
                folder.put(NoteColumns.TYPE, Notes.TYPE_FOLDER); // 否则将元信息中的 TYPE 键设置为 TYPE_FOLDER

            js.put(GTaskStringUtils.META_HEAD_NOTE, folder); // 将元信息对象存入 js 对象中，以 META_HEAD_NOTE 键为键名

            return js; // 返回创建好的 JSONObject 对象
        } catch (JSONException e) { // 如果在创建 JSONObject 对象的过程中出现异常
            Log.e(TAG, e.toString()); // 打印异常信息
            e.printStackTrace();
            return null; // 返回 null
        }
    }

    public int getSyncAction(Cursor c) {
        try {
            if (c.getInt(SqlNote.LOCAL_MODIFIED_COLUMN) == 0) { // 如果本地没有更新
                if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { // 如果本地最后一次修改时间与云端最后一次修改时间相同
                    // 两边都没有更新
                    return SYNC_ACTION_NONE;
                } else {
                    // 将云端的修改应用到本地
                    return SYNC_ACTION_UPDATE_LOCAL;
                }
            } else { // 如果本地有更新
                // 验证 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()) { // 如果本地最后一次修改时间与云端最后一次修改时间相同
                    // 只有本地修改
                    return SYNC_ACTION_UPDATE_REMOTE;
                } else {
                    // 对于文件夹冲突，只应用本地修改
                    return SYNC_ACTION_UPDATE_REMOTE;
                }
            }
        } catch (Exception e) {
            Log.e(TAG, e.toString());
            e.printStackTrace();
        }

        return SYNC_ACTION_ERROR; // 如果在执行过程中出现异常，返回 SYNC_ACTION_ERROR
    }

    public int getChildTaskCount() {
        return mChildren.size();
    }//返回一个私有成员变量mChildren中存储的子任务数量

    public boolean addChildTask(Task task) {
        boolean ret = false;
        if (task != null && !mChildren.contains(task)) { // 如果任务不为空且不包含在子任务列表中
            ret = mChildren.add(task); // 添加任务到子任务列表中
            if (ret) {
                // 需要设置前一个兄弟和父任务
                task.setPriorSibling(mChildren.isEmpty() ? null : mChildren.get(mChildren.size() - 1)); // 设置前一个兄弟，如果子任务列表为空，则前一个兄弟为 null
                task.setParent(this); // 设置父任务为当前任务
            }
        }
        return ret;
    }

    public boolean addChildTask(Task task, int index) {
        if (index < 0 || index > mChildren.size()) { // 如果索引位置小于 0 或大于子任务列表的大小
            Log.e(TAG, "add child task: invalid index"); // 打印错误日志
            return false;
        }

        int pos = mChildren.indexOf(task); // 获取任务在子任务列表中的位置
        if (task != null && pos == -1) { // 如果任务不为空且不在子任务列表中
            mChildren.add(index, task); // 在指定索引位置添加任务

            // 更新任务列表
            Task preTask = null;
            Task afterTask = null;
            if (index != 0)
                preTask = mChildren.get(index - 1);
            if (index != mChildren.size() - 1)
                afterTask = mChildren.get(index + 1);

            task.setPriorSibling(preTask); // 设置前一个兄弟任务
            if (afterTask != null)
                afterTask.setPriorSibling(task); // 设置后一个兄弟任务的前一个兄弟任务为当前任务
        }

        return true;
    }

    public boolean removeChildTask(Task task) {
        boolean ret = false;
        int index = mChildren.indexOf(task); // 获取任务在子任务列表中的位置
        if (index != -1) { // 如果任务在子任务列表中
            ret = mChildren.remove(task); // 从子任务列表中移除任务

            if (ret) {
                // 重置前一个兄弟任务和父任务
                task.setPriorSibling(null);
                task.setParent(null);

                // 更新任务列表
                if (index != mChildren.size()) {
                    mChildren.get(index).setPriorSibling(
                            index == 0 ? null : mChildren.get(index - 1));
                }
            }
        }
        return ret;
    }

    public boolean moveChildTask(Task task, int index) {
        if (index < 0 || index >= mChildren.size()) { // 如果索引位置小于 0 或大于等于子任务列表的大小
            Log.e(TAG, "move child task: invalid index"); // 打印错误日志
            return false;
        }

        int pos = mChildren.indexOf(task); // 获取任务在子任务列表中的位置
        if (pos == -1) { // 如果任务不在子任务列表中
            Log.e(TAG, "move child task: the task should in the list"); // 打印错误日志
            return false;
        }

        if (pos == index)
            return true;
        return (removeChildTask(task) && addChildTask(task, index)); // 先将任务从原位置移除，再将其移动到新位置
    }

    public Task findChildTaskByGid(String gid) {
        for (int i = 0; i < mChildren.size(); i++) { // 遍历子任务列表
            Task t = mChildren.get(i);
            if (t.getGid().equals(gid)) { // 如果找到了 GID 匹配的任务
                return t; // 返回该任务
            }
        }
        return null; // 如果没有找到匹配的任务，则返回 null
    }

    public int getChildTaskIndex(Task task) {
        return mChildren.indexOf(task); // 获取指定任务在子任务列表中的位置
    }

    public Task getChildTaskByIndex(int index) {
        if (index < 0 || index >= mChildren.size()) { // 如果索引位置小于 0 或大于等于子任务列表的大小
            Log.e(TAG, "getTaskByIndex: invalid index"); // 打印错误日志
            return null;
        }
        return mChildren.get(index); // 返回指定位置的任务
    }

    public Task getChilTaskByGid(String gid) {
        for (Task task : mChildren) { // 遍历子任务列表中的所有任务
            if (task.getGid().equals(gid)) // 如果找到了 GID 匹配的任务
                return task; // 返回该任务
        }
        return null; // 如果没有找到匹配的任务，则返回 null
    }

    public ArrayList<Task> getChildTaskList() {
        return this.mChildren; // 返回子任务列表
    }

    public void setIndex(int index) {
        this.mIndex = index; // 设置任务列表的索引位置
    }

    public int getIndex() {
        return this.mIndex; // 获取任务列表的索引位置
    }
}
