package org.keyscience.fragments;

import android.Manifest;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
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.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.Fragment;
import android.support.v4.app.NotificationCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.FileProvider;
import android.text.Html;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import net.gotev.uploadservice.MultipartUploadRequest;
import net.gotev.uploadservice.ServerResponse;
import net.gotev.uploadservice.UploadInfo;

import org.keyscience.R;
import org.keyscience.app.Constants;
import org.keyscience.app.HomeActivity;
import org.keyscience.geolocation.GPSTracker;
import org.keyscience.network.SingleUploadBroadcastReceiver;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.UUID;

import static android.app.Activity.RESULT_CANCELED;
import static android.app.Activity.RESULT_OK;
import static org.keyscience.fragments.SectionsStatePagerAdapter.getInstanceSectionsStatePagerAdapter;

public class CspRecordFragment extends Fragment implements View.OnClickListener {

    private static final int PERMISSIONS_NEEDED = 1;

    //  http://www.keyscience.org/wp-content/maps/fileupload.php
    public static final String UPLOAD_URL = "http://www.keyscience.org/wp-content/app/appPhotoUpload.php";
    static final int REQUEST_TAKE_PHOTO = 1;

    private SingleUploadBroadcastReceiver uploadReceiver = new SingleUploadBroadcastReceiver();

    private ImageView pictureThumb;

    private Button sendPhoto;
    //    private Button btnPrevSubmission;
    private Button btnCapturePicture;

    private EditText name;
    private EditText title;
    private EditText desc;

    private TextView contactForm2;

    private ProgressDialog dialog;

    private GPSTracker gps;
    private double dLatitude;
    private double dLongitude;

    private String currentPhotoPath;
    private String imageFileName;

    private String imgTitle;
    private String imgDesc;
    private String userName;

    HashMap<String, Integer> fragmentMap = new HashMap<>();

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

        View view = inflater.inflate(R.layout.csp_record_fragment, container, false);

        AddChildActivities();

        pictureThumb = view.findViewById(R.id.thumbnail);
        name = view.findViewById(R.id.name);
        title = view.findViewById(R.id.img_title);
        desc = view.findViewById(R.id.img_desc);

        contactForm2 = view.findViewById(R.id.contactText2);
        sendPhoto = view.findViewById(R.id.sendButton);
        btnCapturePicture = view.findViewById(R.id.recordButton);
//        btnPrevSubmission = view.findViewById(R.id.submissionsButton);

        // Paragraphs that require HTML
        String userName = getResources().getString(R.string.userName);
        TextView txtName = view.findViewById(R.id.txtUserName);
        txtName.setText(Html.fromHtml(userName));

        String imgTitle = getResources().getString(R.string.photoTitle);
        TextView txtTitle = view.findViewById(R.id.txtPhotoTitle);
        txtTitle.setText(Html.fromHtml(imgTitle));

        String imgDesc = getResources().getString(R.string.photoDesc);
        TextView txtDesc = view.findViewById(R.id.txtPhotoDesc);
        txtDesc.setText(Html.fromHtml(imgDesc));

        //btnPrevSubmission.setOnClickListener(this);
        btnCapturePicture.setOnClickListener(this);
        sendPhoto.setOnClickListener(this);

        contactForm2.setOnClickListener(this);
        pictureThumb.setOnClickListener(this);

        // Checking camera availability
        checkCameraSupport();

        resetFragmentData();

        return view;
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.thumbnail:
                if (currentPhotoPath != null) {
                    File photoFile = new File(currentPhotoPath);
                    Uri photoURI = FileProvider.getUriForFile(getContext(), "org.keyscience.fileprovider", photoFile);

                    Intent intent = new Intent();
                    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                    intent.setAction(Intent.ACTION_VIEW);
                    intent.setDataAndType(photoURI, "image/*");
                    startActivity(intent);
                }
                break;

            case R.id.recordButton:
                // capture picture
                // check for permissions first, then take picture if permissions granted
                if (hasPermissions(getContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA, Manifest.permission.ACCESS_FINE_LOCATION)) {
                    dispatchTakePictureIntent();
                }
                break;

            case R.id.sendButton:
                // send picture
                sendSubmission();
                break;

//            case R.id.submissionsButton:
//                // open previous submissions activity
//                Intent intent = new Intent(getContext(), ReviewMapActivity.class);
//                if (intent.resolveActivity(getActivity().getPackageManager()) != null) {
//                    startActivity(intent);
//                }
//                break;

            case R.id.contactText2:
                ((HomeActivity) getContext()).setViewPager(fragmentMap.get(Constants.CONTACT_US));
                break;
        }

    }

    public void checkCameraSupport() {
        if (!cameraSupportFlag()) {
            Toast.makeText(getContext(),
                    "Sorry! Your device doesn't support camera",
                    Toast.LENGTH_LONG).show();
        }
    }

    private void resetFragmentData() {
        name.setText("");
        title.setText("");
        desc.setText("");
        pictureThumb.setImageResource(R.drawable.lab_record);

        currentPhotoPath = null;
    }

    private boolean cameraSupportFlag() {
        if (getContext().getPackageManager().hasSystemFeature(
                PackageManager.FEATURE_CAMERA)) {
            return true;
        } else {
            // no camera
            return false;
        }
    }

    public void createChannel(String title, String content, int color) {
        int NOTIFICATION_ID = 234;

        NotificationManager notificationManager = (NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE);

        String CHANNEL_ID = "KeyScience";
        CharSequence name = "KeyScience";
        String Description = "Image Upload";

        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
            int importance = NotificationManager.IMPORTANCE_DEFAULT;
            NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID, name, importance);
            mChannel.setDescription(Description);
            mChannel.enableLights(true);
            mChannel.setLightColor(color);
            mChannel.enableVibration(true);
            mChannel.setVibrationPattern(new long[]{100, 100, 100});
            mChannel.setShowBadge(false);
            notificationManager.createNotificationChannel(mChannel);
        }

        NotificationCompat.Builder builder = new NotificationCompat.Builder(getContext(), CHANNEL_ID)
                .setSmallIcon(R.mipmap.ic_launcher);
        builder.setContentTitle(title);
        builder.setContentText(content);

        notificationManager.notify(NOTIFICATION_ID, builder.build());
    }

    private void dispatchTakePictureIntent() {
        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        // Ensure that there's a camera activity to handle the intent
        if (takePictureIntent.resolveActivity(getContext().getPackageManager()) != null) {
            // Create the File where the photo should go
            File photoFile = null;
            try {
                photoFile = createImageFile();
            } catch (IOException ex) {
                // Error occurred while creating the File

            }

            Uri photoURI;

            // Continue only if the File was successfully created
            if (photoFile != null) {
                if (Build.VERSION_CODES.N <= android.os.Build.VERSION.SDK_INT) {
                    photoURI = FileProvider.getUriForFile(getContext(),
                            "org.keyscience.fileprovider",
                            photoFile);
                } else {
                    photoURI = Uri.fromFile(photoFile);
                }
                takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
                startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO);
            }
        }
    }

    public void sendSubmission() {
        if (currentPhotoPath == null) {
            Toast.makeText(getContext(), "Missing photo", Toast.LENGTH_LONG).show();
            return;
        }

        if (name.getText().toString().equals("") || title.getText().toString().equals("") || desc.getText().toString().equals("")) {
            Toast.makeText(getContext(), "Missing required fields", Toast.LENGTH_LONG).show();
            return;
        }

        imgTitle = title.getText().toString();
        imgDesc = desc.getText().toString();
        userName = name.getText().toString();

        gps = new GPSTracker(getContext());
        if (gps.canGetLocation()) {
            dLatitude = gps.getLatitude();
            dLongitude = gps.getLongitude();
        } else {
            Toast.makeText(getContext(), "Location services turned off. Please turn on Location services to submit!", Toast.LENGTH_LONG).show();
            return;
        }

        String imgFileName = imageFileName + ".jpg";    // Add extension

        // Compress image prior to uploading
        File f = new File(resizeAndCompressImageBeforeSend(getContext(), currentPhotoPath, imgFileName));
        Uri contentUri = Uri.fromFile(f);

        dialog = new ProgressDialog(getContext());
        dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        dialog.setIcon(R.mipmap.ic_launcher);
        dialog.setMessage("Uploading photo, please wait.");
        dialog.setMax(100);
        dialog.show();

        uploadMultipart(contentUri);
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        // if the result is capturing Image
        if (requestCode == REQUEST_TAKE_PHOTO) {
            if (resultCode == RESULT_OK) {
                // Staging area for image upload: Ask user for Name, Title, Description

                // Thumbnail
                File file = new File(currentPhotoPath);
                Bitmap bitmap = null;
                try {
                    bitmap = MediaStore.Images.Media
                            .getBitmap(getContext().getContentResolver(), Uri.fromFile(file));

                    int imgOrientation = getOrientationDegree(currentPhotoPath);
                    bitmap = rotateBitmap(bitmap, imgOrientation);

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

                if (bitmap != null)
                    pictureThumb.setImageBitmap(bitmap);

            } else if (resultCode == RESULT_CANCELED) {

                // user cancelled Image capture
                Toast.makeText(getContext(),
                        "User cancelled image capture.", Toast.LENGTH_SHORT)
                        .show();

                currentPhotoPath = null;

            } else {
                // failed to capture image
                Toast.makeText(getContext(),
                        "Sorry! Failed to capture image.", Toast.LENGTH_SHORT)
                        .show();

                currentPhotoPath = null;
            }

        }
    }

    public void AddChildActivities() {
        SectionsStatePagerAdapter sectionsStatePagerAdapter = getInstanceSectionsStatePagerAdapter();
        for (int i = 0; i < sectionsStatePagerAdapter.getCount(); i++) {
            fragmentMap.put(sectionsStatePagerAdapter.getFragmentName(i), i);
        }
    }

    /***
     *  Image Upload
     ***/
    private File createImageFile() throws IOException {
        // Create an image file name
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
        imageFileName = "JPEG_" + timeStamp + "_";
        File storageDir = getContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES);
        File image = File.createTempFile(
                imageFileName,  /* prefix */
                ".jpg",         /* suffix */
                storageDir      /* directory */
        );

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

        return image;
    }

    public void uploadMultipart(Uri uri) {

        //getting the actual path of the image
        String path = uri.getPath();

        // Uploading code
        try {
            String uploadId = UUID.randomUUID().toString();
            uploadReceiver.setUploadID(uploadId);

            //Creating a multi part request
            new MultipartUploadRequest(getContext(), uploadId, UPLOAD_URL)
                    .addFileToUpload(path, "image") // Adding file
                    .addParameter("file_name", imageFileName) // Adding text parameter to the request

                    .addParameter("title", imgTitle) // Adding text parameter to the request
                    .addParameter("desc", imgDesc) // Adding text parameter to the request
                    .addParameter("lat", String.valueOf(dLatitude))
                    .addParameter("long", String.valueOf(dLongitude))
                    .addParameter("name", userName) // Adding text parameter to the request

                    .setAutoDeleteFilesAfterSuccessfulUpload(true)  // Avoid file clutter, but deleting it also prevents upload sometimes..
                    .setMaxRetries(2)
                    .setDelegate(new SingleUploadBroadcastReceiver() {
                        @Override
                        public void onProgress(Context context, UploadInfo uploadInfo) {
                            // your code here
                            Log.d("On Progress", String.valueOf(uploadInfo));
                            dialog.setProgress(uploadInfo.getProgressPercent());
                        }

                        @Override
                        public void onError(Context context, UploadInfo uploadInfo, ServerResponse serverResponse, Exception exception) {
                            Log.d("On error", String.valueOf(exception));

                            Toast.makeText(getContext(),
                                    String.valueOf(exception), Toast.LENGTH_LONG)
                                    .show();

                            dialog.dismiss();
                            createChannel("Image Upload Error", serverResponse.getBodyAsString(), Color.RED);
                        }

                        @Override
                        public void onCompleted(Context context, UploadInfo uploadInfo, ServerResponse serverResponse) {
                            Log.d("serverResponse", String.valueOf(serverResponse));
                            dialog.dismiss();
//                            Toast.makeText(getContext(),
//                                    "Image uploaded!", Toast.LENGTH_LONG)
//                                    .show();

                            // Only reset if we successfully send data
                            resetFragmentData();

                            // Read whether or not image upload was successful server side
                            createChannel("Image Upload", serverResponse.getBodyAsString(), Color.GREEN);
                        }

                        @Override
                        public void onCancelled(Context context, UploadInfo uploadInfo) {
                            // your code here
                            dialog.dismiss();
                            Log.d("Cancelled by user.", String.valueOf(uploadInfo));
                        }
                    })
                    .startUpload(); //Starting the upload

        } catch (Exception exc) {
            Toast.makeText(getContext(), exc.getMessage(), Toast.LENGTH_SHORT).show();
        }
    }


    /***
     *  Image Orientation
     ***/

    public static int getOrientationDegree(String imagePath) {
        if (TextUtils.isEmpty(imagePath)) {
            return 0;
        }

        try {
            final ExifInterface exifInterface = new ExifInterface(imagePath);
            final int attributeInt = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED);
            switch (attributeInt) {
                case ExifInterface.ORIENTATION_ROTATE_90:
                    return 90;
                case ExifInterface.ORIENTATION_ROTATE_180:
                    return 180;
                case ExifInterface.ORIENTATION_ROTATE_270:
                    return 270;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return 0;
    }

    public static Bitmap rotateBitmap(Bitmap bitmap, int degree) {
        if (degree == 0 || bitmap == null) {
            return bitmap;
        }

        final Matrix matrix = new Matrix();
        matrix.setRotate(degree, bitmap.getWidth() / 2, bitmap.getHeight() / 2);
        return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
    }


    /***
     *  Image Compression
     ***/

    public static String resizeAndCompressImageBeforeSend(Context context, String filePath, String fileName) {
        final int MAX_IMAGE_SIZE = 700 * 1024; // max final file size in kilobytes

        // First decode with inJustDecodeBounds=true to check dimensions of image
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(filePath, options);

        // Calculate inSampleSize(First we are going to resize the image to 800x800 image, in order to not have a big but very low quality image.
        //resizing the image will already reduce the file size, but after resizing we will check the file size and start to compress image
        options.inSampleSize = calculateInSampleSize(options, 800, 800);

        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;
        options.inPreferredConfig = Bitmap.Config.ARGB_8888;

        Bitmap bmpPic = BitmapFactory.decodeFile(filePath, options);

        // Correct issues with image rotation
        int imgOrientation = getOrientationDegree(filePath);
        bmpPic = rotateBitmap(bmpPic, imgOrientation);

        int compressQuality = 100; // quality decreasing by 5 every loop.
        int streamLength;
        do {
            ByteArrayOutputStream bmpStream = new ByteArrayOutputStream();
            Log.d("compressBitmap", "Quality: " + compressQuality);
            bmpPic.compress(Bitmap.CompressFormat.JPEG, compressQuality, bmpStream);
            byte[] bmpPicByteArray = bmpStream.toByteArray();
            streamLength = bmpPicByteArray.length;
            compressQuality -= 5;
            Log.d("compressBitmap", "Size: " + streamLength / 1024 + " kb");
        } while (streamLength >= MAX_IMAGE_SIZE);

        try {
            //save the resized and compressed file to disk cache
            Log.d("compressBitmap", "cacheDir: " + context.getCacheDir());
            FileOutputStream bmpFile = new FileOutputStream(context.getCacheDir() + fileName);
            bmpPic.compress(Bitmap.CompressFormat.JPEG, compressQuality, bmpFile);
            bmpFile.flush();
            bmpFile.close();
        } catch (Exception e) {
            Log.e("compressBitmap", "Error on saving file");
        }

        //Let's delete the old, larger file that stays in cache
        File file = new File(filePath);
        if (file.exists())
            file.delete();

        //return the path of resized and compressed file
        return context.getCacheDir() + fileName;
    }

    public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
        String debugTag = "MemoryInformation";

        final int height = options.outHeight;
        final int width = options.outWidth;
        Log.d(debugTag, "image height: " + height + "---image width: " + width);
        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;
            }
        }
        Log.d(debugTag, "inSampleSize: " + inSampleSize);
        return inSampleSize;
    }

    /**
     * request permission and check if all granted
     *
     * @param context     apps context
     * @param permissions permissions to request
     * @return true if all granted, false otherwise
     */
    public boolean hasPermissions(Context context, String... permissions) {

        if (context != null && permissions != null) {
            ActivityCompat.requestPermissions(getActivity(), permissions, PERMISSIONS_NEEDED);

            for (String permission : permissions) {
                if (ActivityCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
                    return false;
                }
            }
        }
        return true;
    }
}
