package com.secspace.lib.common.utils;

/**
 * Created by SKR-CYW on 2017/7/13.
 */

import android.content.ContentResolver;
import android.content.Context;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.provider.MediaStore;
import android.support.annotation.NonNull;
import android.util.Log;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

/**
 * @author hyw
 * @since 2017/3/9
 */
public class FileCryptUtil {

    private static final String TAG = "FileEncryptUtil";

    public interface ProgressListener {
        void onProgress(int length);
    }

    //秘钥算法
    private static final String KEY_ALGORITHM = "AES";
    //加密算法：algorithm/mode/padding 算法/工作模式/填充模式
    private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";
    // 分组长度固定为128比特，密钥长度只有128、192、和256比特三种。
    private static final String KEY = "M2D0AfMqvRvv7Sum";

    /**
     * 文件进行加密并保存加密后的文件到指定目录
     *
     * @param fromFilePath 要加密的文件 如c:/test/待加密文件.txt
     * @param toFilePath   加密后存放的文件 如c:/加密后文件.txt
     * @param deleteSource 是否删除源文件（计算图片缩略图等操作请在删除文件前处理！！！或自行删除处理）
     * @return 加密是否成功
     */
    public static boolean encrypt(Context context, String fromFilePath, String toFilePath, boolean deleteSource, ProgressListener progressListener) {
        String fileName = fromFilePath.substring(fromFilePath.lastIndexOf("/") + 1);
        Log.i(TAG, fileName + " encrypting...");

        File fromFile = new File(fromFilePath);
        if (!fromFile.exists()) {
            Log.i(TAG, "to be encrypt file no exist!");
            return false;
        }
        File toFile = getFile(toFilePath);
        Log.i(TAG, "toFile when encrypt:" + toFilePath);
        // 初始化iv长度为16字节
        IvParameterSpec iv = new IvParameterSpec(KEY.substring(0, 16).getBytes());
        SecretKey secretKey = new SecretKeySpec(KEY.getBytes(), KEY_ALGORITHM);
        InputStream is = null;
        OutputStream out = null;
        CipherInputStream cis = null;
        try {
            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, secretKey, iv);
            is = new FileInputStream(fromFile);
            out = new FileOutputStream(toFile);
            cis = new CipherInputStream(is, cipher);
            byte[] buffer = new byte[1024];
            int size;
            while ((size = cis.read(buffer)) > 0) {
                out.write(buffer, 0, size);
                if (progressListener != null)
                    progressListener.onProgress(size);
            }

            Log.i(TAG, fileName + " encrypt completed");

            // 计算图片缩略图等操作需要在删除文件前处理！！！
            if (deleteSource) {
                boolean isDeleteSuccess = new File(fromFilePath).delete();
                Log.i(TAG, "delete source file is success: " + isDeleteSuccess);
                if (isDeleteSuccess) {
                    // 删除MediaStore数据库文件信息
                    deleteFileFromMediaStore(context, fromFilePath);
                }
            }
        } catch (Exception e) {
            Log.i(TAG, e.toString());
            return false;
        } finally {
            try {
                if (cis != null) {
                    cis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (is != null) {
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return true;
    }

    @NonNull
    private static File getFile(String filePath) {
        File fromFile = new File(filePath);
        if (!fromFile.getParentFile().exists()) {
            boolean result = fromFile.getParentFile().mkdirs();
            Log.i(TAG, "mkdirs is success: " + result);
        }
        return fromFile;
    }

    /**
     * 文件进行解密并保存解密后的文件到指定目录
     *
     * @param fromFilePath 已加密的文件 如c:/加密后文件.txt
     * @param toFilePath   解密后存放的文件 如c:/ test/解密后文件.txt
     * @param deleteSource 是否删除源文件
     */
    public static boolean decrypt(Context context, String fromFilePath, String toFilePath, boolean deleteSource, ProgressListener progressListener) {
        String fileName = fromFilePath.substring(fromFilePath.lastIndexOf("/") + 1);
        Log.i(TAG, fileName + " decrypting...");

        File fromFile = new File(fromFilePath);
        if (!fromFile.exists()) {
            Log.i(TAG, "to be decrypt file no exist!");
            return false;
        }
        File toFile = getFile(toFilePath);

        IvParameterSpec iv = new IvParameterSpec(KEY.substring(0, 16).getBytes());
        SecretKey secretKey = new SecretKeySpec(KEY.getBytes(), KEY_ALGORITHM);

        InputStream is = null;
        OutputStream out = null;
        CipherOutputStream cos = null;
        try {
            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
            is = new FileInputStream(fromFile);
            out = new FileOutputStream(toFile);
            cos = new CipherOutputStream(out, cipher);
            byte[] buffer = new byte[1024];
            int size;
            while ((size = is.read(buffer)) >= 0) {
                cos.write(buffer, 0, size);
                if (progressListener != null)
                    progressListener.onProgress(size);
            }

            Log.i(TAG, fileName + " decrypt completed");

            if (deleteSource) {
                boolean isDeleteSuccess = new File(fromFilePath).delete();
                Log.i(TAG, "delete source file is success: " + isDeleteSuccess);
                if (isDeleteSuccess) {
                    addFileToMediaStore(context, toFilePath);
                }
            }
        } catch (Exception e) {
            Log.i(TAG, e.toString());
            return false;
        } finally {
            try {
                if (cos != null) {
                    cos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (is != null) {
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return true;
    }

    public static boolean decrypt(InputStream is, String toFilePath) {
        Log.i(TAG, " decrypting...");

        File toFile = getFile(toFilePath);

        IvParameterSpec iv = new IvParameterSpec(KEY.substring(0, 16).getBytes());
        SecretKey secretKey = new SecretKeySpec(KEY.getBytes(), KEY_ALGORITHM);

        OutputStream out = null;
        CipherOutputStream cos = null;
        try {
            Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, secretKey, iv);
            out = new FileOutputStream(toFile);
            cos = new CipherOutputStream(out, cipher);
            byte[] buffer = new byte[1024];
            int size;
            while ((size = is.read(buffer)) >= 0) {
                cos.write(buffer, 0, size);
            }

            Log.i(TAG, " decrypt completed");
        } catch (Exception e) {
            Log.i(TAG, e.toString());
            return false;
        } finally {
            try {
                if (cos != null) {
                    cos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (is != null) {
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return true;
    }

    /**
     * 删除文件数据库中的记录，不然delete文件后虽然文件已经不存在了，但是还是会显示出来
     */
    public static void deleteFileFromMediaStore(Context context, String filePath) {
        // 方法一因为需要回调所以有一定的刷新延迟,数据显示可能不及时
//        scanFile(filePath, true);

//        方法二
        ContentResolver contentResolver = context.getApplicationContext().getContentResolver();

        File file = new File(filePath);
        String canonicalPath;
        try {
            canonicalPath = file.getCanonicalPath();
        } catch (IOException e) {
            canonicalPath = file.getAbsolutePath();
        }

        String selection = MediaStore.Files.FileColumns.DATA + " = ?";
        String[] selectionArgs = new String[]{canonicalPath};
        final Uri uri = MediaStore.Files.getContentUri("external");
        final int result = contentResolver.delete(uri, selection, selectionArgs);

        if (result == 0) {
            final String absolutePath = file.getAbsolutePath();
            if (!absolutePath.equals(canonicalPath)) {
                contentResolver.delete(uri, selection, selectionArgs);
            }
        }

    }

    /**
     * 添加MediaStore记录
     */
    public static void addFileToMediaStore(Context context, String filePath) {
        scanFile(context, filePath, false);
    }

    /**
     * 扫描指定路径的文件，并添加到数据库或从数据库中移除
     *
     * @param isDelete true：删除；false：添加
     */
    private static void scanFile(final Context context, String path, final boolean isDelete) {
        try {
            MediaScannerConnection.scanFile(context, new String[]{path},
                    null, new MediaScannerConnection.OnScanCompletedListener() {
                        @Override
                        public void onScanCompleted(String path, Uri uri) {
                            if (isDelete && uri != null) {
                                context.getContentResolver().delete(uri,
                                        null, null);
                            }
                        }
                    });
        } catch (Exception e) {
            Log.e(TAG, e.toString());
        }
    }
}