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

import android.content.Context;
import android.database.Cursor;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.Data;
import android.telephony.PhoneNumberUtils;
import android.util.Log;

import java.util.HashMap;

/**
 * @Package:    net.micode.notes.data
 * @ClassName:  Contact
 * @Description:
 * Contact类是一个用于获取联系人信息的工具类，包含有一个方法getContact，通过给定的电话号码来实现获取联系人信息的功能。
 * 并且使用缓存和错误日志记录功能来提高程序的稳定性和效率。
 * @Author: Dong Jiaqi
 * @CreateDate: 12/21/2023 19:31 PM
 */
public class Contact {


    private static HashMap<String, String> sContactCache;//定义了一个静态的HashMap变量sContactCache
    private static final String TAG = "Contact";//定义了一个静态的字符串常量TAG
    private static final String CALLER_ID_SELECTION = "PHONE_NUMBERS_EQUAL(" + Phone.NUMBER
    + ",?) AND " + Data.MIMETYPE + "='" + Phone.CONTENT_ITEM_TYPE + "'"
    + " AND " + Data.RAW_CONTACT_ID + " IN "
            + "(SELECT raw_contact_id "
            + " FROM phone_lookup"
            + " WHERE min_match = '+')";
    /*
     *声明了一个字符串常量CALLER_ID_SELECTION,
     *其中使用了函数调用PHONE_NUMBERS_EQUAL用来匹配电话号码,同时还使用了条件语句限制数据类型为电话号码联系人ID.
    */

    /**
     * 该方法定义了一个getContact,通过给定的电话号码实现获取联系人信息的功能。
     * getContact中有2个输入参数分别是context和phoneNumber,其中前者是一个Context对象,提供了访问应用程序的信息和资源的环境;后者就是给定输入的电话号码
     * 有3种返回结果，其中返回sContactCache.get(phoneNumber)代表通过sContactCache这一个HashMap对象获取并且返回电话号码对应的联系人的名字
     * 返回 name则是直接返回联系人的名字;返回 null表示发生了IndexOutOfBoundsException异常；
     */
    public static String getContact(Context context, String phoneNumber) {
        if(sContactCache == null) {//确保HashMap对象不会空，始终指向一个实例化的HashMap对象
            sContactCache = new HashMap<String, String>();
        }

        if(sContactCache.containsKey(phoneNumber)) {
            return sContactCache.get(phoneNumber);
        }

        String selection = CALLER_ID_SELECTION.replace("+",
                PhoneNumberUtils.toCallerIDMinMatch(phoneNumber)); //获取电话号码的最小匹配形式，比如字母换数字，然后用replace方发将CALLER_ID_SELECTION中的"+"替换为最小匹配后的电话号码
        Cursor cursor = context.getContentResolver().query(
                Data.CONTENT_URI,
                new String [] { Phone.DISPLAY_NAME },
                selection,
                new String[] { phoneNumber },
                null);
        /*
         *context.getContentResolver()获取应用的内容解析器对象，然后调用query()方法进行查询，
         *接受了Data.CONTENT_URI等参数，其中Data.CONTENT_URI表示联系人数据的URI；
         *new String []{Phone.DISPLAY_NAME}表示联系人的显示名称；
         *selection表示查询的条件；
         *new String[] {phoneNumber}表示替换前的原始电话号码，也是查询的参数；
         *null表示没有排序方式
         */

        //如果查询结果不为空，将第一个结果的显示名称存储在缓存中，并返回该名称；否则，记录到错误日志并返回null。
        if (cursor != null && cursor.moveToFirst()) {
            /*
             * 语句块功能：是尝试获取联系人的名称，并且存储在name变量中，再将电话号码和对应联系人名字放到sContactCache缓存中去
             * 如果获取失败，发生IndexOutOfBoundsException报错，那么把错误信息按照错误级别写进日志，有利于debug的进行。
             * 为什么：通过使用缓存和try-catch-finally语句块，可以确保在获取联系人姓名时不会因为异常而导致程序崩溃，并且在结束查询后及时释放资源，提高了程序的稳定性和效率。
             * 同时错误信息被写进日志文件，可以有助于开发人员调试和故障排除时找到问题所在。
             */
            try {
                String name = cursor.getString(0);
                sContactCache.put(phoneNumber, name);
                return name;
            } catch (IndexOutOfBoundsException e) {
                Log.e(TAG, " Cursor get string error " + e.toString());//调用Log.e()方法，把错误信息写到日志。
                return null;
            } finally {
                cursor.close();
            }
        } else {
            Log.d(TAG, "No contact matched with number:" + phoneNumber);
            return null;
        }
    }
}
