/*
//单个便签项
 * 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.model;//设置包的名称
import android.content.ContentProviderOperation; //批量更新、插入、删除便签数据
import android.content.ContentProviderResult; //操作的结果
import android.content.ContentUris; //用于添加或修改uri后面的ID
import android.content.ContentValues; //设置存储基本数据类型的存储机制
import android.content.Context; //获取调用者的调用内容
import android.content.OperationApplicationException; //操作应用数据容错
import android.net.Uri; //访问uri相关操作
import android.os.RemoteException; //处理远程异常
import android.util.Log; //输出工作日志

import net.micode.notes.data.Notes; //处理小米便签某些属性的操作
import net.micode.notes.data.Notes.CallNote; //对小米便签数据处理的调用
import net.micode.notes.data.Notes.DataColumns; //数据栏操作处理
import net.micode.notes.data.Notes.NoteColumns; //笔记栏操作处理
import net.micode.notes.data.Notes.TextNote; //文本操作处理

import java.util.ArrayList; //导入Java,Util,ArrayList


public class Note { //定义note类，处理单个便签
    private ContentValues mNoteDiffValues; //声明一个ContentValues变量，用于存储与上次保存时修改的内容
    private NoteData mNoteData; //申明一个NoteData变量，存储note的一些数据
    private static final String TAG = "Note"; //设置软件标签
    /**
     * Create a new note id for adding a new note to databases //创建一个新的便签id，用于将新便签加入数据库
     */
    public static synchronized long getNewNoteId(Context context, long folderId) { //获取新建便签的编号
        // Create a new note in the database
        ContentValues values = new ContentValues(); //在数据库中创建一个新便签文件
        long createdTime = System.currentTimeMillis(); //记录便签创建时间
        values.put(NoteColumns.CREATED_DATE, createdTime); //将创建时间与便签的创建时间、修改时间相匹配，放入数据库
        values.put(NoteColumns.MODIFIED_DATE, createdTime); //修改时间为系统当前时间
        values.put(NoteColumns.TYPE, Notes.TYPE_NOTE); //设定类型为便签类型
        values.put(NoteColumns.LOCAL_MODIFIED,1);  //设定局部修改的初值为1
        values.put(NoteColumns.PARENT_ID, folderId); //将数据写入数据库表格
        Uri uri = context.getContentResolver().insert(Notes.CONTENT_NOTE_URI, values); //得到便签ID，并处理一些异常ID

        long noteId = 0; //设置便签ID初值为0
        try { //获取id并且检测是否异常，如果异常则报错
            noteId = Long.valueOf(uri.getPathSegments().get(1)); //获取路径部分赋值为1时的值，转换成long类型赋给noteId
        } catch (NumberFormatException e) { // 异常处理提示
            Log.e(TAG, "Get note id error :" + e.toString()); //获取id错误
            noteId = 0; //将便签的ID置为0
        }
        if (noteId == -1) { // 错误ID的异常处理
            throw new IllegalStateException("Wrong note id:" + noteId); //非法状态时返回出错便签编号
        }
        return noteId; //若没有异常，那么返回这个id
    }

    public Note() { //构造Note,实例化note数据
        mNoteDiffValues = new ContentValues(); //存储便签属性
        mNoteData = new NoteData(); //存储便签的内容
    }

    public void setNoteValue(String key, String value) { //设置数据库表格的标签属性数据
        mNoteDiffValues.put(key, value); //设置key值和对应的value值
        mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); //修改之后标志为1
        mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); //设置修改时间为系统当前时间
    }

    public void setTextData(String key, String value) { //设置数据库表格的标签文本内容的数据
        mNoteData.setTextData(key, value); //写入key和value值
    }

    public void setTextDataId(long id) { //设置文本数据的ID
        mNoteData.setTextDataId(id); //获取文本id
    } 

    public long getTextDataId() { //获取文本数据ID
        return mNoteData.mTextDataId; //返回文本数据的ID号
    }

    public void setCallDataId(long id) { //设置电话号码数据的ID
        mNoteData.setCallDataId(id);
    }

    public void setCallData(String key, String value) { //设置数据库表格文件中电话号码数据对应的键值和数据
        mNoteData.setCallData(key, value); //设置key，value值

    public boolean isLocalModified() { //根据属性特征值判定便签是否被本地修改
        return mNoteDiffValues.size() > 0 || mNoteData.isLocalModified(); //相较上次存在修改size()>0
    }

    public boolean syncNote(Context context, long noteId) { //判断便签是否同步
        if (noteId <= 0) { //便签ID不合法时抛出异常
            throw new IllegalArgumentException("Wrong note id:" + noteId); //抛出异常，弹出对话框错误ID并显示错误ID号
        }

        if (!isLocalModified()) { //如果本地没有发现修改，直接返回1，指示已经同步到数据库中
            return true;
        }

        /**
         * In theory, once data changed, the note should be updated on {@link NoteColumns#LOCAL_MODIFIED} and
         * {@link NoteColumns#MODIFIED_DATE}. For data safety, though update note fails, we also update the
         * note data info
         */
        if (context.getContentResolver().update( //发现更新错误时，及时反馈错误信息
                ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), mNoteDiffValues, null,
                null) == 0) {
            Log.e(TAG, "Update note error, should not happen"); //标志更新失败
            // Do not return, fall through
        }
        mNoteDiffValues.clear(); //清除修改的版本的属性

        if (mNoteData.isLocalModified() //.判断数据是否同步
                && (mNoteData.pushIntoContentResolver(context, noteId) == null)) {  //如果上下文传输失败，内容与ID为空，则返回false
            return false; //失败，返回false
        }

        return true; //成功则返回true
    }

    private class NoteData { //定义一个基本的便签内容的数据类，主要包含文本数据和电话号码数据
        private long mTextDataId; //文本数据id

        private ContentValues mTextDataValues; //.文本数据内容

        private long mCallDataId; //电话数据id

        private ContentValues mCallDataValues; //便签的电话号码数据

        private static final String TAG = "NoteData"; //默认构造函数

        public NoteData() { // NoteData的构造函数，初始化四个变量
            mTextDataValues = new ContentValues(); //初始化一个文本数据，储存文本
            mCallDataValues = new ContentValues(); //初始化一个电话号码数据，为了储存电话号码
            mTextDataId = 0; //初始化文本数据ID，置为0
            mCallDataId = 0;  //初始化电话号码ID，置为0
        }

        boolean isLocalModified() { //判断文本数据是否改变，是返回true，否返回false
            return mTextDataValues.size() > 0 || mCallDataValues.size() > 0;
        }

        void setTextDataId(long id) { //设置文本数据ID号
            if(id <= 0) { // 对textDataId设置，但是不允许ID小于等于0
                throw new IllegalArgumentException("Text data id should larger than 0"); //ID号默认大于0
            }
            mTextDataId = id; //设置电话号码数据ID号
        }

        void setCallDataId(long id) {  // 设置呼叫数据ID，若传入参数小于0，则抛出参数错误的异常
            if (id <= 0) {
                throw new IllegalArgumentException("Call data id should larger than 0");  //.电话号码数据ID应大于0
            }
            mCallDataId = id; //设置电话号码的属性值
        }

        void setCallData(String key, String value) { //设置电话号码数据内容，并且保存修改时间
            mCallDataValues.put(key, value); //写入内容数据
            mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); //设置修改时间为系统当前时间
            mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis());
        }

        void setTextData(String key, String value) { //设置数据库中的文本数据
            mTextDataValues.put(key, value); //写入参数key，value
            mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); //设置属性值为1
            mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); //设置修改时间为当前系统时间
        }

        Uri pushIntoContentResolver(Context context, long noteId) { //使用uri将数据添加到数据库
            /**
             * Check for safety
             */
            if (noteId <= 0) { //.判断ID是否非法
                throw new IllegalArgumentException("Wrong note id:" + noteId); //如果ID非法抛出异常，显示错误ID
            }

            ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
            ContentProviderOperation.Builder builder = null; //数据库的操作列表
 
            if(mTextDataValues.size() > 0) { //将文本的数据存入DataColums的数据结构中
                mTextDataValues.put(DataColumns.NOTE_ID, noteId); //将文本数据ID传入文本数据库
                if (mTextDataId == 0) { //文本数据ID为零，则此ID是新建默认的ID
                    mTextDataValues.put(DataColumns.MIME_TYPE, TextNote.CONTENT_ITEM_TYPE); //写入拓展类型
                    Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI,
                            mTextDataValues); //uri内容接收器插入文本数据库，放在Notes.CONTENT_DATA_URI的路径下
                    try { // 尝试设置文本数据ID
                        setTextDataId(Long.valueOf(uri.getPathSegments().get(1)));
                    } catch (NumberFormatException e) {
                        Log.e(TAG, "Insert new text data fail with noteId" + noteId); // 插入数据失败
                        mTextDataValues.clear();  //将电话数据加入DataColums的数据结构中，并加入数据库
                        return null;
                    }
                } else {
                    builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId( //更新builder对象，追加uri数据和文本ID
                            Notes.CONTENT_DATA_URI, mTextDataId)); //将uri和id合并后，更新
                    builder.withValues(mTextDataValues); //操作列表里添加build操作
                    operationList.add(builder.build());
                }
                mTextDataValues.clear(); //同步完毕之后，把更改的版本删除
            }

            if(mCallDataValues.size() > 0) {
                mCallDataValues.put(DataColumns.NOTE_ID, noteId); // 写入noteID
                if (mCallDataId == 0) { //将电话ID设置为uri提供的ID
                    mCallDataValues.put(DataColumns.MIME_TYPE, CallNote.CONTENT_ITEM_TYPE);
                    Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI,
                            mCallDataValues);
                    try { //异常检测，出现异常则清空电话号码的ID
                        setCallDataId(Long.valueOf(uri.getPathSegments().get(1)));
                    } catch (NumberFormatException e) {
                        Log.e(TAG, "Insert new call data fail with noteId" + noteId); //插入新的电话号码数据时出错
                        mCallDataValues.clear();
                        return null;
                    }
                } else { //当电话号码不为新建时，更新电话号码ID
                    builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId(
                            Notes.CONTENT_DATA_URI, mCallDataId));
                    builder.withValues(mCallDataValues);
                    operationList.add(builder.build()); //存储过程中的异常处理
                }
                mCallDataValues.clear();
            }

            if (operationList.size() > 0) { //存储过程中如果遇到异常，通过如下进行处理
                try {
                    ContentProviderResult[] results = context.getContentResolver().applyBatch( //Android源码中对通讯录的操作，应用端使用ContentProvider提供的applyBatch，进行批量处理通讯录的联系人入库
                            Notes.AUTHORITY, operationList); 
                    return (results == null || results.length == 0 || results[0] == null) ? null //完成后返回一个URI
                            : ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId);
                } catch (RemoteException e) { //异常检测，若出现异常则写回日志
                    Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); // 异常日志
                    return null;
                } catch (OperationApplicationException e) {
                    Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); // 异常日志
                    return null;
                }
            }
            return null;
        }
    }
}
