/*
 * Copyright (c) 2015. Ncomz. All Rights Reserved
 */

package com.ntels.onecable.admin.view;

import com.ntels.onecable.admin.R;
import com.ntels.onecable.admin.common.Constants;
import com.ntels.onecable.admin.common.util.Dlog;
import com.ntels.onecable.admin.common.util.Util;
import com.ntels.onecable.admin.common.widget.CustomActivity;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.ExifInterface;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.v4.content.FileProvider;
import android.view.LayoutInflater;
import android.view.View;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;


/**
 * <pre>
 * 1. ClassName :   ImageSelect
 * 2. FileName  :   ImageSelect.java
 * 3. Package   :   com.ntels.onecable.admin.view
 * 4. Date      :   2016-11-28
 * 5. Author    :   aaron
 * 6. Comment   :   Initialize
 * </pre>
 */
public class ImageSelect extends CustomActivity implements View.OnClickListener {

    private static final int REQ_CODE_PICK_GALLERY = 9001;
    private static final int REQ_CODE_PICK_CAMERA = 9002;
    private static final int REQ_CODE_PICK_CROP = 9003;

    private static final int DEFAULT_IMAGE_SIZE = 500;
    private static final int BITMAP_COMPRESS_QUALITY = 100;

    private static final int ORIENTATION_ROTATE_90 = 90;
    private static final int ORIENTATION_ROTATE_180 = 180;
    private static final int ORIENTATION_ROTATE_270 = 270;

    private Activity mActivity;
    private Intent mIntent;
    private Util mUtil = new Util();
    private boolean mIsCropRequested = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        mActivity = ImageSelect.this;
        // 세로로 고정 역방향 세로는 가능
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
        View viewToLoad = LayoutInflater.from(mActivity).inflate(R.layout.image_select, null);
        this.setContentView(viewToLoad);

        if (savedInstanceState != null) {
            mIsCropRequested = savedInstanceState.getBoolean(Constants.EXTRA_CROP_TYPE, false);
        } else {
            Intent intent = getIntent();
            mIsCropRequested = intent.getBooleanExtra(Constants.EXTRA_CROP_TYPE, false);
        }

        Dlog.v("mIsCropRequested = " + mIsCropRequested);
        startSelectImage();
    }

    @Override
    public void onClick(View view) {

        switch (view.getId()) {
            case R.id.llPic1:
                mIntent = new Intent(Intent.ACTION_PICK);
                mIntent.setType(android.provider.MediaStore.Images.Media.CONTENT_TYPE);
                mIntent.setData(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
                mUtil.callActivityForResultNoAnim(mActivity, mIntent, REQ_CODE_PICK_GALLERY);
                break;
            case R.id.llPic2:

                Uri imagefileUri;
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                    imagefileUri = FileProvider.getUriForFile(mActivity, mActivity.getApplicationContext().getPackageName() + ".provider",
                            getTempImageFile());
                } else {
                    imagefileUri = Uri.fromFile(getTempImageFile());
                }
                mIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                mIntent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, imagefileUri);
                mIntent.putExtra("return-data", true);
                mUtil.callActivityForResultNoAnim(mActivity, mIntent, REQ_CODE_PICK_CAMERA);
                break;
            case R.id.llDelete:
                mIntent = new Intent();
                mIntent.putExtra(Constants.EXTRA_IMAGE_FILE_NAME, "");
                setResult(RESULT_OK, mIntent);
                finish();
                break;
            case R.id.llCancel:
                onBackPressed();
                break;
            default:
                break;
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {

        Dlog.v("requestCode = " + requestCode);
        Dlog.v("resultCode = " + resultCode);
        Dlog.v("data = " + data);
        if (requestCode == REQ_CODE_PICK_GALLERY && resultCode == Activity.RESULT_OK) {
            // 갤러리의 경우 곧바로 data 에 uri가 넘어옴.
            Uri uri = data.getData();
            copyUriToFile(uri, getTempImageFile());
            correctCameraOrientation(getTempImageFile());
            Dlog.v("getTempImageFile = " + getTempImageFile());
            Dlog.v("mIsCropRequested = " + mIsCropRequested);
            if (mIsCropRequested) {
                cropImage();
            } else {
                doFinalProcess();
            }
        } else if (requestCode == REQ_CODE_PICK_CAMERA && resultCode == Activity.RESULT_OK) {
            // 카메라의 경우 file 로 결과물이 돌아옴.
            // 카메라 회전 보정.
            correctCameraOrientation(getTempImageFile());
            Dlog.v("mIsCropRequested = " + mIsCropRequested);
            if (mIsCropRequested) {
                cropImage();
            } else {
                doFinalProcess();
            }
        } else if (requestCode == REQ_CODE_PICK_CROP && resultCode == Activity.RESULT_OK) {
            // crop 한 결과는 file로 돌아옴.
            doFinalProcess();
        } else {
            super.onActivityResult(requestCode, resultCode, data);
        }
    }

    /**
     * Call this to start.
     */
    public void startSelectImage() {

        if (!checkWriteExternalPermission()) {
            return;
        }
        if (!checkSDisAvailable()) {
            return;
        }
        mUtil.getView(mActivity, R.id.llPic1).setOnClickListener(this);
        mUtil.getView(mActivity, R.id.llPic2).setOnClickListener(this);
        mUtil.getView(mActivity, R.id.llDelete).setOnClickListener(this);
        mUtil.getView(mActivity, R.id.llCancel).setOnClickListener(this);
    }

    /**
     * 선택된 이미지 파일 getter.
     *
     * @return 선택된 이미지 파일
     */
    public File getSelectedImageFile() {

        return getTempImageFile();
    }

    private int cropAspectWidth = 1;
    private int cropAspectHeight = 1;

    /**
     * crop 이 필요한 경우 설정함. 설정하지 않으면 crop 하지 않음.
     *
     * @param width  crop size width.
     * @param height crop size height.
     */
    public void setCropOption(int width, int height) {

        mIsCropRequested = true;
        cropAspectWidth = width;
        cropAspectHeight = height;
    }


    private int imageSizeBoundary = DEFAULT_IMAGE_SIZE;

    /**
     * <pre>
     * 사용할 이미지의 최대 크기 설정. 가로, 세로 지정한 크기보다 작은 사이즈로 이미지 크기를 조절함.
     * default size : 500
     * </pre>
     *
     * @param sizePixel 기본 500
     */
    public void setImageSizeBoundary(int sizePixel) {

        imageSizeBoundary = sizePixel;
    }

    /**
     * WRITE_EXTERNAL_STORAGE permission check.
     *
     * @return permission 수락 여부
     */
    private boolean checkWriteExternalPermission() {

        String permission = "android.permission.WRITE_EXTERNAL_STORAGE";
        int res = checkCallingOrSelfPermission(permission);
        return (res == PackageManager.PERMISSION_GRANTED);
    }

    /**
     * 외장메모리 마운트 상태 체크.
     *
     * @return 외장메모리 마운트 상태
     */
    private boolean checkSDisAvailable() {

        String state = Environment.getExternalStorageState();
        return state.equals(Environment.MEDIA_MOUNTED);
    }

    /**
     * 임시 이미지 파일 가져오기.
     *
     * @return 임시 이미지 파일
     */
    private File getTempImageFile() {

        File path = new File(Environment.getExternalStorageDirectory() + "/Android/data/"
                + getPackageName() + "/temp/");
        if (!path.exists()) {
            path.mkdirs();
        }
        File file = new File(path, "tempimage.png");
        return file;
    }

    /**
     * 선택된 이미지 처리.
     */
    private void doFinalProcess() {
        // sample size 를 적용하여 bitmap load.
        Bitmap bitmap = loadImageWithSampleSize(getTempImageFile());

        // image boundary size 에 맞도록 이미지 축소.
        bitmap = resizeImageWithinBoundary(bitmap);

        // 결과 file 을 얻어갈 수 있는 메서드 제공.
        saveBitmapToFile(bitmap);

        mIntent = new Intent();
        mIntent.putExtra(Constants.EXTRA_IMAGE_FILE_NAME, getSelectedImageFile().toString());
        setResult(RESULT_OK, mIntent);
        finish();
    }

    /**
     * bitmap을 파일로 저장.
     *
     * @param bitmap 파일로 저장할 bitmap
     */
    private void saveBitmapToFile(Bitmap bitmap) {

        File target = getTempImageFile();
        try {
            FileOutputStream fos = new FileOutputStream(target, false);
            bitmap.compress(Bitmap.CompressFormat.PNG, BITMAP_COMPRESS_QUALITY, fos);
            fos.close();
        } catch (FileNotFoundException ex) {
            ex.printStackTrace();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    /**
     * 이미지 사이즈 수정 후, 카메라 rotation 정보가 있으면 회전 보정함.
     */
    private void correctCameraOrientation(File imgFile) {

        Bitmap bitmap = loadImageWithSampleSize(imgFile);
        try {
            ExifInterface exif = new ExifInterface(imgFile.getAbsolutePath());
            int exifOrientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,
                    ExifInterface.ORIENTATION_NORMAL);
            int exifRotateDegree = exifOrientationToDegrees(exifOrientation);
            bitmap = rotateImage(bitmap, exifRotateDegree);
            saveBitmapToFile(bitmap);
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    /**
     * 이미지 회전.
     *
     * @param bitmap  회전할 이미지 bitmap
     * @param degrees 회전할 각도
     * @return rotate 적용된 bitmap
     */
    private Bitmap rotateImage(Bitmap bitmap, int degrees) {

        if (degrees != 0 && bitmap != null) {
            Matrix matrix = new Matrix();
            matrix.setRotate(degrees, (float) bitmap.getWidth() / 2,
                    (float) bitmap.getHeight() / 2);
            try {
                Bitmap converted = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
                        bitmap.getHeight(), matrix, true);
                if (bitmap != converted) {
                    bitmap.recycle();
                    bitmap = converted;
                }
            } catch (OutOfMemoryError ex) {
                ex.printStackTrace();
            }
        }
        return bitmap;
    }

    /**
     * EXIF정보를 회전각도로 변환하는 메서드.
     *
     * @param exifOrientation EXIF 회전각
     * @return 실제 각도
     */
    private int exifOrientationToDegrees(int exifOrientation) {

        if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_90) {
            return ORIENTATION_ROTATE_90;
        } else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_180) {
            return ORIENTATION_ROTATE_180;
        } else if (exifOrientation == ExifInterface.ORIENTATION_ROTATE_270) {
            return ORIENTATION_ROTATE_270;
        }
        return 0;
    }

    /**
     * 원하는 크기의 이미지로 options 설정.
     */
    private Bitmap loadImageWithSampleSize(File file) {

        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(file.getAbsolutePath(), options);
        int width = options.outWidth;
        int height = options.outHeight;
        int longSide = Math.max(width, height);
        int sampleSize = 1;
        if (longSide > imageSizeBoundary) {
            sampleSize = longSide / imageSizeBoundary;
        }
        options.inJustDecodeBounds = false;
        options.inSampleSize = sampleSize;
        options.inPurgeable = true;
        options.inDither = false;

        Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath(), options);
        return bitmap;
    }

    /**
     * imageSizeBoundary 크기로 이미지 크기 조정. imageSizeBoundary 보다 작은 경우 resize하지 않음.
     */
    private Bitmap resizeImageWithinBoundary(Bitmap bitmap) {

        int width = bitmap.getWidth();
        int height = bitmap.getHeight();

        if (width > height) {
            if (width > imageSizeBoundary) {
                bitmap = resizeBitmapWithWidth(bitmap, imageSizeBoundary);
            }
        } else {
            if (height > imageSizeBoundary) {
                bitmap = resizeBitmapWithHeight(bitmap, imageSizeBoundary);
            }
        }
        return bitmap;
    }

    /**
     * bitmap 세로크기 resizing.
     *
     * @param source       resizing할 bitmap
     * @param wantedHeight resizing할 세로 크기
     * @return resizing된 bitmap
     */
    private Bitmap resizeBitmapWithHeight(Bitmap source, int wantedHeight) {

        if (source == null) {
            return null;
        }

        int width = source.getWidth();
        int height = source.getHeight();

        float resizeFactor = wantedHeight * 1f / height;

        int targetWidth = (int) (width * resizeFactor);
        int targetHeight = (int) (height * resizeFactor);

        Bitmap resizedBitmap = Bitmap.createScaledBitmap(source, targetWidth, targetHeight, true);

        return resizedBitmap;
    }

    /**
     * bitmap 가로크기 resizing.
     *
     * @param source      resizing할 bitmap
     * @param wantedWidth resizing할 가로 크기
     * @return resizing된 bitmap
     */
    private Bitmap resizeBitmapWithWidth(Bitmap source, int wantedWidth) {

        if (source == null) {
            return null;
        }

        int width = source.getWidth();
        int height = source.getHeight();

        float resizeFactor = wantedWidth * 1f / width;

        int targetWidth = (int) (width * resizeFactor);
        int targetHeight = (int) (height * resizeFactor);

        Bitmap resizedBitmap = Bitmap.createScaledBitmap(source, targetWidth, targetHeight, true);

        return resizedBitmap;
    }

    private String getPath(Uri uri) {

        String[] projection = {MediaStore.Images.Media.DATA};
        Cursor cursor = managedQuery(uri, projection, null, null, null);
        int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
        cursor.moveToFirst();
        return cursor.getString(columnIndex);
    }

    /**
     * Uri을 통해 가져온 이미지 파일로 저장.
     *
     * @param srcUri 저장할 이미지 Uri
     * @param target 저장될 파일
     */
    private void copyUriToFile(Uri srcUri, File target) {

        try {
            InputStream is;
            if (srcUri.toString().startsWith("content://com.google.android.gallery3d")) {
                is = getContentResolver().openInputStream(Uri.parse(getPath(srcUri)));
                Bitmap bmp = MediaStore.Images.Media.getBitmap(getContentResolver(),
                        Uri.parse(getPath(srcUri)));
                FileOutputStream fileStream = new FileOutputStream(target);
                bmp.compress(Bitmap.CompressFormat.PNG, BITMAP_COMPRESS_QUALITY, fileStream);
                fileStream.close();
            } else {
                //                is = new URL(srcUri.toString()).openStream();
                is = getContentResolver().openInputStream(srcUri);
            }
            OutputStream os = new FileOutputStream(target);

            final int bufferSize = 1024;
            try {
                byte[] bytes = new byte[bufferSize];
                for (; ; ) {
                    int count = is.read(bytes, 0, bufferSize);
                    if (count == -1) {
                        break;
                    }
                    os.write(bytes, 0, count);
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            os.close();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     * 이미지 크롭.
     */
    private void cropImage() {

        mIntent = new Intent("com.android.camera.action.CROP");
        mIntent.setType("image/*");
        List<ResolveInfo> cropToolLists = getPackageManager().queryIntentActivities(mIntent, 0);
        int size = cropToolLists.size();
        if (size == 0) {
            // crop 을 처리할 앱이 없음. 곧바로 처리.
            doFinalProcess();
        } else {

            Uri imagefileUri;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                imagefileUri = FileProvider.getUriForFile(mActivity, mActivity.getApplicationContext().getPackageName() + ".provider",
                        getTempImageFile());
            } else {
                imagefileUri = Uri.fromFile(getTempImageFile());
            }
            /*this.grantUriPermission("com.android.camera", imagefileUri,
                    Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
            grantUriPermission(cropToolLists.get(0).activityInfo.packageName, imagefileUri,
                    Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);*/
            Dlog.i("imagefileUri = " + imagefileUri);
            mIntent.setData(imagefileUri);
            mIntent.putExtra("scale", true);
            mIntent.putExtra("aspectX", cropAspectWidth);
            mIntent.putExtra("aspectY", cropAspectHeight);
            mIntent.putExtra("scaleUpIfNeeded", true);
            mIntent.putExtra("noFaceDetection", true);
            mIntent.putExtra("output", imagefileUri);
            mIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            mIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
            Intent intent = new Intent(this.mIntent);
            ResolveInfo res = cropToolLists.get(0);
            intent.setComponent(new ComponentName(res.activityInfo.packageName,
                    res.activityInfo.name));
            mUtil.callActivityForResultNoAnim(mActivity, intent, REQ_CODE_PICK_CROP);
        }
    }

}
