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

import android.content.Context;
import android.database.Cursor;
import android.os.Environment;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.util.Log;

import net.micode.notes.R;
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 java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;//////
//添加包。类。对象等

public class BackupUtils {
    private static final String TAG = "BackupUtils";
    // Singleton stuff
    private static BackupUtils sInstance;

    public static synchronized BackupUtils getInstance(Context context) {
        if (sInstance == null) {//如果实例为空则创建新实例
            sInstance = new BackupUtils(context);
        }//返回实例
        return sInstance;
    }

    /**
     * Following states are signs to represents backup or restore
     * status
     */
    // Currently, the sdcard is not mounted
    public static final int STATE_SD_CARD_UNMOUONTED           = 0;
    // The backup file not exist
    public static final int STATE_BACKUP_FILE_NOT_EXIST        = 1;
    // The data is not well formated, may be changed by other programs
    public static final int STATE_DATA_DESTROIED               = 2;
    // Some run-time exception which causes restore or backup fails
    public static final int STATE_SYSTEM_ERROR                 = 3;
    // Backup or restore success
    public static final int STATE_SUCCESS                      = 4;

    private TextExport mTextExport;

    private BackupUtils(Context context) {
        mTextExport = new TextExport(context);
    }

    private static boolean externalStorageAvailable() {
        return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState());
    }
//将笔记·导出文本，返回导出时的状态码
    public int exportToText() {
        return mTextExport.exportToText();
    }
//获取导出文本文件名
    public String getExportedTextFileName() {
        return mTextExport.mFileName;
    }
//获得到处文本文件目录
    public String getExportedTextFileDir() {
        return mTextExport.mFileDirectory;
    }
//负责将笔记导出为文本
    private static class TextExport {
        private static final String[] NOTE_PROJECTION = {
                NoteColumns.ID,
                NoteColumns.MODIFIED_DATE,
                NoteColumns.SNIPPET,
                NoteColumns.TYPE
        };

        private static final int NOTE_COLUMN_ID = 0;//查询结果中笔记id所在列的索引

        private static final int NOTE_COLUMN_MODIFIED_DATE = 1;//查询结果中笔记上次修改时间所在的索引

        private static final int NOTE_COLUMN_SNIPPET = 2;//查询摘要所在索引

        private static final String[] DATA_PROJECTION = {
                DataColumns.CONTENT,
                DataColumns.MIME_TYPE,
                DataColumns.DATA1,
                DataColumns.DATA2,
                DataColumns.DATA3,
                DataColumns.DATA4,
        };//定义静态不可变数据列

        private static final int DATA_COLUMN_CONTENT = 0;

        private static final int DATA_COLUMN_MIME_TYPE = 1;

        private static final int DATA_COLUMN_CALL_DATE = 2;

        private static final int DATA_COLUMN_PHONE_NUMBER = 4;//定义以上数据列MIME类，呼叫日期，号码的索引初始值

        private final String [] TEXT_FORMAT;//定义带有3个元素字符串数组TEXT_FORMAT
        private static final int FORMAT_FOLDER_NAME          = 0;//格式化后目录名称FORMAT_FOLDER_NAME= 0
        private static final int FORMAT_NOTE_DATE            = 1;
        private static final int FORMAT_NOTE_CONTENT         = 2;//格式化后的笔记内容索引为2

        private Context mContext;//声名Context类型和字符串类型
        private String mFileName;//文件名称
        private String mFileDirectory;//文件目录路径

        public TextExport(Context context) {
            TEXT_FORMAT = context.getResources().getStringArray(R.array.format_for_exported_note);//初始化文件名称变量为控字符串
            mContext = context;
            mFileName = "";
            mFileDirectory = "";
        }//初始化文件路径mFileDirectory = ""为空
//创建TextExport对象
        private String getFormat(int id) {
            return TEXT_FORMAT[id];
        }

        /**
         * Export the folder identified by folder id to text
         */
        private void exportFolderToText(String folderId, PrintStream ps) {
            // Query notes belong to this folder
            Cursor notesCursor = mContext.getContentResolver().query(Notes.CONTENT_NOTE_URI,
                    NOTE_PROJECTION, NoteColumns.PARENT_ID + "=?", new String[] {
                        folderId
                    }, null);

            if (notesCursor != null) {
                if (notesCursor.moveToFirst()) {
                    do {
                        // Print note's last modified date
                        ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format(
                                mContext.getString(R.string.format_datetime_mdhm),
                                notesCursor.getLong(NOTE_COLUMN_MODIFIED_DATE))));
                        // Query data belong to this note
                        String noteId = notesCursor.getString(NOTE_COLUMN_ID);
                        exportNoteToText(noteId, ps);
                    } while (notesCursor.moveToNext());
                }
                notesCursor.close();
            }
        }

        /**
         * Export note identified by id to a print stream
         */
        private void exportNoteToText(String noteId, PrintStream ps) {
            Cursor dataCursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI,//查询CONTENT_DATA_URI对应的数据表
                    DATA_PROJECTION, DataColumns.NOTE_ID + "=?", new String[] {
                        noteId
                    }, null);
//使用DataColumns.NOTE_ID是否为？限制查询条件，并查询noted对应的数据行
            if (dataCursor != null) {//若查询不为空，则执行以下代码：
                if (dataCursor.moveToFirst()) {//移动游标为第一条记录并循环记录
                    do {
                        String mimeType = dataCursor.getString(DATA_COLUMN_MIME_TYPE);//获取MIME_TYPE的值
                        if (DataConstants.CALL_NOTE.equals(mimeType)) {
                            // Print phone number
                            String phoneNumber = dataCursor.getString(DATA_COLUMN_PHONE_NUMBER);
                            long callDate = dataCursor.getLong(DATA_COLUMN_CALL_DATE);
                            String location = dataCursor.getString(DATA_COLUMN_CONTENT);//从记录中获取phonenumber和calldate的值并输出号码

                            if (!TextUtils.isEmpty(phoneNumber)) {//若电话号码不为空
                                ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT),
                                        phoneNumber));//输出电话号码输出到PrintStream实例PS中
                            }
                            // Print call date
                            ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), DateFormat
                                    .format(mContext.getString(R.string.format_datetime_mdhm),
                                            callDate)));//输出通话日期信息到PrintStream中
                            // Print call attachment location
                            if (!TextUtils.isEmpty(location)) {//若位置信息不为空
                                ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT),
                                        location));
                            }//输出位置信息到PrintStream实例PS中
                        } else if (DataConstants.NOTE.equals(mimeType)) {//若数据类型为Note，则执行以下语句
                            String content = dataCursor.getString(DATA_COLUMN_CONTENT);//从Cursor中获取信息
                            if (!TextUtils.isEmpty(content)) {
                                ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT),
                                        content));//将内容信息格式化输出rintStream实例PS中
                            }
                        }
                    } while (dataCursor.moveToNext());
                }
                dataCursor.close();
            }
            // print a line separator between note
            try {
                ps.write(new byte[] {
                        Character.LINE_SEPARATOR, Character.LETTER_NUMBER
                });
            } catch (IOException e) {//若出现IO异常，则廖永Log类的e（）方法将异常信息输出日志中
                Log.e(TAG, e.toString());
            }
        }
//使用rintStream实例PS的write（）方法写入字节数组到输出流中，包括换行和字母数字
        /**
         * Note will be exported as text which is user readable
         */
        public int exportToText() {//定义exportToText()，返回值类型为整型
            if (!externalStorageAvailable()) {
                Log.d(TAG, "Media was not mounted");
                return STATE_SD_CARD_UNMOUONTED;
            }
//若外部存储不可用，输出日记信息，返回表示Media was not mounted
            PrintStream ps = getExportToTextPrintStream();//获取打印输出
            if (ps == null) {
                Log.e(TAG, "get print stream error");
                return STATE_SYSTEM_ERROR;
            }//若获取不到打印输出，输出get print stream error"，并返回错误
            // First export folder and its notes
            Cursor folderCursor = mContext.getContentResolver().query(//声名Cursor类的变量folder，存储调查结果
                    Notes.CONTENT_NOTE_URI,
                    NOTE_PROJECTION,//调用getContentResolver()，查询目标为Note表中数据和NOTE_PROJECTION指定列
                    "(" + NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + " AND "
                            + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + ") OR "
                            + NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER, null, null);
//限定查询条件为文件夹且父类文件夹不是回收站
            if (folderCursor != null) {//判断folder是否为空
                if (folderCursor.moveToFirst()) {
                    do {
                        // Print folder's name
                        String folderName = "";//遍历查询文件夹中所有文件夹记录
                        if(folderCursor.getLong(NOTE_COLUMN_ID) == Notes.ID_CALL_RECORD_FOLDER) {
                            folderName = mContext.getString(R.string.call_record_folder_name);//若记录对应通话记录文件夹，给folder赋值Notes.ID_CALL_RECORD_FOLDER
                        } else {//否则folder赋值为记录的摘要字段值
                            folderName = folderCursor.getString(NOTE_COLUMN_SNIPPET);
                        }
                        if (!TextUtils.isEmpty(folderName)) {
                            ps.println(String.format(getFormat(FORMAT_FOLDER_NAME), folderName));
                        }
                        String folderId = folderCursor.getString(NOTE_COLUMN_ID);//获取当前记录对应文件夹id并赋值给folder
                        exportFolderToText(folderId, ps);
                    } while (folderCursor.moveToNext());//将当前记录文件夹导出为文本形式，结果写入PS中
                }
                folderCursor.close();
            }

            // Export notes in root's folder
            Cursor noteCursor = mContext.getContentResolver().query(//通过getContentResolver()获取resolver对象，并调用querty方法查询
                    Notes.CONTENT_NOTE_URI,//查询URI
                    NOTE_PROJECTION,//查询列
                    NoteColumns.TYPE + "=" + +Notes.TYPE_NOTE + " AND " + NoteColumns.PARENT_ID
                            + "=0", null, null);//限制查询类型为Notes.TYPE_NOTE，且父id为0的note记录

            if (noteCursor != null) {
                if (noteCursor.moveToFirst()) {//将游标移到查询结果的第一条记录
                    do {
                        ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format(
                                mContext.getString(R.string.format_datetime_mdhm),
                                noteCursor.getLong(NOTE_COLUMN_MODIFIED_DATE))));//根据时间格式化字符串的时间戳，将时间转换成字符串格式
                        // Query data belong to this note
                        String noteId = noteCursor.getString(NOTE_COLUMN_ID);
                        exportNoteToText(noteId, ps);
                    } while (noteCursor.moveToNext());
                }//或i去当前Note记录的id，吧id对应数据写入输出流中
                noteCursor.close();
            }
            ps.close();//关闭游标

            return STATE_SUCCESS;
        }

        /**
         * Get a print stream pointed to the file {@generateExportedTextFile}
         */
        private PrintStream getExportToTextPrintStream() {
            File file = generateFileMountedOnSDcard(mContext, R.string.file_path,//使用getExportToTextPrintStream()再sd卡上生成指定名称和路径的文本文件
                    R.string.file_name_txt_format);
            if (file == null) {
                Log.e(TAG, "create file to exported failed");
                return null;//若生成文件失败，返回null，再logcat输出错误信息
            }
            mFileName = file.getName();
            mFileDirectory = mContext.getString(R.string.file_path);//将成功创建文件保存为FoleNAMe，路径柏村委mFileDiectory
            PrintStream ps = null;
            try {
                FileOutputStream fos = new FileOutputStream(file);
                ps = new PrintStream(fos);//创建对象fos，用于写入文件
            } catch (FileNotFoundException e) {//使用fos创建PrintStream对象PS，一边向文件夹写入数据
                e.printStackTrace();
                return null;//若无法找到写入目标文件，再logcat输出错误信息，并返回null
            } catch (NullPointerException e) {
                e.printStackTrace();
                return null;
            }
            return ps;
        }
    }

    /**
     * Generate the text file to store imported data
     */
    private static File generateFileMountedOnSDcard(Context context, int filePathResId, int fileNameFormatResId) {
        StringBuilder sb = new StringBuilder();
        sb.append(Environment.getExternalStorageDirectory());//再strtingbuilder中添加SD卡根目录路径
        sb.append(context.getString(filePathResId));
        File filedir = new File(sb.toString());//添加文件路径字符串资源id，并返回file对象表示该目录
        sb.append(context.getString(
                fileNameFormatResId,
                DateFormat.format(context.getString(R.string.format_date_ymd),
                        System.currentTimeMillis())));//再StringBuilder添加文件名格式字符串资源id，并使用dateFormat替换为当前日期格式化字符串
        File file = new File(sb.toString());
//返回表示最终创建的文件夹对象
        try {
            if (!filedir.exists()) {
                filedir.mkdir();
            }//判断指定目录是否存在，若不存在创建
            if (!file.exists()) {
                file.createNewFile();
            }//判断指定目录是否存在，若不存在创建
            return file;//返回创建对象
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }//如果不能在指定目录下创建新的文件，则抛出IOException异常

        return null;
    }
}


