/****************************************************************************
*
* CanvasActivity - deals with setting up and handling drawing
*
* Programmer: Annicka Rabida, May 2021
* 
***************************************************************************/
package com.example.paintingphotos;

import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.PopupMenu;

import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BlurMaskFilter;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.provider.MediaStore;
import android.util.Log;

import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView;

import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;

import java.text.SimpleDateFormat;
import java.util.Date;

public class CanvasActivity extends AppCompatActivity {
    Bitmap selectedBitmap;
    boolean useBlank, usedCamera;
    ImageButton ibStroke, ibUndo, ibRedo, ibEyeDropper, ibColor, ibBrushes, ibSave, ibBack, ibClear;
    TextView tvSelectedBrush;
    static ImageView ivCurrentColor;
    private static CanvasView customCanvas;
    private final int HUE = 0;
    private final int SATURATION = 1;
    private final int VALUE = 2;
    boolean changedColor = false;
    boolean eyeDropperEnabled = false;
    private static int newStrokeSize = 4;
    private static int newStrokeHardness = 1;

    private final int EYEDROPPER = 1;
    private ScaleGestureDetector mScaleGestureDetector;
    private float mScaleFactor = 1.0f;
    private static boolean zoomEnabled = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_canvas);
        customCanvas = (CanvasView) findViewById(R.id.signature_canvas);
        //Log.w("canvas MSG", "customCanvas: " + customCanvas);
        ivCurrentColor = (ImageView) findViewById(R.id.ivCurrentColor);
        //Log.w("canvas MSG", "bitmap: "+ selectedBitmap);
        selectedBitmap = MainActivity.selectedBitmap;
        useBlank = ChoicesActivity.useBlank;
        usedCamera = ChoicesActivity.usedCamera;
        if (useBlank)
            customCanvas.setBackgroundColor(Color.WHITE);
        else{
            //Log.w("canvas MSG", "global bitmap: "+ selectedBitmap);
            Drawable d;
            if (usedCamera){
                String currentPhotoPath = ChoicesActivity.currentPhotoPath;
                Bitmap b = getPic(currentPhotoPath);
                b = resizeImage(b);
                d = new BitmapDrawable(getResources(), b);
            } else{
                selectedBitmap = resizeImage(selectedBitmap);
                resizeLayout(selectedBitmap);
                d = new BitmapDrawable(getResources(), selectedBitmap);
            }
            customCanvas.setBackground(d);
        }

        customCanvas=(CanvasView)findViewById(R.id.signature_canvas);
        mScaleGestureDetector = new ScaleGestureDetector(this, new ScaleListener());

        tvSelectedBrush = (TextView)findViewById(R.id.tvBrushSelected);
        tvSelectedBrush.setText(customCanvas.getBrushType());

        /****************************************************************************
         *
         * BACK BUTTON
         *
         ***************************************************************************/
        ibBack = (ImageButton) findViewById(R.id.ibBack);
        ibBack.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                AlertDialog.Builder builder = new AlertDialog.Builder(CanvasActivity.this);
                builder.setMessage(R.string.back_button_message);
                builder.setTitle(R.string.back_button_title);
                builder.setCancelable(false);
                builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        Intent choicesIntent = new Intent(getApplicationContext(), ChoicesActivity.class);
                        startActivity(choicesIntent);
                        finish();
                    }
                });
                builder.setNegativeButton("No", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        dialog.cancel();
                    }
                });
                AlertDialog alert = builder.create();
                alert.show();
            }
        });

        /****************************************************************************
         *
         * CLEAR BUTTON
         *
         ***************************************************************************/
        ibClear = (ImageButton) findViewById(R.id.ibClear);
        ibClear.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                AlertDialog.Builder builder = new AlertDialog.Builder(CanvasActivity.this);
                builder.setMessage(R.string.clear_button_message);
                builder.setTitle(R.string.clear_button_title);
                builder.setCancelable(false);
                builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        customCanvas = (CanvasView) findViewById(R.id.signature_canvas);
                        customCanvas.clearCanvas();
                    }
                });
                builder.setNegativeButton("No", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        dialog.cancel();
                    }
                });
                AlertDialog alert = builder.create();
                alert.show();
            }
        });

        /****************************************************************************
         *
         * UNDO BUTTON
         *
         ***************************************************************************/
        ibUndo = (ImageButton) findViewById(R.id.ibUndo);
        ibUndo.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                customCanvas = (CanvasView) findViewById(R.id.signature_canvas);
                customCanvas.undoStroke();
            }
        });

        /****************************************************************************
         *
         * REDO BUTTON
         *
         ***************************************************************************/
        ibRedo = (ImageButton) findViewById(R.id.ibRedo);
        ibRedo.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                customCanvas = (CanvasView) findViewById(R.id.signature_canvas);
                customCanvas.redoStroke();
            }
        });

        /****************************************************************************
         *
         * SHARE BUTTON
         *
         ***************************************************************************/
        ibSave = (ImageButton) findViewById(R.id.ibShare);
        ibSave.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                AlertDialog.Builder builder = new AlertDialog.Builder(CanvasActivity.this);
                builder.setMessage(R.string.share_button_message);
                builder.setTitle(R.string.share_button_title);
                builder.setCancelable(false);
                builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        Bitmap b;
                        b = Bitmap.createBitmap(customCanvas.getWidth(), customCanvas.getHeight(), Bitmap.Config.ARGB_8888);
                        Canvas c = new Canvas(b);
                        customCanvas.draw(c);
                        String saveTitle;
                        saveTitle = new SimpleDateFormat("yyMMdd_HHmmss").format(new Date());
                        MediaStore.Images.Media.insertImage(getContentResolver(), b, "painting_photos_" + saveTitle , "Painting Photos Artwork");  // Saves the image.
                        Toast.makeText(getApplicationContext(),"Image Saved",Toast.LENGTH_SHORT).show();
                    }
                });
                builder.setNegativeButton("No", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        dialog.cancel();
                    }
                });
                AlertDialog alert = builder.create();
                alert.show();
            }
        });

        /****************************************************************************
         *
         * BRUSHES BUTTON
         *
         ***************************************************************************/
        ibBrushes = (ImageButton) findViewById(R.id.ibBrushes);
        ibBrushes.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                customCanvas = (CanvasView) findViewById(R.id.signature_canvas);
                PopupMenu popup = new PopupMenu(CanvasActivity.this, ibBrushes);
                popup.getMenuInflater().inflate(R.menu.brushes_popup_menu, popup.getMenu());
                popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
                    public boolean onMenuItemClick(MenuItem item) {
                        String itemClicked = item.getTitle().toString();
                        customCanvas.setBrush(itemClicked);
                        if (customCanvas.disableColorsForBrush()){
                            ibColor.setClickable(false);
                            ibColor.setImageDrawable(getDrawable(R.drawable.colors_light));
                            ibEyeDropper.setClickable(false);
                            ibEyeDropper.setImageDrawable(getDrawable(R.drawable.eyedropper_light));
                        } else {
                            ibColor.setClickable(true);
                            ibColor.setImageDrawable(getDrawable(R.drawable.colors));
                            ibEyeDropper.setClickable(true);
                            ibEyeDropper.setImageDrawable(getDrawable(R.drawable.eyedropper));
                        }
                        setCurrentColor(customCanvas.getColor());
                        tvSelectedBrush.setText(customCanvas.getBrushType());
                        return true;
                    }
                });
                popup.show();/**SHOW POP UP MENU**/
            }
        });//closing the setOnClickListener method

        /****************************************************************************
         *
         * STROKE BUTTON
         *
         ***************************************************************************/
        ibStroke = (ImageButton) findViewById(R.id.ibStroke);
        ibStroke.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                customCanvas = (CanvasView) findViewById(R.id.signature_canvas);

                final Dialog colorDialog = new Dialog(CanvasActivity.this);
                colorDialog.setContentView(R.layout.stroke_customizer);
                colorDialog.setTitle("Brush Customizer");
                colorDialog.show();

                Button btnSelect = (Button) colorDialog.findViewById(R.id.btnSelectStrokeChange);
                Button btnCancel = (Button) colorDialog.findViewById(R.id.btnCancelStrokeChange);
                ImageView ivStrokeSize = (ImageView) colorDialog.findViewById(R.id.ivStrokeSize);
                SeekBar sbThickness = (SeekBar) colorDialog.findViewById(R.id.sbThickness);
                SeekBar sbHardness = (SeekBar) colorDialog.findViewById(R.id.sbHardness);

                newStrokeHardness = (int)customCanvas.getBrushHardness();

                float circleHardness = customCanvas.getBrushHardness()/2;
                final Bitmap bitmapStrokeSize = Bitmap.createBitmap(100,100,Bitmap.Config.ARGB_8888);
                final Canvas canvas = new Canvas(bitmapStrokeSize);
                final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
                paint.setColor(customCanvas.getColor());
                drawCircle(circleHardness, paint, canvas);

                ivStrokeSize.setImageBitmap(bitmapStrokeSize);
                /** if select button is clicked, close the custom dialog and set new color **/
                btnSelect.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        customCanvas.setStrokeSize(newStrokeSize);
                        customCanvas.setBrushHardness(newStrokeHardness);
                        colorDialog.dismiss();
                    }
                });

                /** if button is clicked, close the custom dialog **/
                btnCancel.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        colorDialog.dismiss();
                    }
                });
                sbThickness.setProgress((int)(customCanvas.getStrokeSize()));
                sbThickness.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
                    @Override
                    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                        Log.w("canvas MSG", "progress: " + progress);
                        newStrokeSize = progress;
                        float circleHardness = newStrokeHardness/2;
                        drawCircle(circleHardness, paint, canvas);
                        ivStrokeSize.setImageBitmap(bitmapStrokeSize);
                    }
                    @Override
                    public void onStartTrackingTouch(SeekBar seekBar) {
                    }
                    @Override
                    public void onStopTrackingTouch(SeekBar seekBar) {
                    }
                });
                sbHardness.setProgress((int)(21-(customCanvas.getBrushHardness())));
                sbHardness.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
                    @Override
                    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                        newStrokeHardness = 21 - progress;
                        float circleHardness = newStrokeHardness/2;
                        drawCircle(circleHardness, paint, canvas);
                    }
                    @Override
                    public void onStartTrackingTouch(SeekBar seekBar) {
                    }
                    @Override
                    public void onStopTrackingTouch(SeekBar seekBar) {
                    }
                });
            }
        });

        /****************************************************************************
         *
         * CURRENT COLOR BOX
         *
         ***************************************************************************/
        setCurrentColor(customCanvas.getColor());

        /****************************************************************************
         *
         * EYEDROPPER
         *
         ***************************************************************************/
        ibEyeDropper = (ImageButton) findViewById(R.id.ibEyeDropper);
        ibEyeDropper.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!eyeDropperEnabled){
                    customCanvas.setEyeDropperActive(true);
                    disableButtons(EYEDROPPER);
                    ibEyeDropper.setImageDrawable(getDrawable(R.drawable.eyedropper_blue));
                } else{
                    customCanvas.setEyeDropperActive(false);
                    reEnableButtons(EYEDROPPER);
                    ibEyeDropper.setImageDrawable(getDrawable(R.drawable.eyedropper));
                }
                eyeDropperEnabled = !eyeDropperEnabled;
            }
        });

        /****************************************************************************
         *
         * COLOR PICKER
         *
         ***************************************************************************/
        ibColor = (ImageButton) findViewById(R.id.ibColorPalette);
        ibColor.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                customCanvas = (CanvasView) findViewById(R.id.signature_canvas);

                final Dialog colorDialog = new Dialog(CanvasActivity.this);
                colorDialog.setContentView(R.layout.color_palette);
                colorDialog.setTitle("Color Picker");
                colorDialog.show();

                Button btnSelect = (Button) colorDialog.findViewById(R.id.btnSelectColor);
                Button btnCancel = (Button) colorDialog.findViewById(R.id.btnCancelColor);
                TextView tvColorValue = (TextView) colorDialog.findViewById(R.id.tvColorValue);
                ImageView ivColorWheel = (ImageView) colorDialog.findViewById(R.id.ivColorWheel);
                SeekBar sbBrightness = (SeekBar) colorDialog.findViewById(R.id.sbBrightness);
                float changingHSV[] = new float[3];
                for(int i = 0; i < 3; i++)
                    changingHSV[i] = customCanvas.getHSV(i);
                tvColorValue.setBackgroundColor(customCanvas.getColor());
                /** if select button is clicked, close the custom dialog and set new color **/
                btnSelect.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        if (changedColor){
                            customCanvas.setColor(changingHSV);
                            changedColor = false;
                            setCurrentColor(customCanvas.getColor());
                        }
                        colorDialog.dismiss();
                    }
                });

                /** if button is clicked, close the custom dialog **/
                btnCancel.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        colorDialog.dismiss();
                    }
                });

                ivColorWheel.setDrawingCacheEnabled(true);
                ivColorWheel.buildDrawingCache(true);
                ivColorWheel.setOnTouchListener(new View.OnTouchListener() {
                    @Override
                    public boolean onTouch(View view, MotionEvent event) {
                        if (event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE){
                            Bitmap bitmap = ivColorWheel.getDrawingCache();
                            int x = (int)event.getX();
                            int y = (int)event.getY();
                            if (x>0 && y>0 && y < bitmap.getHeight() && x < bitmap.getWidth()){
                                int touchedRGB = bitmap.getPixel((int)event.getX(), (int)event.getY());
                                int r = Color.red(touchedRGB);
                                int g = Color.green(touchedRGB);
                                int b = Color.blue(touchedRGB);
                                changedColor = true;
                                float oldBrightness = changingHSV[VALUE];
                                Color.RGBToHSV(r,g,b,changingHSV);
                                changingHSV[VALUE] = oldBrightness;
                                sbBrightness.setProgress((int)(changingHSV[VALUE]*100));
                                //tvColorValue.setText("touched color: " + "#" + Integer.toHexString(touchedRGB));
                                tvColorValue.setBackgroundColor(Color.HSVToColor(changingHSV));
                                //Log.w("canvas MSG", "hex value: "+Integer.toHexString(touchedRGB));
                            }
                        }
                        return true;
                    };
                });
                sbBrightness.setProgress((int)(customCanvas.getBrightness()*100));
                sbBrightness.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
                    @Override
                    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                        customCanvas.setBrightness((float)progress);
                        changingHSV[VALUE] = (float)progress / 100;
                        changedColor = true;
                        tvColorValue.setBackgroundColor(Color.HSVToColor(changingHSV));
                    }
                    @Override
                    public void onStartTrackingTouch(SeekBar seekBar) {
                    }
                    @Override
                    public void onStopTrackingTouch(SeekBar seekBar) {
                    }
                });
            }
        });
    }

    /*****************************************************************************
     *
     * MISC FUNCTIONS
     *
     ****************************************************************************/
    /****************************************************************************
     * drawCircle:
     *  handles drawing the circle preview on the stroke customizer popup menu
     ***************************************************************************/
    public void drawCircle(float circleHardness, Paint paint, Canvas canvas){
        canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
        if (circleHardness == 0.0)
            circleHardness = (float) 0.1;
        Log.w("msg canvas", "circleHardness: " + circleHardness);
        BlurMaskFilter circleFilter = new BlurMaskFilter(circleHardness, BlurMaskFilter.Blur.NORMAL);
        paint.setMaskFilter(circleFilter);
        canvas.drawCircle(50,50,(newStrokeSize/3), paint);
    }

    /****************************************************************************
     * setCurrentColor
     * sets the current color in the circle preview on the bottom bar
     ***************************************************************************/
    public static void setCurrentColor(int color){
        Bitmap bitmapStrokeSize = Bitmap.createBitmap(100,100,Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmapStrokeSize);
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(color);
        canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
        canvas.drawCircle(50,50,30, paint);
        ivCurrentColor.setImageBitmap(bitmapStrokeSize);
    }

    /****************************************************************************
     * disableButtons
     * disables other buttons when the user is using the eyedropper tool
     ***************************************************************************/
    public void disableButtons(int btn_pressed){
        ibBrushes.setClickable(false);
        ibBrushes.setImageDrawable(getDrawable(R.drawable.brushes_light));
        ibColor.setClickable(false);
        ibColor.setImageDrawable(getDrawable(R.drawable.colors_light));
        ibUndo.setClickable(false);
        ibUndo.setImageDrawable(getDrawable(R.drawable.undo_light));
        ibRedo.setClickable(false);
        ibRedo.setImageDrawable(getDrawable(R.drawable.redo_light));
        ibStroke.setClickable(false);
        ibStroke.setImageDrawable(getDrawable(R.drawable.stroke_light));
        ibClear.setClickable(false);
        ibClear.setImageDrawable(getDrawable(R.drawable.clear_light));
    }

    /****************************************************************************
     * reEnableButtons
     * re-enables other buttons after user is done using the eyedropper tool
     ***************************************************************************/
    public void reEnableButtons(int btn_pressed){
        ibBrushes.setClickable(true);
        ibBrushes.setImageDrawable(getDrawable(R.drawable.brushes));
        ibColor.setClickable(true);
        ibColor.setImageDrawable(getDrawable(R.drawable.colors));
        ibUndo.setClickable(true);
        ibUndo.setImageDrawable(getDrawable(R.drawable.undo));
        ibRedo.setClickable(true);
        ibRedo.setImageDrawable(getDrawable(R.drawable.redo));
        ibStroke.setClickable(true);
        ibStroke.setImageDrawable(getDrawable(R.drawable.stroke));
        ibClear.setClickable(true);
        ibClear.setImageDrawable(getDrawable(R.drawable.clear));
    }

    /****************************************************************************
     * resizeImage
     * resizes the image whether it comes from the camera or phone gallery
     ***************************************************************************/
    public Bitmap resizeImage(Bitmap b){
        double newW;
        double newH;
        newW = b.getWidth();
        newH = b.getHeight();

        while(newW > 1090 || newH > 1400){
            newW = newW/1.1;
            newH = newH/1.1;
        }
        if (usedCamera) {
            Bitmap newBitmap = Bitmap.createScaledBitmap(b, (int) newW, (int) newH, true);
            Matrix matrix = new Matrix();
            matrix.postRotate(90);
            Bitmap scaledBitmap = Bitmap.createScaledBitmap(newBitmap, (int) newW, (int) newH, true);
            Bitmap rotatedBitmap = Bitmap.createBitmap(scaledBitmap, 0, 0, scaledBitmap.getWidth(), scaledBitmap.getHeight(), matrix, true);
            return rotatedBitmap;
        } else{
            b = Bitmap.createScaledBitmap(b, (int) newW, (int) newH, true);
            return b;
        }
    }

    /****************************************************************************
     * resizeLayout
     * resizes the layout based on the dimensions of the current bitmap
     ***************************************************************************/
    public void resizeLayout(Bitmap bmp){
        ViewGroup.LayoutParams lp = customCanvas.getLayoutParams();
        lp.width = bmp.getWidth();
        lp.height = bmp.getHeight();
        customCanvas.requestLayout();
    }

    static public Bitmap getPic(String currentPhotoPath) {
        /** Get the dimensions of the View **/
        Log.w("choices MSG","canvas null? " + customCanvas);
        int targetW = customCanvas.getWidth();
        int targetH = customCanvas.getHeight();
        Log.w("choices MSG","targetW " + targetW);
        Log.w("choices MSG","targetH " + targetH);
        /** Get the dimensions of the bitmap **/
        BitmapFactory.Options bmOptions = new BitmapFactory.Options();
        bmOptions.inJustDecodeBounds = true;

        targetH = 540;
        targetW = 400;
        BitmapFactory.decodeFile(currentPhotoPath, bmOptions);

        int photoW = bmOptions.outWidth;
        int photoH = bmOptions.outHeight;

        /** Determine how much to scale down the image **/
        int scaleFactor = Math.max(1, Math.min(photoW/targetW, photoH/targetH));

        /** Decode the image file into a Bitmap sized to fill the View **/
        bmOptions.inJustDecodeBounds = false;
        bmOptions.inSampleSize = scaleFactor;

        Bitmap bitmap = BitmapFactory.decodeFile(currentPhotoPath, bmOptions);
        return bitmap;
    }

    /****************************************************************************
     * onTouchEvent
     * handles calling the scale gesture detector for zooming
     ***************************************************************************/
    public boolean onTouchEvent(MotionEvent motionEvent) {
        mScaleGestureDetector.onTouchEvent(motionEvent);
        return true;
    }

    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
        @Override
        public boolean onScale(ScaleGestureDetector scaleGestureDetector){
            if (true){
                mScaleFactor *= scaleGestureDetector.getScaleFactor();
                mScaleFactor = Math.max(0.1f,
                        Math.min(mScaleFactor, 10.0f));
                customCanvas.setScaleX(mScaleFactor);
                customCanvas.setScaleY(mScaleFactor);
            } return true;
        }
    }
}