package com.aluxoft.earrecognition;

import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.provider.BaseColumns;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.support.v7.app.ActionBarActivity;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;

import org.opencv.android.Utils;
import org.opencv.core.Mat;
import org.opencv.core.MatOfByte;
import org.opencv.core.MatOfDMatch;
import org.opencv.core.MatOfKeyPoint;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.features2d.DMatch;
import org.opencv.features2d.DescriptorExtractor;
import org.opencv.features2d.DescriptorMatcher;
import org.opencv.features2d.FeatureDetector;
import org.opencv.features2d.Features2d;
import org.opencv.highgui.Highgui;
import org.opencv.imgproc.Imgproc;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Vector;

public class CameraActivity extends ActionBarActivity {

    private static final int REQUEST_IMAGE_CAPTURE = 1;
    private static final int SELECT_PICTURE = 0;
    private String mCurrentPhotoPath;
    private String mCurrentPhotoPath1;
    private String mCurrentPhotoPath2;
    private int previewWidth = 150;
    private int previewHeight = 150;
    private ImageView previewImage1;
    private ImageView previewImage2;
    private ImageView previewImage;

    public void resizeMatToVga(Mat image) {
        final double kMaxWidth = 640;
        final double kMaxHeight = 480;

        if (image.width() <= kMaxWidth && image.height() <= kMaxHeight) {
            return;
        }

        double ratio = (double)image.width() / (double)image.height();
        double width, height;
        if (ratio > 1) {
            width = kMaxWidth;
            height = kMaxWidth * (image.height()/image.width());
        } else {
            height = kMaxHeight;
            width = kMaxHeight* ratio;
        }
        Imgproc.resize(image, image, new Size(width, height));
    }

    public void doOpenCvMagic() {

        System.loadLibrary("opencv_java");
        System.loadLibrary("nonfree");

        Mat img1 = Highgui.imread(mCurrentPhotoPath1, Highgui.CV_LOAD_IMAGE_GRAYSCALE);
        Mat img2 = Highgui.imread(mCurrentPhotoPath2, Highgui.CV_LOAD_IMAGE_GRAYSCALE);


        resizeMatToVga(img1);
        resizeMatToVga(img2);

        int minHessian = 400;
        FeatureDetector detector = FeatureDetector.create(FeatureDetector.SURF);

        MatOfKeyPoint keypoints1 = new MatOfKeyPoint();
        MatOfKeyPoint keypoints2 = new MatOfKeyPoint();

        detector.detect(img1, keypoints1);
        detector.detect(img2, keypoints2);

        Mat descriptors1 = new Mat();
        Mat descriptors2 = new Mat();

        DescriptorExtractor extractor = DescriptorExtractor.create(DescriptorExtractor.SURF);
        extractor.compute(img1, keypoints1, descriptors1);
        extractor.compute(img2, keypoints2, descriptors2);

        MatOfDMatch matches = new MatOfDMatch();

        DescriptorMatcher matcher = DescriptorMatcher.create(DescriptorMatcher.FLANNBASED);
        matcher.match(descriptors1, descriptors2, matches);

        double max_dist = 0;
        double min_dist = 100;

        DMatch[] aMatches = matches.toArray();

        for (int i=0;i<aMatches.length;++i) {
            double dist = aMatches[i].distance;
            if (dist < min_dist) {
                min_dist = dist;
            }
            if (dist > max_dist) {
                max_dist = dist;
            }
        }
        Vector<DMatch> vgood_matches = new Vector<DMatch>();
        for (int i=0;i<aMatches.length;++i) {
            if (aMatches[i].distance < 3*min_dist) {
                vgood_matches.add(aMatches[i]);
            }
        }
        MatOfDMatch good_matches = new MatOfDMatch();
        good_matches.fromList(vgood_matches);

        Mat image_matches = new Mat();

        Features2d.drawMatches(img1, keypoints1, img2, keypoints2, good_matches, image_matches, Scalar.all(-1), Scalar.all(-1), new MatOfByte(), Features2d.NOT_DRAW_SINGLE_POINTS);

        Bitmap bitmap = Bitmap.createBitmap(image_matches.cols(), image_matches.rows(), Bitmap.Config.ARGB_8888);
        Utils.matToBitmap(image_matches, bitmap);

        previewImage1.setImageBitmap(bitmap);

    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_camera);
        previewImage1 = (ImageView)findViewById(R.id.imageView);
        previewImage2 = (ImageView)findViewById(R.id.imageView2);
        final Button takeButton = (Button)findViewById(R.id.button);
        takeButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                previewImage = previewImage1;
                takePhoto(takeButton);
                mCurrentPhotoPath1 = mCurrentPhotoPath;
            }
        });


        final Button pickButton = (Button)findViewById(R.id.button2);
        pickButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                previewImage = previewImage2;
                takePhoto(takeButton);
                mCurrentPhotoPath2 = mCurrentPhotoPath;
            }
        });

        final Button processButton = (Button) findViewById(R.id.button3);
        processButton.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                // do opencv magic here...
                if (mCurrentPhotoPath1 != null && mCurrentPhotoPath2 != null) {
                    doOpenCvMagic();
                }
            }
        });

    }

    public void pickPhoto(View view) {
        Intent intent = new Intent();
        intent.setType("image/*");
        intent.setAction(Intent.ACTION_GET_CONTENT);
        startActivityForResult(Intent.createChooser(intent, "Select Picture"), SELECT_PICTURE);

    }
    public void takePhoto(View view) {
        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) {
            if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
                File photoFile = null;
                try {
                    photoFile = createImageFile();
                } catch (IOException ex) {
                    // Error occurred while creating the File
                }
                // Continue only if the File was successfully created
                if (photoFile != null) {
                    takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile));
                    startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
                }
            }
        }
    }
    private File createImageFile() throws IOException {
        // Create an image file name
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
        String imageFileName = "JPEG_" + timeStamp + "_";
        File storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
        File image = File.createTempFile(imageFileName, /* prefix */
                ".jpg", /* suffix */
                storageDir /* directory */
        );

        // Save a file: path for use with ACTION_VIEW intents
        mCurrentPhotoPath = image.getAbsolutePath();
        return image;
    }

    @SuppressLint("NewApi")
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == REQUEST_IMAGE_CAPTURE) {
            if (resultCode == RESULT_OK) {
                if (mCurrentPhotoPath != null) {
					/*
					 * añadimos la imagen a la galeria
					 */
                    Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
                    File f = new File(mCurrentPhotoPath);
                    Uri contentUri = Uri.fromFile(f);
                    mediaScanIntent.setData(contentUri);
                    this.sendBroadcast(mediaScanIntent);
                    String path = f.getAbsolutePath();
                    Log.e("", path);
                    Bitmap galleryImage = getBitMapForPreview(path,previewWidth,previewHeight);
                    previewImage.setImageBitmap(galleryImage);
                }
            }
        }

        if (requestCode == SELECT_PICTURE) {
            if (resultCode == RESULT_OK) {

                int currentapiVersion = android.os.Build.VERSION.SDK_INT;
                if (currentapiVersion >= android.os.Build.VERSION_CODES.KITKAT) {
                    Uri uri = data.getData();
                    String IMAGE_FILEPATH = "";
                    try {
                        if (uri == null) {
                            IMAGE_FILEPATH = uri.getPath();
                        } else {
                            // get the id of the image selected by the user
                            String wholeID = DocumentsContract.getDocumentId(data.getData());
                            String id = wholeID.split(":")[1];

                            String[] projection = { MediaStore.MediaColumns.DATA };
                            String whereClause = BaseColumns._ID + "=?";
                            Cursor cursor = getContentResolver().query(getUri(), projection, whereClause,
                                    new String[] { id }, null);
                            if (cursor != null) {
                                int column_index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA);
                                if (cursor.moveToFirst()) {
                                    IMAGE_FILEPATH = cursor.getString(column_index);
                                    Bitmap galleryImage = getBitMapForPreview(IMAGE_FILEPATH,previewWidth,previewHeight);
                                    previewImage.setImageBitmap(galleryImage);
                                }

                                cursor.close();
                            } else {
                                IMAGE_FILEPATH = uri.getPath();
                                Bitmap galleryImage = getBitMapForPreview(IMAGE_FILEPATH,previewWidth,previewHeight);
                                previewImage.setImageBitmap(galleryImage);
                            }
                        }
                    } catch (Exception e) {
                        try {
                            Uri selectedImageUri = data.getData();
                            String[] projection = { MediaStore.MediaColumns.DATA };
                            Cursor cursor = managedQuery(selectedImageUri, projection, null, null, null);
                            int column_index_data = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA);
                            cursor.moveToFirst();
                            String selectedImagePath = cursor.getString(column_index_data);
                            Bitmap galleryImage = getBitMapForPreview(selectedImagePath,previewWidth,previewHeight);
                            previewImage.setImageBitmap(galleryImage);
                        } catch (Exception ex) {
                            AlertDialog.Builder builder = new AlertDialog.Builder(CameraActivity.this);
                            builder.setMessage("El elemento seleccionado no es una imágen válida").setTitle("Información");
                            builder.setNegativeButton("Aceptar", new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int id) {
                                    // User cancelled the dialog
                                }
                            });

                            AlertDialog dialog = builder.create();
                            dialog.show();
                        }
                    }

                } else {
                    try {
                        Uri selectedImageUri = data.getData();
                        String[] projection = { MediaStore.MediaColumns.DATA };
                        Cursor cursor = managedQuery(selectedImageUri, projection, null, null, null);
                        int column_index_data = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA);
                        cursor.moveToFirst();
                        String selectedImagePath = cursor.getString(column_index_data);
                        Bitmap galleryImage = getBitMapForPreview(selectedImagePath,previewWidth,previewHeight);
                    } catch (Exception e) {
                        AlertDialog.Builder builder = new AlertDialog.Builder(CameraActivity.this);
                        builder.setMessage("El elemento seleccionado no es una imágen válida").setTitle("Información");
                        builder.setNegativeButton("Aceptar", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int id) {
                                // User cancelled the dialog
                            }
                        });

                        AlertDialog dialog = builder.create();
                        dialog.show();
                    }
                }

            }
        }

    }
    private Bitmap getBitMapForPreview(String path, int width, int height) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        // solo se usa para calcular el samplesize
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(path, options);
        options.inSampleSize = calculateInSampleSize(options, (int)convertDpToPixel((float)width, this), (int)convertDpToPixel((float)height,this));
        options.inJustDecodeBounds = false;
        Bitmap galleryImage = BitmapFactory.decodeFile(path, options);

        return galleryImage;
    }

    /**
     * esto se utiliza para reducir la carga en memoria de la imagen, android es muy
     * ineficiente para cargar imagenes y si no se reduce el tamaño de acuerdo a la resolucion
     * del dispositivo, se crean errores de memoria insuficiente, se tienen que obtener el calculo de
     * los pixieles en base a los dp de la interfaz y generarlos de acuerdo a la densidad del dispositivo
     *
     * @param options
     * @param reqWidth   ancho del imageView donde se metera la imagen en pixeles
     * @param reqHeight alto del imageView donde se metera la imagen en pixeles
     * @return
     */
    private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
        // Raw height and width of image
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;

        if (height > reqHeight || width > reqWidth) {

            final int halfHeight = height / 2;
            final int halfWidth = width / 2;

            // Calculate the largest inSampleSize value that is a power of 2 and
            // keeps both
            // height and width larger than the requested height and width.
            while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth) {
                inSampleSize *= 2;
            }
        }

        return inSampleSize;
    }

    /**
     * This method converts dp unit to equivalent pixels, depending on device density.
     *
     * @param dp A value in dp (density independent pixels) unit. Which we need to convert into pixels
     * @param context Context to get resources and device specific display metrics
     * @return A float value to represent px equivalent to dp depending on device density
     */
    public static float convertDpToPixel(float dp, Context context){
        Resources resources = context.getResources();
        DisplayMetrics metrics = resources.getDisplayMetrics();

        /**
         *
         * formulas de android developers:
         *
         * px = dp * (dpi / 160)
         *
         * dp = px / (dpi / 160)
         *
         */
        float px = dp * (metrics.densityDpi / 160f);
        return px;
    }
    private Uri getUri() {
        String state = Environment.getExternalStorageState();
        if (!state.equalsIgnoreCase(Environment.MEDIA_MOUNTED))
            return MediaStore.Images.Media.INTERNAL_CONTENT_URI;

        return MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
    }



}
