package net.micode.notes.data;


import android.app.SearchManager;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Intent;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;

import net.micode.notes.R;
import net.micode.notes.data.Notes.DataColumns;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.data.NotesDatabaseHelper.TABLE;


public class NotesProvider extends ContentProvider {
//    Android 应用程序中的一部分：内容提供者（ContentProvider）。
//    内容提供者是 Android 四大组件之一，它允许应用程序之间共享数据。

    //概述：
    //NotesProvider的主要功能是作为一个内容提供者，为其他应用程序或组件提供对“Notes”数据的访问。
    //它允许其他应用程序查询、插入、更新或删除标签数据。
    //通过URI匹配，NotesProvider能够区分对哪种数据类型的请求（例如，单独的标签、标签的数据、文件夹操作等），并执行相应的操作。

    //用于匹配不同URI的UriMatcher对象，通常用于解析传入的URI，并确定应该执行哪种操作。
    private static final UriMatcher mMatcher;

    //NotesDatabaseHelper实类，用来操作SQLite数据库，负责创建、更新和查询数据库。
    private NotesDatabaseHelper mHelper;

    //标签，输出日志时用来表示是该类发出的消息
    private static final String TAG = "NotesProvider";

    //6个URI的匹配码，用于区分不同的URI类型
    private static final int URI_NOTE            = 1;
    private static final int URI_NOTE_ITEM       = 2;
    private static final int URI_DATA            = 3;
    private static final int URI_DATA_ITEM       = 4;

    private static final int URI_SEARCH          = 5;
    private static final int URI_SEARCH_SUGGEST  = 6;

    //进一步定义了URI匹配规则和搜索查询的投影
    //功能概述：
    //初始化了一个UriMatcher对象mMatcher，并添加了一系列的URI匹配规则。
    //解读：
    static {
        //创建了一个UriMatcher实例，并设置默认匹配码为NO_MATCH，表示如果没有任何URI匹配，则返回这个码。
        mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        //添加规则，当URI的authority为Notes.AUTHORITY，路径为note时，返回匹配码URI_NOTE。
        mMatcher.addURI(Notes.AUTHORITY, "note", URI_NOTE);
        //添加规则，当URI的authority为Notes.AUTHORITY，路径为note/后跟一个数字（#代表数字）时，返回匹配码URI_NOTE_ITEM。
        mMatcher.addURI(Notes.AUTHORITY, "note/#", URI_NOTE_ITEM);
        //和上面两句同理，但用于匹配数据相关的URI
        mMatcher.addURI(Notes.AUTHORITY, "data", URI_DATA);
        mMatcher.addURI(Notes.AUTHORITY, "data/#", URI_DATA_ITEM);
        //用于匹配搜索相关的URI
        mMatcher.addURI(Notes.AUTHORITY, "search", URI_SEARCH);
        //这两行用于匹配搜索建议相关的URI
        mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, URI_SEARCH_SUGGEST);
        mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", URI_SEARCH_SUGGEST);
    }

    /**
     * x'0A' represents the '\n' character in sqlite. For title and content in the search result,
     * we will trim '\n' and white space in order to show more information.
     */
    //功能概述：
    //一个 SQL 查询的投影部分，用于定义查询返回的结果集中应该包含哪些列。
    //解读：（每行对应）
    //返回笔记的 ID。
    //笔记的 ID 也被重命名为 SUGGEST_COLUMN_INTENT_EXTRA_DATA，这通常用于 Android 的搜索建议中，作为传递给相关 Intent 的额外数据。
    //对 SNIPPET 列的处理：首先使用 REPLACE 函数将 x'0A'（即换行符 \n）替换为空字符串，然后使用 TRIM 函数删除前后的空白字符，处理后的结果分别重命名为 SUGGEST_COLUMN_TEXT_1
    //对 SNIPPET 列的处理：首先使用 REPLACE 函数将 x'0A'（即换行符 \n）替换为空字符串，然后使用 TRIM 函数删除前后的空白字符，处理后的结果分别重命名为 SUGGEST_COLUMN_TEXT_2
    //返回一个用于搜索建议图标的资源 ID，并命名为 SUGGEST_COLUMN_ICON_1。
    //返回一个固定的 Intent 动作 ACTION_VIEW，并命名为 SUGGEST_COLUMN_INTENT_ACTION。
    //返回一个内容类型，并命名为 SUGGEST_COLUMN_INTENT_DATA。
    private static final String NOTES_SEARCH_PROJECTION = NoteColumns.ID + "," //返回笔记的 ID
            + NoteColumns.ID + " AS " + SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA + ","
            + "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_1 + ","
            + "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_2 + ","
            + R.drawable.search_result + " AS " + SearchManager.SUGGEST_COLUMN_ICON_1 + ","
            + "'" + Intent.ACTION_VIEW + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_ACTION + ","
            + "'" + Notes.TextNote.CONTENT_TYPE + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_DATA;

    //功能概述：
    //完整的 SQL 查询语句，用于从 TABLE.NOTE 表中检索信息
    //解读：
    // 使用上面定义的投影来选择数据。
    // 并指定从哪个表中选择数据。
    //WHERE子句包含三个条件：
    // ①搜索 SNIPPET 列中包含特定模式的行（? 是一个占位符，实际查询时会用具体的值替换）。
    // ②父ID不为回收站的ID：排除那些父 ID 为回收站的行。
    // ③只选择类型为note（标签）的行。
    private static String NOTES_SNIPPET_SEARCH_QUERY = "SELECT " + NOTES_SEARCH_PROJECTION
            + " FROM " + TABLE.NOTE
            + " WHERE " + NoteColumns.SNIPPET + " LIKE ?"
            + " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER
            + " AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE;

    //重写onCreate方法：
    //getContext() 方法被调用以获取当前组件的上下文（Context），以便 NotesDatabaseHelper 能够访问应用程序的资源和其他功能
    //mHelper用于存储从 NotesDatabaseHelper.getInstance 方法返回的实例。这样，该实例就可以在整个组件的其他方法中被访问和使用。
    @Override
    public boolean onCreate() {
        mHelper = NotesDatabaseHelper.getInstance(getContext());
        return true;
    }

    //功能：查询数据
    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
                        String sortOrder) {
        //初始化变量：
        //Cursor对象 c，用来存储查询结果
        //使用 NotesDatabaseHelper 的实例 mHelper来获取一个可读的数据库实例
        //定义一个字符串id，用来存储从URI中解析出的ID
        Cursor c = null;
        SQLiteDatabase db = mHelper.getReadableDatabase();
        String id = null;

        //根据匹配不同的URI来进行不同的查询
        switch (mMatcher.match(uri)) {
            //  URI_NOTE：查询整个 NOTE 表。
            //  URI_NOTE_ITEM：查询 NOTE 表中的特定项。ID 从 URI 的路径段中获取，并添加到查询条件中。
            //  URI_DATA：查询整个 DATA 表。
            //  URI_DATA_ITEM：查询 DATA 表中的特定项。ID 的获取和处理方式与 URI_NOTE_ITEM 相同。
            case URI_NOTE:
                c = db.query(TABLE.NOTE, projection, selection, selectionArgs, null, null,
                        sortOrder);
                break;
            case URI_NOTE_ITEM:
                id = uri.getPathSegments().get(1);
                c = db.query(TABLE.NOTE, projection, NoteColumns.ID + "=" + id
                        + parseSelection(selection), selectionArgs, null, null, sortOrder);
                break;
            case URI_DATA:
                c = db.query(TABLE.DATA, projection, selection, selectionArgs, null, null,
                        sortOrder);
                break;
            case URI_DATA_ITEM:
                id = uri.getPathSegments().get(1);
                c = db.query(TABLE.DATA, projection, DataColumns.ID + "=" + id
                        + parseSelection(selection), selectionArgs, null, null, sortOrder);
                break;

            //URI_SEARCH 和 URI_SEARCH_SUGGEST：处理搜索查询。
            //  代码首先检查是否提供了不应与搜索查询一起使用的参数（如 sortOrder, selection, selectionArgs, 或 projection）。
            //  如果提供了这些参数，则抛出一个 IllegalArgumentException。
            //  根据 URI 类型，从 URI 的路径段或查询参数中获取搜索字符串 searchString。
            //  如果 searchString 为空或无效，则返回 null，表示没有搜索结果。
            case URI_SEARCH:
            case URI_SEARCH_SUGGEST:
                if (sortOrder != null || projection != null) {
                    throw new IllegalArgumentException(
                            "do not specify sortOrder, selection, selectionArgs, or projection" + "with this query");
                }

                String searchString = null;
                if (mMatcher.match(uri) == URI_SEARCH_SUGGEST) {
                    if (uri.getPathSegments().size() > 1) {
                        searchString = uri.getPathSegments().get(1);
                    }
                } else {
                    searchString = uri.getQueryParameter("pattern");
                }

                if (TextUtils.isEmpty(searchString)) {
                    return null;
                }

                //字符串格式化：格式化后的字符串就会是 "%s%"，即包含s是任何文本
                //然后执行原始SQL查询
                try {
                    searchString = String.format("%%%s%%", searchString);
                    c = db.rawQuery(NOTES_SNIPPET_SEARCH_QUERY,
                            new String[] { searchString });
                } catch (IllegalStateException ex) {
                    Log.e(TAG, "got exception: " + ex.toString());
                }
                break;

            //未知URI处理：
            default:
                throw new IllegalArgumentException("Unknown URI " + uri);
        }
        //如果查询结果不为空（即 Cursor 对象 c 不是 null），则为其设置一个通知 URI。
        //这意味着当与这个 URI 关联的数据发生变化时，任何注册了监听这个 URI 的 ContentObserver 都会被通知。
        if (c != null) {
            c.setNotificationUri(getContext().getContentResolver(), uri);
        }
        return c;
    }

    //功能：插入数据
    //参数：Uri 用来标识要插入数据的表，ContentValues对象包含要插入的键值对
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        //获取数据库
        //三个长整型变量，分别用来存储数据项ID、便签ID 和插入行的ID
        SQLiteDatabase db = mHelper.getWritableDatabase();
        long dataId = 0, noteId = 0, insertedId = 0;

        //对于 URI_NOTE，将values插入到 TABLE.NOTE 表中，并返回插入行的 ID。
        //对于 URI_DATA，首先检查values是否包含 DataColumns.NOTE_ID，如果包含，则获取其值。如果不包含，记录一条日志信息。然后，将 values 插入到 TABLE.DATA 表中，并返回插入行的 ID。
        //如果 uri 不是已知的 URI 类型，则抛出一个 IllegalArgumentException。
        switch (mMatcher.match(uri)) {
            case URI_NOTE:
                insertedId = noteId = db.insert(TABLE.NOTE, null, values);
                break;
            case URI_DATA:
                if (values.containsKey(DataColumns.NOTE_ID)) {
                    noteId = values.getAsLong(DataColumns.NOTE_ID);
                } else {
                    Log.d(TAG, "Wrong data format without note id:" + values.toString());
                }
                insertedId = dataId = db.insert(TABLE.DATA, null, values);
                break;
            default:
                throw new IllegalArgumentException("Unknown URI " + uri);
        }

        //功能：通知变化
        //如果noteId 或 dataId 大于 0（即成功插入了数据），则使用 ContentResolver 的 notifyChange 方法通知监听这些 URI 的观察者，告知数据已经改变。
        //ContentUris.withAppendedId 方法用于在基本 URI 后面追加一个 ID，形成完整的 URI。
        // Notify the note uri
        if (noteId > 0) {
            getContext().getContentResolver().notifyChange(
                    ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null);
        }

        // Notify the data uri
        if (dataId > 0) {
            getContext().getContentResolver().notifyChange(
                    ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), null);
        }

        //返回包含新插入数据项ID 的 Uri。允许调用者知道新插入的数据项的位置
        return ContentUris.withAppendedId(uri, insertedId);
    }

    //功能：删除数据项
    //参数：uri：标识要删除数据的表或数据项。  selection：一个可选的 WHERE 子句，用于指定删除条件。  selectionArgs：一个可选的字符串数组，用于替换 selection 中的占位符
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        //count：记录被删除的行数。
        //id：用于存储从 URI 中解析出的数据项 ID。
        //db：可写的数据库对象，用于执行删除操作。
        //deleteData：一个布尔值，用于标记是否删除了 DATA 表中的数据。
        int count = 0;
        String id = null;
        SQLiteDatabase db = mHelper.getWritableDatabase();
        boolean deleteData = false;

        switch (mMatcher.match(uri)) {
            //URI_NOTE:       修改 selection 语句：确保只删除 ID 大于 0 的笔记。然后执行删除操作并返回被删除的行数。
            //URI_NOTE_ITEM:  从 URI 中解析出 ID。检查 ID 是否小于等于 0，如果是，则不执行删除操作；否则执行删除操作并返回被删除的行数
            //URI_DATA：       执行删除操作并返回被删除的行数。设置 deleteData 为 true，表示删除了 DATA 表中的数据。
            //URI_DATA_ITEM：  先从 URI 中解析出 ID，然后执行删除操作并返回被删除的行数，并设置 deleteData 为 true，表示删除了 DATA 表中的数据。
            case URI_NOTE:
                selection = "(" + selection + ") AND " + NoteColumns.ID + ">0 ";
                count = db.delete(TABLE.NOTE, selection, selectionArgs);
                break;
            case URI_NOTE_ITEM:
                id = uri.getPathSegments().get(1);
                /**
                 * ID that smaller than 0 is system folder which is not allowed to
                 * trash
                 */
                long noteId = Long.valueOf(id);
                if (noteId <= 0) {
                    break;
                }
                count = db.delete(TABLE.NOTE,
                        NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs);
                break;
            case URI_DATA:
                count = db.delete(TABLE.DATA, selection, selectionArgs);
                deleteData = true;
                break;
            case URI_DATA_ITEM:
                id = uri.getPathSegments().get(1);
                count = db.delete(TABLE.DATA,
                        DataColumns.ID + "=" + id + parseSelection(selection), selectionArgs);
                deleteData = true;
                break;
            default:
                throw new IllegalArgumentException("Unknown URI " + uri);
        }

        //如果 count 大于 0，说明有数据被删除。
        //如果 deleteData 为 true，则通知监听 Notes.CONTENT_NOTE_URI 的观察者，数据已改变。
        //通知监听传入 uri 的观察者数据已改变。
        if (count > 0) {
            if (deleteData) {
                getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null);
            }
            getContext().getContentResolver().notifyChange(uri, null);
        }

        return count;
    }

    //功能：更新数据库的数据
    //参数：uri：标识要更新数据的表或数据项。  values：一个包含新值的键值对集合。
    //     selection：一个可选的 WHERE 子句，用于指定更新条件。  selectionArgs：一个可选的字符串数组，用于替换 selection 中的占位符。
    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        //count：记录被更新的行数。
        //id：用于存储从 URI 中解析出的数据项 ID。
        //db：可写的 SQLite 数据库对象，用于执行更新操作。
        //updateData：用于标记是否更新了 data 表中的数据。
        int count = 0;
        String id = null;
        SQLiteDatabase db = mHelper.getWritableDatabase();
        boolean updateData = false;

        switch (mMatcher.match(uri)) {
            //URI_NOTE：调用 increaseNoteVersion 方法（用于增加便签版本），然后在note表执行更新操作并返回被更新的行数。
            //URI_NOTE_ITEM：从 URI 中解析出 ID，并调用 increaseNoteVersion 方法，传入解析出的 ID，最后在note表执行更新操作并返回被更新的行数。
            //URI_DATA：在data表执行更新操作并返回被更新的行数。设置 updateData 为 true，表示更新了 DATA 表中的数据。
            //URI_DATA_ITEM：从 URI 中解析出 ID。执行更新操作并返回被更新的行数。置 updateData 为 true，表示更新了 DATA 表中的数据。
            case URI_NOTE:
                increaseNoteVersion(-1, selection, selectionArgs);
                count = db.update(TABLE.NOTE, values, selection, selectionArgs);
                break;
            case URI_NOTE_ITEM:
                id = uri.getPathSegments().get(1);
                increaseNoteVersion(Long.valueOf(id), selection, selectionArgs);
                count = db.update(TABLE.NOTE, values, NoteColumns.ID + "=" + id
                        + parseSelection(selection), selectionArgs);
                break;
            case URI_DATA:
                count = db.update(TABLE.DATA, values, selection, selectionArgs);
                updateData = true;
                break;
            case URI_DATA_ITEM:
                id = uri.getPathSegments().get(1);
                count = db.update(TABLE.DATA, values, DataColumns.ID + "=" + id
                        + parseSelection(selection), selectionArgs);
                updateData = true;
                break;
            default:
                throw new IllegalArgumentException("Unknown URI " + uri);
        }

        //如果 count 大于 0，说明有数据被更新。
        //如果 updateData 为 true，则通知监听 Notes.CONTENT_NOTE_URI 的观察者数据已改变。
        //通知监听传入 uri 的观察者数据已改变。
        if (count > 0) {
            if (updateData) {
                getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null);
            }
            getContext().getContentResolver().notifyChange(uri, null);
        }
        return count;
    }

    //解析传入的条件语句：一个 SQL WHERE 子句的一部分
    private String parseSelection(String selection) {
        return (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : "");
    }

    //更新note表的version列，将其值增加 1。
    private void increaseNoteVersion(long id, String selection, String[] selectionArgs) {
        StringBuilder sql = new StringBuilder(120);
        sql.append("UPDATE ");
        sql.append(TABLE.NOTE);
        sql.append(" SET ");
        sql.append(NoteColumns.VERSION);
        sql.append("=" + NoteColumns.VERSION + "+1 ");

        if (id > 0 || !TextUtils.isEmpty(selection)) {
            sql.append(" WHERE ");
        }
        if (id > 0) {
            sql.append(NoteColumns.ID + "=" + String.valueOf(id));
        }
        if (!TextUtils.isEmpty(selection)) {
            String selectString = id > 0 ? parseSelection(selection) : selection;
            for (String args : selectionArgs) {
                selectString = selectString.replaceFirst("\\?", args);
            }
            sql.append(selectString);
        }

        mHelper.getWritableDatabase().execSQL(sql.toString());
    }

    @Override
    public String getType(Uri uri) {
        // TODO Auto-generated method stub
        return null;
    }

}