/*  Copyright (C) 2020-2024 Daniel Dakhno

    This file is part of Gadgetbridge.

    Gadgetbridge is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published
    by the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Gadgetbridge is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.qhybrid;

import android.content.ContentResolver;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.Toast;

import java.io.IOException;

import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBActivity;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.image.AssetImage;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.image.AssetImageFactory;
import nodomain.freeyourgadget.gadgetbridge.util.GB;

public class ImageEditActivity extends AbstractGBActivity implements View.OnTouchListener {
    static final public int RESULT_CODE_EDIT_SUCCESS = 0;

    ImageView overlay;
    Canvas overlayCanvas;
    Paint overlayPaint;
    Bitmap overlayBitmap, mainBitmap;

    float x = 0, y = 0, diameter = 0, imageDimension = 0;
    int imageWidth, imageHeight;

    private enum MovementState {
        MOVE_UPPER_LEFT,
        MOVE_LOWER_RIGHT,
        MOVE_FRAME
    }
    private MovementState movementState;
    float movementStartX, movementStartY, movementStartFrameX, movementStartFrameY, movementStartDiameter, leftUpperDeltaX, leftUpperDeltaY;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_qhybrid_image_edit);

        final RelativeLayout mainLayout = findViewById(R.id.qhybrid_image_edit_container);
        overlay = findViewById(R.id.qhybrid_image_edit_image_overlay);
        overlay.setOnTouchListener(this);

        findViewById(R.id.qhybrid_image_edit_okay).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                finalizeImage();
            }
        });

        mainLayout.post(new Runnable() {
            @Override
            public void run() {
                try {
                    fitImageToLayout(mainLayout);
                } catch (IOException | RuntimeException e) {
                    GB.log("Error formatting image", GB.ERROR, e);
                    GB.toast("Error formatting image", Toast.LENGTH_LONG, GB.ERROR);
                    finish();
                }
            }
        });
    }

    private void finalizeImage(){
        Bitmap cropped = Bitmap.createBitmap(this.mainBitmap, (int) this.x, (int) this.y, (int) this.diameter, (int) this.diameter);
        Bitmap scaled = Bitmap.createScaledBitmap(cropped, 400, 400, false);
        cropped.recycle();

        try {
            AssetImage image = AssetImageFactory.createAssetImage(scaled, false, 0, 0, 0);

            Intent resultIntent = new Intent();
            resultIntent.putExtra("EXTRA_PIXELS_ENCODED", image.getFileData());
            setResult(RESULT_CODE_EDIT_SUCCESS, resultIntent);
            finish();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {

            scaled.recycle();
        }
    }

    private void fitImageToLayout(RelativeLayout mainLayout) throws IOException, RuntimeException {
        float containerHeight = mainLayout.getHeight();
        float containerWidth = mainLayout.getWidth();
        float containerRelation = containerHeight / containerWidth;

        Bitmap bitmap = this.createImageFromURI();
        float imageHeight = bitmap.getHeight();
        float imageWidth = bitmap.getWidth();
        float imageRelation = imageHeight / imageWidth;

        float scaleRatio;
        if(imageRelation > containerRelation){
            scaleRatio = containerHeight / imageHeight;
        }else{
            scaleRatio = containerWidth / imageWidth;
        }

        int scaledHeight = (int)(imageHeight * scaleRatio);
        int scaledWidth = (int)(imageWidth * scaleRatio);

        this.imageHeight = scaledHeight;
        this.imageWidth = scaledWidth;

        this.imageDimension = this.diameter = Math.min(scaledHeight, scaledWidth);

        mainBitmap = Bitmap.createScaledBitmap(bitmap, scaledWidth, scaledHeight, false);

        ImageView mainImageView = findViewById(R.id.qhybrid_image_edit_image);
        mainImageView.setImageBitmap(mainBitmap);
        createOverlay(scaledWidth, scaledHeight);
    }

    private void createOverlay(int width, int height){
        this.overlayBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_4444);
        this.overlayCanvas = new Canvas(this.overlayBitmap);

        this.overlayPaint = new Paint();
        this.overlayPaint.setColor(Color.BLACK);
        this.overlayPaint.setStyle(Paint.Style.STROKE);
        this.overlayPaint.setStrokeWidth(imageDimension / 100);

        renderOverlay();
    }

    private Bitmap createImageFromURI() throws IOException, RuntimeException {
        Uri imageURI = getIntent().getData();
        if (imageURI == null) {
            throw new RuntimeException("no image attached");
        }

        ContentResolver resolver = getContentResolver();
        Cursor c = resolver.query(imageURI, new String[]{MediaStore.Images.ImageColumns.ORIENTATION}, null, null, null);
        c.moveToFirst();
        int orientation = c.getInt(c.getColumnIndex(MediaStore.Images.ImageColumns.ORIENTATION));
        c.close();
        Bitmap bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), imageURI);
        if (orientation != 0) {
            Matrix matrix = new Matrix();
            matrix.postRotate(90);

            bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
        }

        return bitmap;
    }

    private void renderOverlay() {
        overlayCanvas.drawColor(0, PorterDuff.Mode.CLEAR);

        overlayCanvas.drawCircle(x, y, imageDimension / 15, overlayPaint);
        overlayCanvas.drawCircle(x + diameter, y + diameter, imageDimension / 15, overlayPaint);

        overlayCanvas.drawCircle(x + diameter / 2, y + diameter / 2, diameter / 2, overlayPaint);

        overlay.setImageBitmap(overlayBitmap);
    }

    private void forceImageBoundaries(){
        this.x = Math.max(this.x, 0);
        this.y = Math.max(this.y, 0);

        this.x = Math.min(this.x, this.imageWidth - diameter);
        this.y = Math.min(this.y, this.imageHeight - diameter);
    }

    @Override
    public boolean onTouch(View view, MotionEvent motionEvent) {
        if(motionEvent.getAction() == MotionEvent.ACTION_DOWN){
            handleTouchDown(motionEvent);
        }else if(motionEvent.getAction() == MotionEvent.ACTION_MOVE){
            handleTouchMove(motionEvent);
        }
        return true;
    }

    private void handleTouchMove(MotionEvent motionEvent){
        if(movementState == MovementState.MOVE_UPPER_LEFT){
            float moveDeltaX = motionEvent.getX() - this.movementStartX;
            float moveDeltaY = motionEvent.getY() - this.movementStartY;

            float mid = (moveDeltaX + moveDeltaY) / 2;

            this.diameter = this.movementStartDiameter - mid;
            this.x = this.movementStartX + mid + this.leftUpperDeltaX;
            this.y = this.movementStartY + mid + this.leftUpperDeltaY;
        }else if(movementState == MovementState.MOVE_LOWER_RIGHT) {
            float moveDeltaX = motionEvent.getX() - this.movementStartX;
            float moveDeltaY = motionEvent.getY() - this.movementStartY;

            float mid = (moveDeltaX + moveDeltaY) / 2;

            this.diameter = this.movementStartDiameter + mid;
        }else if(movementState == MovementState.MOVE_FRAME){
            this.x = this.movementStartFrameX + (motionEvent.getX() - this.movementStartX);
            this.y = this.movementStartFrameY + (motionEvent.getY() - this.movementStartY);
        }

        this.forceImageBoundaries();
        renderOverlay();
    }

    private void handleTouchDown(MotionEvent motionEvent) {
        this.movementStartX = motionEvent.getX();
        this.movementStartY = motionEvent.getY();
        this.movementStartFrameX = this.x;
        this.movementStartFrameY = this.y;
        this.movementStartDiameter = this.diameter;

        final float threshold = imageDimension / 15;

        float upperLeftDeltaX = this.x - motionEvent.getX();
        float upperLeftDeltaY = this.y - motionEvent.getY();

        float upperLeftDistance = (float) Math.sqrt(upperLeftDeltaX * upperLeftDeltaX + upperLeftDeltaY * upperLeftDeltaY);

        if(upperLeftDistance < threshold){
            // Toast.makeText(this, "upper left", 0).show();
            this.leftUpperDeltaX = upperLeftDeltaX;
            this.leftUpperDeltaY = upperLeftDeltaY;
            this.movementState = MovementState.MOVE_UPPER_LEFT;
            return;
        }

        float lowerLeftX = this.x + diameter;
        float lowerLeftY = this.y + diameter;

        float lowerRightDeltaX = lowerLeftX - motionEvent.getX();
        float lowerRightDeltaY = lowerLeftY - motionEvent.getY();

        float lowerRightDistance = (float) Math.sqrt(lowerRightDeltaX * lowerRightDeltaX + lowerRightDeltaY * lowerRightDeltaY);

        if(lowerRightDistance < threshold){
            // Toast.makeText(this, "lower right", 0).show();
            this.movementState = MovementState.MOVE_LOWER_RIGHT;
            return;
        }

        // Toast.makeText(this, "anywhere else", 0).show();
        this.movementState = MovementState.MOVE_FRAME;
    }
}
