package com.reactlibrary.vishwamdkyclib.Activities;

/*
 * Vishwam Corp CONFIDENTIAL

 * Vishwam Corp 2018
 * All Rights Reserved.

 * NOTICE:  All information contained herein is, and remains
 * the property of Vishwam Corp. The intellectual and technical concepts contained
 * herein are proprietary to Vishwam Corp
 * and are protected by trade secret or copyright law of U.S.
 * Dissemination of this information or reproduction of this material
 * is strictly forbidden unless prior written permission is obtained
 * from Vishwam Corp
 */

import android.annotation.SuppressLint;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.hardware.Camera;
import android.net.ConnectivityManager;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.support.v7.app.AppCompatActivity;
import android.util.DisplayMetrics;
import android.util.SparseArray;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.gms.vision.Frame;
import com.google.android.gms.vision.face.Face;
import com.google.android.gms.vision.face.FaceDetector;
import com.google.android.gms.vision.face.Landmark;
import com.microsoft.appcenter.AppCenter;
import com.microsoft.appcenter.analytics.Analytics;
import com.microsoft.appcenter.crashes.Crashes;
import com.reactlibrary.R;
import com.reactlibrary.vishwamdkyclib.Camera.CameraSource;
import com.reactlibrary.vishwamdkyclib.Camera.CameraSourcePreview;
import com.reactlibrary.vishwamdkyclib.CrashProof;
import com.reactlibrary.vishwamdkyclib.FaceCaptureCompleteHandler;
import com.reactlibrary.vishwamdkyclib.ImageAnalysis.ImageAnalysis;
import com.reactlibrary.vishwamdkyclib.LibUtils;
import com.reactlibrary.vishwamdkyclib.LiveFaceAuthHolder;
import com.reactlibrary.vishwamdkyclib.Tracker.FaceDect;
import com.reactlibrary.vishwamdkyclib.Tracker.GraphicOverlay;
import com.reactlibrary.vishwamdkyclib.VishwamError;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import okhttp3.CertificatePinner;
import okhttp3.Headers;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;

import static com.reactlibrary.vishwamdkyclib.Camera.CameraSource.framesAddedCount;
import static com.reactlibrary.vishwamdkyclib.Camera.CameraSource.needAnalysis;
import static com.reactlibrary.vishwamdkyclib.Camera.CameraSource.takePicture;
import static com.reactlibrary.vishwamdkyclib.ImageAnalysis.ImageAnalysis.canAddData;
import static com.reactlibrary.vishwamdkyclib.ImageAnalysis.ImageAnalysis.okForAnalysis;
import static com.reactlibrary.vishwamdkyclib.ImageAnalysis.ImageAnalysis.readyForAnalysis;
import static com.reactlibrary.vishwamdkyclib.LibConstants.IS_DOC_CONTEXT;
import static com.reactlibrary.vishwamdkyclib.Networking.VishwamNetworkHelper.liveFaceCoordinates;
import static com.reactlibrary.vishwamdkyclib.Networking.VishwamNetworkHelper.paramBE;
import static com.reactlibrary.vishwamdkyclib.Tracker.FaceDect.detectorAvailable;
import static com.reactlibrary.vishwamdkyclib.Tracker.FaceDect.previewFaceDetector;
import static com.reactlibrary.vishwamdkyclib.Tracker.FaceDect.straightFaceFound;
import static com.reactlibrary.vishwamdkyclib.Tracker.FaceGraphic.docOrNot;
import static com.reactlibrary.vishwamdkyclib.Tracker.FaceGraphic.faceIsInTheBox;
import static com.reactlibrary.vishwamdkyclib.Tracker.FaceGraphic.faceRatioOk;
import static com.reactlibrary.vishwamdkyclib.Tracker.FaceGraphic.mHintOutlinePaint;
import static okhttp3.Protocol.HTTP_1_1;

/**
 * Here in this activity we render preview on surfaceview capture image onclick process it for face recognition and detection.
 * **/

public class VishwamFaceActivity extends AppCompatActivity implements FaceDect.OnMultipleFacesDetectedListener, FaceDect.OnCaptureListener, View.OnClickListener{

    private Context context;

    FaceDect faceDect;

    public CameraSource mCameraSource;
    private CameraSourcePreview mPreview;
    private GraphicOverlay mGraphicOverlay;
    private boolean wasActivityResumed = false;

    public ImageView previewImages;
    public ImageButton button;

    public ProgressDialog dialog;
    TextView straightFaceWarning;

    public Bitmap squareBitmapFace, bitmap224;

    public static int circleX, circleY;
    public static int circleRadius;
    private Resources resources;
    public Button exitBtn;

    @SuppressLint("StaticFieldLeak")
    public static ImageAnalysis imageAnalysis;
    LibUtils libUtils;
    String path450, pathFace224 ;
    public boolean previewOrNot, okForCapture = true, allowDataLogging;
    public static Bitmap squareFace450Bitmap;
    public boolean shouldUseBackCamera;
    public static  boolean doSuccessLogs;

    public int env, capture_type;
    public String referenceId, livenessdata, allowEyesClosed, allowOnlyWhiteBackground, faceCaptureTitle, app_id, storeID,sdk_version;

    public JSONObject afterFaceCaptured;
    View viewToHideCircle;
    FaceDetector faceFramedetector;

    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_live_face_authentication);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
        doSuccessLogs = true;
        docOrNot = false;
        afterFaceCaptured = new JSONObject();
        straightFaceWarning = findViewById(R.id.straightFaceWarning);
        viewToHideCircle = findViewById(R.id.viewToHideCircle);
        liveFaceCoordinates = "";
        sdk_version = getString(R.string.sdk_version);
        initAnalytics();
        libUtils = new LibUtils();
        needAnalysis = true;

        framesAddedCount = 0;

        if (!readyForAnalysis) {
            imageAnalysis = new ImageAnalysis(getAssets(), VishwamFaceActivity.this);
            imageAnalysis.initImageAnalysis();
        }

        imageAnalysis.setupImageAnalysis();
        resources = this.getResources();

        faceFramedetector = new FaceDetector.Builder(VishwamFaceActivity.this)
                .setTrackingEnabled(false)
                .setLandmarkType(FaceDetector.ALL_LANDMARKS)
                .build();

        DisplayMetrics metrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(metrics);

        int height = metrics.heightPixels;
        int width = metrics.widthPixels;

        //Log.e("screenSize", width + "," + height);

        circleX = width / 2;
        circleY = height / 2;

        /**** ================================ params from BE =============================**/


         double circle_radius = 0.4; // 2/5
         if (paramBE!=null){
             if (paramBE.has("circle_radius")){
                 try {
                     circle_radius = paramBE.getDouble("circle_radius");
                 } catch (JSONException e) {
                     e.printStackTrace();
                 }

             }
         }

        /**** ================================ params from BE =============================**/


        circleRadius = (int) (width * circle_radius);

        context = getApplicationContext();
        dialog = new ProgressDialog(VishwamFaceActivity.this);

        previewImages = findViewById(R.id.preview);
        previewImages.setVisibility(View.GONE);
        TextView faceCaptureTitleText = findViewById(R.id.faceCaptureTitleText);

        exitBtn = findViewById(R.id.exitBtn);

        Intent i = getIntent();
        Bundle extras = i.getExtras();

        if (extras != null) {
            faceCaptureTitle = extras.getString("faceCaptureTitle");
            faceCaptureTitleText.setText(faceCaptureTitle);
            shouldUseBackCamera = extras.getBoolean("shouldUseBackCamera");
            previewOrNot = extras.getBoolean("shouldShowReviewScreen");
            allowOnlyWhiteBackground = extras.getString("allowOnlyWhiteBackground");
            allowEyesClosed = extras.getString("allowEyesClosed");
            env = extras.getInt("env");
            capture_type = extras.getInt("capture_type");
            referenceId = extras.getString("referenceId");
            storeID = extras.getString("store_id");
            app_id = extras.getString("app_id");
            allowDataLogging = extras.getBoolean("allowDataLogging");
        }else try {
            throw new Exception("Please specify preview in Intent ");

        } catch (Exception e) {
            e.printStackTrace();
        }

        mPreview = findViewById(R.id.previewAuth);
        ///////////////////////////////////////////////////////////////////////////////////
        /*ViewGroup.LayoutParams layoutparams = mPreview.getLayoutParams();

        int widthPreview = width;
        int heightPreview = height;

        //Log.e("mPreviewSize", widthPreview + ", " + heightPreview);

        ((RelativeLayout.LayoutParams) layoutparams).setMargins(widthPreview / 10, heightPreview / 10, widthPreview / 10, heightPreview / 10);
        mPreview.setLayoutParams(layoutparams);*/
        ///////////////////////////////////////////////////////////////////////////////////

        mGraphicOverlay = findViewById(R.id.faceOverlayAuth);
        createCameraSourceFront();
        startCameraSource();

        button = findViewById(R.id.button);

        if (detectorAvailable) {
            button.setEnabled(false);
        } else {
            button.setEnabled(true);
            button.setBackground(getResources().getDrawable(R.drawable.enable_camera));
        }

        button.setOnClickListener(this);

        exitBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });
        logFaceStartEvent(shouldUseBackCamera);
    }
    public void initAnalytics(){
        AppCenter.start(getApplication(), "edae2aab-a04a-49cd-8c64-ae5872a61a44", Analytics.class, Crashes.class);
        Analytics.setEnabled(true);
    }
    /**
     * Method to log on start of this activity.
     * **/
    public void logFaceStartEvent(Boolean isBackCam){
        if(doSuccessLogs){
            Map<String, String> properties = new HashMap<>();
            if (isBackCam) {
                properties.put("CameraType", "Back");
            } else {
                properties.put("CameraType", "Front");
            }

            Analytics.trackEvent("OnStartFaceCapture", properties);
        }


    }
    /**
     * Method to no internet event.
     * **/
    public  void logNoInternetEvent(String ActivityName,String referenceId) {
        if(doSuccessLogs){
            Map<String, String> properties = new HashMap<>();
            properties.put("Activity", ActivityName);
            properties.put("ReferenceId", referenceId);
            Analytics.trackEvent("No Internet", properties);
        }
    }


    /**
     * Method to start this activity.
     * **/

    public static void start(Context context, JSONObject faceParams, FaceCaptureCompleteHandler onLiveFaceCapturedResultListener){

        Intent intent = new Intent(context, VishwamFaceActivity.class);
        LiveFaceAuthHolder.getInstance().setLiveFaceAuthResultListener(onLiveFaceCapturedResultListener);
        try {
            intent.putExtra("env",faceParams.getInt("env"));
            intent.putExtra("capture_type",faceParams.getInt("capture_type"));
            intent.putExtra("referenceId", faceParams.getJSONObject("headers").getString("referenceId"));

            if (faceParams.has("store_id")){
                intent.putExtra("store_id",faceParams.getString("store_id"));
            }else{
                intent.putExtra("store_id"," ");
            }

            if (faceParams.has("shouldUseBackCamera")){
                intent.putExtra("shouldUseBackCamera",faceParams.getBoolean("shouldUseBackCamera"));
            }else{
                intent.putExtra("shouldUseBackCamera",false);
            }

            if (faceParams.has("shouldShowReviewScreen")){
                intent.putExtra("shouldShowReviewScreen", faceParams.getBoolean("shouldShowReviewScreen"));
            }else{
                intent.putExtra("shouldShowReviewScreen", true);
            }

            if (faceParams.has("allowDataLogging")) {
                intent.putExtra("allowDataLogging", faceParams.getBoolean("allowDataLogging"));
            } else {
                intent.putExtra("allowDataLogging", false);
            }

            if (faceParams.has("allowOnlyWhiteBackground")){
                intent.putExtra("allowOnlyWhiteBackground", faceParams.getString("allowOnlyWhiteBackground"));
            }else{
                intent.putExtra("allowOnlyWhiteBackground", "no");
            }

            if(faceParams.has("faceCaptureTitle")){
                intent.putExtra("faceCaptureTitle", faceParams.getString("faceCaptureTitle"));
            }else{
                intent.putExtra("faceCaptureTitle", "Face Capture");
            }

            if (faceParams.has("allowEyesClosed")){
                intent.putExtra("allowEyesClosed", faceParams.getString("allowEyesClosed"));
            }else{
                intent.putExtra("allowEyesClosed", "yes");
            }
            if (faceParams.has("app_id")){
                intent.putExtra("app_id", faceParams.getString("app_id"));
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                context.startActivity(intent);
            }else{
                LiveFaceAuthHolder.getInstance().getLiveFaceAuthResultListener().onResult(new VishwamError(VishwamError.NO_APP_ID, "app_id not found"),null, null);
            }

        } catch (JSONException e) {
            e.printStackTrace();
        }
    }
    /**
     * Method invoked by facedetec on detection multiple face and many other cases like face in circle,straight face.
     * **/
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
    @Override
    public void onMultipleFacesDetected(int n) {
        if (canAddData) {

            if (n == 1 && faceIsInTheBox && straightFaceFound) {

                if (faceRatioOk){
                    mHintOutlinePaint = new Paint();
                    mHintOutlinePaint.setColor(resources.getColor(R.color.green_color));
                    mHintOutlinePaint.setStyle(Paint.Style.STROKE);
                    mHintOutlinePaint.setStrokeWidth(resources.getDimension(R.dimen.hintStroke));
                    buttonChange(R.drawable.enable_camera, true, "");
                }else{
                    mHintOutlinePaint = new Paint();
                    mHintOutlinePaint.setColor(resources.getColor(R.color.red_color));
                    mHintOutlinePaint.setStyle(Paint.Style.STROKE);
                    mHintOutlinePaint.setStrokeWidth(resources.getDimension(R.dimen.hintStroke));
                    buttonChange(R.drawable.disable_camera, false, "Come closer to camera");
                }

            } else if (n == 1 && faceIsInTheBox && !straightFaceFound) {

                mHintOutlinePaint = new Paint();
                mHintOutlinePaint.setColor(resources.getColor(R.color.red_color));
                mHintOutlinePaint.setStyle(Paint.Style.STROKE);
                mHintOutlinePaint.setStrokeWidth(resources.getDimension(R.dimen.hintStroke));
                buttonChange(R.drawable.disable_camera, false,"Straight face not found");

            } else if (n > 1 && faceIsInTheBox) {

                mHintOutlinePaint = new Paint();
                mHintOutlinePaint.setColor(resources.getColor(R.color.red_color));
                mHintOutlinePaint.setStyle(Paint.Style.STROKE);
                mHintOutlinePaint.setStrokeWidth(resources.getDimension(R.dimen.hintStroke));
                buttonChange(R.drawable.disable_camera, false,"Multiple faces found");

            } else if (n == 1 && !faceIsInTheBox) {

                mHintOutlinePaint = new Paint();
                mHintOutlinePaint.setColor(resources.getColor(R.color.red_color));
                mHintOutlinePaint.setStyle(Paint.Style.STROKE);
                mHintOutlinePaint.setStrokeWidth(resources.getDimension(R.dimen.hintStroke));
                buttonChange(R.drawable.disable_camera, false,"Keep face inside the circle");
            } else {

                mHintOutlinePaint = new Paint();
                mHintOutlinePaint.setColor(resources.getColor(R.color.red_color));
                mHintOutlinePaint.setStyle(Paint.Style.STROKE);
                mHintOutlinePaint.setStrokeWidth(resources.getDimension(R.dimen.hintStroke));
                buttonChange(R.drawable.disable_camera, false,"Face not found");
            }
        }
    }
    /**
     * Interface Callback on shutter of camera invoked by cameraSource.
     * **/
    final CameraSource.ShutterCallback cameraSourceShutterCallback = new CameraSource.ShutterCallback() {
        @Override
        public void onShutter() {

            dialog.setMessage("Receiving Image...");
            dialog.setCancelable(false);
            dialog.show();
        }
    };
    /**
     * Interface callback on camerasocurcepicture taken.
     ***/
    final CameraSource.PictureCallback cameraSourcePictureCallback = new CameraSource.PictureCallback() {
        @SuppressLint("NewApi")
        @Override
        public void onPictureTaken(byte[] data) {
            stopCameraSource();
        }
    };

    /**
     * Method to get facecroped bitmap of orginal given bitmap using face coordinates.
     * **/

    String tryingAbnormally = "0", slopeString = "";

    public Bitmap getCroppedFace(Bitmap bitmap){

        Frame frame = new Frame.Builder().setBitmap(bitmap).build();
        SparseArray<Face> faces = faceFramedetector.detect(frame);

        if(faces.size() == 1) {
            Face face = faces.valueAt(0);

            List<Landmark> landmarks = face.getLandmarks();
            int landmarksCount = landmarks.size();

            if (landmarksCount == 12){
                float slope = (landmarks.get(1).getPosition().y - landmarks.get(0).getPosition().y) / (landmarks.get(1).getPosition().x - landmarks.get(0).getPosition().x);
                slopeString = String.valueOf(slope);
            }else if (landmarksCount == 8){
                float slope = (landmarks.get(1).getPosition().y - landmarks.get(0).getPosition().y) / (landmarks.get(1).getPosition().x - landmarks.get(0).getPosition().x);
                slopeString = String.valueOf(slope);
            } else{
                slopeString = "";
            }

            float centerX = face.getPosition().x + face.getWidth() / 2.0f;
            float centerY = face.getPosition().y + face.getHeight() / 2.0f;
            float faceWidth = face.getWidth();
            float faceHeight = face.getHeight();

            /** ================================ params from BE =============================**/

            int crop_size = 2;

            if (paramBE!=null) {
                if (paramBE.has("crop_size")) {
                    try {
                        crop_size = paramBE.getInt("crop_size");
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                }
            }

            /** ================================ params from BE =============================**/


            // Draw a box around the face.
            float left = centerX - (faceWidth/2 * crop_size);
            float right = centerX + (faceWidth/2 * crop_size);
            float top = centerY - (faceHeight/2 * crop_size);
            float bottom = centerY + (faceHeight/2 * crop_size);

            if (left < 0)left=0;
            if (top < 0)top=0;
            if (right > bitmap.getWidth())right=bitmap.getWidth();
            if (bottom > bitmap.getHeight())bottom=bitmap.getHeight();

            Bitmap croppedBitmap = Bitmap.createBitmap(bitmap, (int) (left), (int) (top), (int) (right-left), (int) (bottom-top));
            return croppedBitmap;
        }else{
            return bitmap;
        }
    }

    /**
     * Method get face coordinates.
     * */

    public void getFaceCoordinates(Bitmap bitmap){
        //long t1 = System.currentTimeMillis();

        Frame finalFrame = new Frame.Builder().setBitmap(bitmap).build();
        SparseArray<Face> finalFaces = faceFramedetector.detect(finalFrame);

        if (finalFaces.size() == 1) {
            Face finalFace = finalFaces.valueAt(0);

            float finalFaceX = finalFace.getPosition().x;
            //Log.e("faceX", String.valueOf(faceX));
            float finalFaceeY = finalFace.getPosition().y;
            //Log.e("faceY", String.valueOf(faceY));
            float finalFaceWidth = finalFace.getWidth();
            //Log.e("faceWidth", String.valueOf(faceWidth));
            float finalFaceHeight = finalFace.getHeight();
            //Log.e("faceHeight", String.valueOf(faceHeight));

            try {
                faceCoordinates.put("left", finalFaceX);
                faceCoordinates.put("right", finalFaceX + finalFaceWidth);
                faceCoordinates.put("top", finalFaceeY);
                faceCoordinates.put("bottom", finalFaceeY + finalFaceHeight);
            } catch (JSONException e) {
                e.printStackTrace();
            }

            liveFaceCoordinates = faceCoordinates.toString();
//            Log.e("vishwamSukshiCoordsLive", liveFaceCoordinates);
            //long t2 = System.currentTimeMillis();
            //Log.e("timeDiff", String.valueOf(t2-t1));
        }
    }

    JSONObject faceCoordinates = new JSONObject();

    /**
     * Method to log any event given as parameter.
     * **/

    public  void logEvent(String eventName) {
        if(doSuccessLogs){
            Analytics.trackEvent(eventName);
        }

    }




    /**
     * Method to move to preview activity.
     * **/
    public void moveToPreview() {
        logEvent("OnOpenFacePreview");
        Intent intent = new Intent(VishwamFaceActivity.this, PreviewActivity.class);
        intent.putExtra(IS_DOC_CONTEXT, 1);
        startActivityForResult(intent, 200);
        overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left);


    }

    String delay = "";

    /**
     * Method to get save bitmap of size 450.
     * **/

    public void saveFace450Bitmap(Bitmap bitmap) {

        File file = libUtils.getOutputMediaFile(VishwamFaceActivity.this, "Face450");
        path450 = file.getPath();
        FileOutputStream out = null;
        try {
            out = new FileOutputStream(file);
            bitmap.compress(Bitmap.CompressFormat.JPEG, 95, out);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (out != null) {
                    out.flush();
                    out.close();

                    livenessdata = imageAnalysis.getImageIndex();
                    JSONObject delayObject = new JSONObject();
                    delayObject.put("data_string", livenessdata);
                    delayObject.put("abnormality_index", tryingAbnormally);
                    delayObject.put("slope", slopeString);
                    //delayObject.put("coordinates", faceCoordinates);
                    delay = delayObject.toString();

//                    Log.e("vishwamSukshiDelay", delay);
                    framesAddedCount = 0;

                    if (checkInternetConnection()) {
                        new ApiCallForLivenessCheck().execute();
                    }else {
                        logNoInternetEvent("OnLivenessCheck",referenceId);
                        LiveFaceAuthHolder.getInstance().getLiveFaceAuthResultListener().onResult(new VishwamError(VishwamError.NO_INTERNET, "No Active Internet Connection found"),null, null);

                        finish();
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * Method to save bitmap of size 224.
     * **/
    public void saveFile224(Bitmap bitmap) {

        File file = libUtils.getOutputMediaFile(VishwamFaceActivity.this, "Face224");
        pathFace224 = file.getPath();
        FileOutputStream out = null;
        try {
            out = new FileOutputStream(file);
            bitmap.compress(Bitmap.CompressFormat.JPEG, 95, out);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (out != null) {
                    out.flush();
                    out.close();

                    //saveFileCropped(croppedFaceBitmap);
                    saveFace450Bitmap(squareFace450Bitmap);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * Method to createCamersourceFront.
     *
     * **/
    private void createCameraSourceFront() {
        faceDect = new FaceDect(this, mGraphicOverlay);

        if(shouldUseBackCamera){
            mCameraSource = new CameraSource.Builder(context, previewFaceDetector)
                    .setFacing(CameraSource.CAMERA_FACING_BACK)
                    .setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)
                    .setRequestedFps(30.0f)
                    .build();
        }else {
            mCameraSource = new CameraSource.Builder(context, previewFaceDetector)
                    .setFacing(CameraSource.CAMERA_FACING_FRONT)
                    .setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)
                    .setRequestedFps(30.0f)
                    .build();
        }

        startCameraSource();
    }
    /**
     * Method to start camerasorurce.
     * **/
    private void startCameraSource() {

        if (mCameraSource != null) {
            try {
                mPreview.start(mCameraSource, mGraphicOverlay);
            } catch (IOException e) {
                mCameraSource.release();
                mCameraSource = null;
            }
        }else {
            logEvent("NoCameraSource");

            LiveFaceAuthHolder.getInstance().getLiveFaceAuthResultListener().onResult(new VishwamError(VishwamError.NO_CAMERA_SOURCE,"No Camera Source"), null, null);

        }
    }
    /**
     * Method to stop camrasource.
     * **/
    private void stopCameraSource() {
        mPreview.stop();
    }

    boolean okForStartCam = true;
    /**
     * Lifecycle event invoked by system on resume of activity from background.
     * **/
    @Override
    protected void onResume() {
        super.onResume();

        if (okForStartCam){
            if (wasActivityResumed) {
                createCameraSourceFront();
            }
            startCameraSource();
        }
    }
    /**
     * Method to take picture using camera1 by camrasource class.
     * **/
    public void takePicture() {

        if (mCameraSource != null) {
            mCameraSource.takePicture(cameraSourceShutterCallback, cameraSourcePictureCallback);
        }else {
            logEvent("NoCameraSource");

            LiveFaceAuthHolder.getInstance().getLiveFaceAuthResultListener().onResult(new VishwamError(VishwamError.NO_CAMERA_SOURCE,"No Camera Source"), null, null);

        }
    }
    /**
     * Lifecycle event invoked by system on pause to background.
     * */
    @Override
    protected void onPause() {
        super.onPause();
        wasActivityResumed = true;
        stopCameraSource();
    }
    /**
     * Lifecycle event invoked by the system on kill of activity.
     * */
    @Override
    protected void onDestroy() {
        super.onDestroy();
        stopCameraSource();

        if (previewFaceDetector != null) {
            previewFaceDetector.release();
        }else {
            logEvent("NoCameraSource");
            LiveFaceAuthHolder.getInstance().getLiveFaceAuthResultListener().onResult(new VishwamError(VishwamError.NO_CAMERA_SOURCE,"No Camera Source"), null, null);

        }
    }
    /**
     * Method to log livenesscall event.
     * **/
    public  void logEventLivenessCall(String eventName,String orn) {
        if(doSuccessLogs){
            Map<String, String> properties = new HashMap<>();
            properties.put("ReferenceId", orn);

            Analytics.trackEvent(eventName, properties);
        }


    }
    /**
     * Method invoked when activity passes results to this activity.
     * **/
    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        //dialog.dismiss();
        if (requestCode == 200) {
            switch (resultCode) {
                case RESULT_OK:
                    viewToHideCircle.setVisibility(View.VISIBLE);
                    mGraphicOverlay.setVisibility(View.INVISIBLE);
                    okForStartCam = false;

                    dialog.setMessage("Receiving Image...");
                    dialog.setCancelable(false);
                    dialog.show();

                    saveFile224(bitmap224);
                    break;

                case RESULT_CANCELED:
                    okForStartCam = true;
                    okForCapture = true;
                    framesAddedCount = 0;

                    Toast.makeText(this, "Photo rejected. Please retake picture", Toast.LENGTH_SHORT).show();
                    break;
            }
        }
    }
    /**
     * Method invoked onclick of buttons.
     * **/
    @Override
    public void onClick(View v) {
        int id = v.getId();
        if (id == R.id.button) {
            if (readyForAnalysis) {

                okForAnalysis = false;
                canAddData = false;

                if (okForCapture){
                    okForCapture = false;
                    takePicture = true;
                    //takePicture();
                }
            } else {
                Toast.makeText(VishwamFaceActivity.this, "Please wait while image analysis is being setup!", Toast.LENGTH_LONG).show();
            }
        }
    }
    /**
     * Method to change button at runtime.
     * **/
    public void buttonChange(final int n, final boolean b, final String instruction) {
        this.runOnUiThread(new Runnable() {
            @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
            public void run() {

                if (!b)straightFaceWarning.setText(instruction); else straightFaceWarning.setText("");
                button.setEnabled(b);
                button.setBackground(getResources().getDrawable(n));
            }
        });
    }
    /**
     * This method invoked by camerasource class onfirst availability of frame and onclick of button.
     * **/
    @Override
    public void onCapture(byte[] data , int angle) {

        dialog.setMessage("Receiving Image...");
        dialog.setCancelable(false);
        dialog.show();

        stopCameraSource();
        Bitmap OriginalBitmap = BitmapFactory.decodeByteArray(data, 0, data.length);

        Matrix matrix = new Matrix();
        matrix.postRotate(angle);
        Bitmap rotatedbitmap = Bitmap.createBitmap(OriginalBitmap,0,0,OriginalBitmap.getWidth(),OriginalBitmap.getHeight(),matrix,true);
        bitmap224 = libUtils.get224Bitmap(rotatedbitmap);
        squareBitmapFace = libUtils.getSquareBitmap(rotatedbitmap);
        squareBitmapFace = getCroppedFace(squareBitmapFace);

        /**** ================================ params from BE =============================**/

        int image_type = 0;

        if (paramBE!=null) {
            if (paramBE.has("image_type")) {
                try {
                    image_type = paramBE.getInt("image_type");
                } catch (JSONException e) {
                    e.printStackTrace();
                    image_type = 0;
                }
            }
        }

        if (image_type == 0) {

            if (squareBitmapFace.getWidth() > 450 && squareBitmapFace.getHeight() > 450) {
                squareFace450Bitmap = libUtils.getSquareCompressedBitmap(squareBitmapFace);
            } else {
                squareFace450Bitmap = squareBitmapFace;
            }

        } else {
            squareFace450Bitmap = squareBitmapFace;
        }

        /**** ================================ params from BE =============================**/

        
        /*if (squareBitmapFace.getWidth() > 450 && squareBitmapFace.getHeight() > 450){
            squareFace450Bitmap = libUtils.getSquareCompressedBitmap(squareBitmapFace);
        }else{
            squareFace450Bitmap = squareBitmapFace;
        }*/

        if (capture_type == 0){
            getFaceCoordinates(squareFace450Bitmap);
        }

        logFacePictureTakenEvent(shouldUseBackCamera);

        saveFile224(bitmap224);
    }
    /**
     * Method to log face Capture image taken.
     * **/
    public void logFacePictureTakenEvent(Boolean isBack) {
        if(doSuccessLogs){
            Map<String, String> properties = new HashMap<>();

            if (isBack) {
                properties.put("CamType","Back");
            } else {
                properties.put("CamType","Front");
            }

            Analytics.trackEvent("OnFacePictureTaken", properties);
        }

    }


    /**
     * class for create a thread for network operation.
     * **/

    @SuppressLint("StaticFieldLeak")
    public class ApiCallForLivenessCheck extends AsyncTask<String, String, CrashProof> {

        @Override
        protected CrashProof doInBackground(String... strings) {

            String url = "", server = "";

            /**** ================================ params from BE =============================**/


            /*if (paramBE != null) {
                if (paramBE.has("server")) {
                    try {
                        server = paramBE.getString("server");
                    } catch (JSONException e) {
                        e.printStackTrace();
                        server = "";
                    }

                } else {
                    server = "";
                }

                if (paramBE.has("url_live")) {
                    try {
                        url = paramBE.getString("url_live");
                    } catch (JSONException e) {
                        e.printStackTrace();
                        url = "";
                    }
                } else {
                    url = "";
                }
            }else {
                server= "";
                url = "";
            }*/

            /**** ================================ params from BE =============================**/


            switch (env) {
                case 0:
                    server = context.getString(R.string.staging_server);
                    url = "https://" + server + "/v1/check_liveness";
                    break;
                case 1:
                    server = context.getString(R.string.prod_server);
                    url = "https://" + server + "/v1/check_liveness";
                    break;
                case 2:
                    server = context.getString(R.string.replica_server);
                    url = "http://" + server + "/v1/check_liveness";
                    break;

            }

//            Log.e("vishwamSukshiUrl", url);


            OkHttpClient client;
            logEventLivenessCall("OnLivenessCall",referenceId);

            if(!server.equals("")){
                CertificatePinner certificatePinner = new CertificatePinner.Builder()
                        .add(server,"sha256/" + context.getString(R.string.certificateKey))
                        .build();

                 client = new OkHttpClient.Builder()
                        .connectTimeout(20, TimeUnit.SECONDS)
                        .writeTimeout(20, TimeUnit.SECONDS)
                        .readTimeout(20, TimeUnit.SECONDS)
                        .protocols(Collections.singletonList(HTTP_1_1))
                        .certificatePinner(certificatePinner)
                        .build();
            }else {
                return new CrashProof("Bad Request params", null);
            }

            final File imageFile = new File(pathFace224);
            final File imageFile2 = new File(path450);
            //Log.e("liveImageSize", String.valueOf(Integer.parseInt(String.valueOf(imageFile.length()/1024))));
            //Log.e("liveImageSize2", String.valueOf(Integer.parseInt(String.valueOf(imageFile2.length()/1024))));

            MultipartBody.Builder multipartBodyBuilder = new MultipartBody.Builder();
            multipartBodyBuilder.setType(MultipartBody.FORM);

            if (allowOnlyWhiteBackground.equals("yes")){
                allowOnlyWhiteBackground = "1";
            }else{
                allowOnlyWhiteBackground = "0";
            }

            if (allowEyesClosed.equals("yes")){
                allowEyesClosed = "0";
            }else{
                allowEyesClosed = "1";
            }

            multipartBodyBuilder.addFormDataPart("image", imageFile.getName(), RequestBody.create(MediaType.parse("image/jpg"), imageFile));
            multipartBodyBuilder.addFormDataPart("image2", imageFile2.getName(), RequestBody.create(MediaType.parse("image/jpg"), imageFile2));
            multipartBodyBuilder.addFormDataPart("app_id", app_id);
            //Log.e("vishwamSukshiAppId", app_id);
            multipartBodyBuilder.addFormDataPart("delay", delay);
            //Log.e("vishwamSukshiDelay", delay);
            multipartBodyBuilder.addFormDataPart("whiteBackground", allowOnlyWhiteBackground);
            multipartBodyBuilder.addFormDataPart("eyesCheck", allowEyesClosed);
            multipartBodyBuilder.addFormDataPart("referenceId", referenceId);
            multipartBodyBuilder.addFormDataPart("store_id", storeID);
            multipartBodyBuilder.addFormDataPart("version", sdk_version);
            multipartBodyBuilder.addFormDataPart("capture_type", String.valueOf(capture_type));
            //Log.e("capture_type", String.valueOf(capture_type));

            SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
            String authTok = prefs.getString("auth_token", "");
//            Log.e("authAuthTOken1", authTok);

            Request request = null;
            if(!url.equals("")){
                 request = new Request.Builder()
                        .header("Authorization","Bearer " +authTok)
                        .url(url)
                        .post(multipartBodyBuilder.build())
                        .build();
            }else {
                return new CrashProof("Bad Request params", null);
            }

            CrashProof crashProof;
            Response liveResponse;

            try {
                liveResponse = client.newCall(request).execute();
//                Log.e("vishwamSukshiLiveResp", String.valueOf(liveResponse));

                if (liveResponse.code() == 400){
                    return new CrashProof("Bad Request", null);
                }else if (liveResponse.code() == 401){
                    return new CrashProof("Unathorised request", null);
                }else if (liveResponse.code() == 500 || liveResponse.code() == 502 ){
                    return new CrashProof("Internal Server Error", null);
                }else {
                    Headers headers = liveResponse.headers();
                    String refID = headers.get("referenceid");
                    String reqId = headers.get("requestid");

                    ResponseBody responseBody = liveResponse.body();
                    JSONObject jsonObject = new JSONObject(responseBody.string());
                    String statusCode = jsonObject.getString("statusCode");

                    crashProof = new CrashProof(null, liveResponse);
                    crashProof.setReferenceid(refID);
                    crashProof.setRequestid(reqId);
                    crashProof.setHeaderStatusCode(liveResponse.code());
                    crashProof.setBodyStatusCode(statusCode);
                    crashProof.setResponseBodyJson(jsonObject);
                    return crashProof;
                }
            } catch (IOException e) {
                return new CrashProof("Request timed out", null);
            } catch (JSONException e) {
                e.printStackTrace();
                return new CrashProof("Json Exception", null);
            }
        }

        @Override
        protected void onPostExecute(CrashProof crashProof) {
            super.onPostExecute(crashProof);

            dialog.dismiss();

            if (crashProof.getResponse() != null) {

                if (crashProof.getHeaderStatusCode() == 502 || crashProof.getHeaderStatusCode() == 500){
                    //Log.e("vishwamLiveRes", String.valueOf(response.code()));

                    dialog.dismiss();
                    logFaceLivenessEvent(crashProof.getReferenceid(),"InternalServerError");

                    LiveFaceAuthHolder.getInstance().getLiveFaceAuthResultListener().onResult(new VishwamError(VishwamError.INTERNAL_SERVER_ERROR, "Internal Server Error"),null, null);
                    finish();
                }else if (crashProof.getHeaderStatusCode() == 400 ){
                    //Log.e("vishwamLiveRes", String.valueOf(response.code()));


                    dialog.dismiss();
                    logFaceLivenessEvent(crashProof.getReferenceid(),"BadRequest");

                    LiveFaceAuthHolder.getInstance().getLiveFaceAuthResultListener().onResult(new VishwamError(VishwamError.BAD_REQUEST, "Bad request"),null, null);
                    finish();
                }else if (crashProof.getHeaderStatusCode() == 401){


                    dialog.dismiss();
                    logFaceLivenessEvent(crashProof.getReferenceid(),"UnAuthRequest");

                    LiveFaceAuthHolder.getInstance().getLiveFaceAuthResultListener().onResult(new VishwamError(VishwamError.UN_AUTH, "Unauthorized request"),null, null);
                    finish();
                }else{
                    try {
                        JSONObject header = new JSONObject();
                        header.put("Reference-Id", crashProof.getReferenceid());
                        header.put("Request-Id", crashProof.getRequestid());

                        JSONObject jsonObject;
                        jsonObject = crashProof.getResponseBodyJson();
                        jsonObject.put("imageUri",path450);
                        //Log.e("vishwamLiveRes", String.valueOf(jsonObject));

                        if (jsonObject.has("statusCode")){
                            if (jsonObject.getString("statusCode").equals("200")) {


                                dialog.dismiss();
                                logFaceNetworkEvent("OnCompleteFaceCapture",crashProof.getReferenceid());

                                LiveFaceAuthHolder.getInstance().getLiveFaceAuthResultListener().onResult(null, jsonObject, header);
                                finish();
                            }else {

                                dialog.dismiss();
                                logFaceLivenessEvent(crashProof.getReferenceid(),jsonObject.getString("error"));

                                LiveFaceAuthHolder.getInstance().getLiveFaceAuthResultListener().onResult(new VishwamError(VishwamError.LOW_QUALITY_IMAGE, jsonObject.getString("error")), null, null);
                                finish();
                            }
                        }else {

                            dialog.dismiss();
                            logFaceLivenessEvent(crashProof.getReferenceid(),"Status Code Not Found");

                            LiveFaceAuthHolder.getInstance().getLiveFaceAuthResultListener().onResult(new VishwamError(10, "statusCode not found"), null, null);
                            finish();
                        }
                    } catch (JSONException e) {

                        dialog.dismiss();
                        logFaceLivenessEvent(crashProof.getReferenceid(),"JsonException");

                        LiveFaceAuthHolder.getInstance().getLiveFaceAuthResultListener().onResult(new VishwamError(VishwamError.RESPONSE_JSON_E, "JSON Exception"),null, null);
                        finish();
                    }
                }
            }else {

                dialog.dismiss();
                logFaceLivenessEvent(referenceId,"ResponseNull");

                LiveFaceAuthHolder.getInstance().getLiveFaceAuthResultListener().onResult(new VishwamError(VishwamError.RESPONSE_NULL, crashProof.getErrorType()),null, null);
                finish();
            }
        }
    }
    /**
     * Method to log face network event passing on parameters like event name and orn.
     * **/
    public void logFaceNetworkEvent(String eventName,String orn) {
        if(doSuccessLogs){
            Map<String, String> properties = new HashMap<>();
            properties.put("ReferenceId", orn);

            Analytics.trackEvent(eventName, properties);
        }

    }
    /**
     * Method to log face liveness event passing on parameters like orn and error.
     * **/
    public void logFaceLivenessEvent(String orn,String error) {

        Map<String, String> properties = new HashMap<>();
        properties.put("ReferenceId", orn);

        Analytics.trackEvent("FL"+limitTo50Chars(error), properties);

    }

    /**
     * Method to check internet connectivity.
     * **/
    private boolean checkInternetConnection() {
        ConnectivityManager conMgr = (ConnectivityManager) context.getSystemService (Context.CONNECTIVITY_SERVICE);
        return conMgr.getActiveNetworkInfo() != null
                && conMgr.getActiveNetworkInfo().isAvailable()
                && conMgr.getActiveNetworkInfo().isConnected();
    }

    /**
     * Lifecycle event invoked on back press.
     * **/
    @Override
    public void onBackPressed() {}

    /**
     * Method to limit event names to 50 char only.
     * **/

    public String limitTo50Chars(String string){
        //Log.e("ASD",string);
        string =  string.replaceAll("[^a-zA-Z0-9]", "");
        //Log.d("ASDXCV",string);
       // Log.d("ASD",String.valueOf(string.length()));
        if(string.length() > 49 ){

            return  string.substring(0, 40);
        }else {
            return string;
        }
    }
}