package com.ardrawing.ardrawingspace.activity;

import android.Manifest;
import android.content.ContentValues;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.Toast;

import androidx.activity.EdgeToEdge;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.camera.core.Camera;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.ImageCapture;
import androidx.camera.core.ImageCaptureException;
import androidx.camera.core.Preview;
import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;

import com.ardrawing.ardrawingspace.R;
import com.ardrawing.ardrawingspace.databinding.ActivityDrawingBinding;
import com.ardrawing.ardrawingspace.util.DocumentManipulationTool;
import com.ardrawing.ardrawingspace.util.PermissionTool;
import com.google.common.util.concurrent.ListenableFuture;

import org.jetbrains.annotations.Nullable;

public class DrawingActivity extends AppCompatActivity implements SeekBar.OnSeekBarChangeListener, View.OnTouchListener {
    private ActivityDrawingBinding binding;

    private static final int CAMERA_PERMISSION_REQUEST = 200;
    private static final int STORAGE_PERMISSION_REQUEST = 201;
    private static final int PICK_IMAGE_REQUEST = 202;

    private static final int MODE_NONE = 0;
    private static final int MODE_DRAG = 1;
    private static final int MODE_ZOOM = 2;

    private final Matrix matrix = new Matrix();
    private final Matrix savedMatrix = new Matrix();
    private final PointF startPoint = new PointF();
    private float initialDistance;
    private int mode = MODE_NONE;

    private boolean isFlash = false;
    private Camera camera;
    private ImageCapture imageCapture;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityDrawingBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());
        EdgeToEdge.enable(this);
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
            Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
            return insets;
        });

        setupEventListeners();
    }

    private void setupEventListeners() {
        String imagePath = getIntent().getStringExtra("imagePath");
        if (imagePath == null) {
            finish();
            return;
        }

        DocumentManipulationTool.loadImageByPath(imagePath, binding.imageView, this);

        binding.imageView.setOnTouchListener(this);
        binding.seekbar.setOnSeekBarChangeListener(this);
        binding.photo.setOnClickListener(v -> handleImagePicker());
        binding.flash.setOnClickListener(v -> toggleFlash());
        binding.back.setOnClickListener(v -> finish());
        binding.camera.setOnClickListener(v -> captureAndSaveImage());

        checkAndRequestPermissions();
    }

    private void checkAndRequestPermissions() {
        String[] permissions = getPermissions();
        if (!PermissionTool.hasPermission(this, permissions)) {
            PermissionTool.reqPermission(this, permissions, CAMERA_PERMISSION_REQUEST);
        } else {
            setupCamera();
        }
    }

    private void setupCamera() {
        ListenableFuture<ProcessCameraProvider> future = ProcessCameraProvider.getInstance(this);
        future.addListener(() -> {
            try {
                ProcessCameraProvider provider = future.get();
                Preview preview = new Preview.Builder().build();
                preview.setSurfaceProvider(binding.preview.getSurfaceProvider());

                imageCapture = new ImageCapture.Builder().build();
                CameraSelector selector = new CameraSelector.Builder()
                        .requireLensFacing(CameraSelector.LENS_FACING_BACK)
                        .build();

                provider.unbindAll();
                camera = provider.bindToLifecycle(this, selector, preview, imageCapture);
            } catch (Exception ignored) {
            }
        }, ContextCompat.getMainExecutor(this));
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        handlePermissionResult(requestCode, grantResults);
    }

    private void handlePermissionResult(int requestCode, int[] grantResults) {
        if (requestCode == CAMERA_PERMISSION_REQUEST && PermissionTool.gotPermission(grantResults)) {
            setupCamera();
        } else if (requestCode == STORAGE_PERMISSION_REQUEST && PermissionTool.gotPermission(grantResults)) {
            handleImagePicker();
        } else {
            showToast("Permission denied. Please enable it in Settings.");
        }
    }

    private void captureAndSaveImage() {
        if (imageCapture == null) {
            showToast("Camera not Init");
            return;
        }

        if (!hasCameraPermission()) {
            requestCameraPermission();
            return;
        }

        ImageCapture.OutputFileOptions options = new ImageCapture.OutputFileOptions.Builder(
                getContentResolver(),
                MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                prepareContentValues()
        ).build();

        imageCapture.takePicture(options, ContextCompat.getMainExecutor(this), new ImageCapture.OnImageSavedCallback() {
            @Override
            public void onImageSaved(@NonNull ImageCapture.OutputFileResults output) {
                showToast("Successful photo shoot");
            }

            @Override
            public void onError(@NonNull ImageCaptureException exception) {
                showToast("Photo shoot failed");
            }
        });
    }

    private void handleImagePicker() {
        String[] permissions = PermissionTool.getPermission();
        if (ContextCompat.checkSelfPermission(this, permissions[0]) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, permissions, STORAGE_PERMISSION_REQUEST);
        } else {
            Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
            startActivityForResult(intent, PICK_IMAGE_REQUEST);
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == PICK_IMAGE_REQUEST && resultCode == RESULT_OK && data != null) {
            Uri imageUri = data.getData();
            if (imageUri != null) {
                binding.imageView.setImageURI(imageUri);
            }
        }
    }

    @Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
        binding.imageView.setAlpha((100 - progress) / 100f);
    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {
    }

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        ImageView view = (ImageView) v;
        switch (event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                savedMatrix.set(matrix);
                startPoint.set(event.getX(), event.getY());
                mode = MODE_DRAG;
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                initialDistance = DocumentManipulationTool.computeTouchDistance(event);
                if (initialDistance > 10f) {
                    savedMatrix.set(matrix);
                    mode = MODE_ZOOM;
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if (mode == MODE_DRAG) {
                    matrix.set(savedMatrix);
                    matrix.postTranslate(event.getX() - startPoint.x, event.getY() - startPoint.y);
                } else if (mode == MODE_ZOOM && event.getPointerCount() >= 2) {
                    float newDist = DocumentManipulationTool.computeTouchDistance(event);
                    if (newDist > 10f) {
                        float scale = newDist / initialDistance;
                        matrix.set(savedMatrix);
                        matrix.postScale(scale, scale, view.getWidth() / 2f, view.getHeight() / 2f);
                    }
                }
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_POINTER_UP:
                mode = MODE_NONE;
                break;
        }
        view.setImageMatrix(matrix);
        return true;
    }

    private void toggleFlash() {
        if (camera != null) {
            isFlash = !isFlash;
            camera.getCameraControl().enableTorch(isFlash);
            binding.flash.setImageResource(isFlash ? R.drawable.flash : R.drawable.un_flash);
        }
    }

    private boolean hasCameraPermission() {
        return ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED;
    }

    private void requestCameraPermission() {
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, CAMERA_PERMISSION_REQUEST);
    }

    private ContentValues prepareContentValues() {
        ContentValues values = new ContentValues();
        values.put(MediaStore.MediaColumns.DISPLAY_NAME, "photo_" + System.currentTimeMillis() + ".jpg");
        values.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg");
        values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES);
        return values;
    }

    private String[] getPermissions() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU ?
                new String[]{Manifest.permission.CAMERA, Manifest.permission.READ_MEDIA_IMAGES} :
                new String[]{Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE};
    }

    private void showToast(String message) {
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        binding = null;
    }
}