package com.pdf417scanner;

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.ImageFormat;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
import android.media.ImageReader;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Size;
import android.view.Surface;
import android.view.TextureView;

import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.DecodeHintType;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.PlanarYUVLuminanceSource;
import com.google.zxing.Result;
import com.google.zxing.common.HybridBinarizer;

import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.Map;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class PDF417CameraView extends TextureView implements TextureView.SurfaceTextureListener {
    private static final String TAG = "PDF417CameraView";
    
    private CameraManager cameraManager;
    private CameraDevice cameraDevice;
    private CameraCaptureSession captureSession;
    private ImageReader imageReader;
    private HandlerThread backgroundThread;
    private Handler backgroundHandler;
    private Semaphore cameraOpenCloseLock = new Semaphore(1);
    private String cameraId;
    private Size previewSize;
    private boolean torchEnabled = false;
    private boolean isScanning = false;
    private MultiFormatReader multiFormatReader;
    private CameraReadyListener cameraReadyListener;
    private ScanResultListener scanResultListener;
    private long lastProcessTime = 0;
    private static final long PROCESS_INTERVAL_MS = 50; // Process every 50ms for better responsiveness

    public interface CameraReadyListener {
        void onCameraReady();
        void onCameraError(String error);
    }

    public interface ScanResultListener {
        void onScanResult(String data, String format);
        void onScanError(String code, String message);
    }

    public PDF417CameraView(Context context) {
        super(context);
        init();
    }

    public PDF417CameraView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public PDF417CameraView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        cameraManager = (CameraManager) getContext().getSystemService(Context.CAMERA_SERVICE);
        setSurfaceTextureListener(this);
        setupBarcodeReader();
    }

    private void setupBarcodeReader() {
        multiFormatReader = new MultiFormatReader();
        Map<DecodeHintType, Object> hints = new EnumMap<>(DecodeHintType.class);
        
        // Enable all barcode formats for maximum compatibility
        hints.put(DecodeHintType.POSSIBLE_FORMATS, EnumSet.of(
            BarcodeFormat.PDF_417,
            BarcodeFormat.DATA_MATRIX,
            BarcodeFormat.QR_CODE,
            BarcodeFormat.AZTEC,
            BarcodeFormat.CODE_128,
            BarcodeFormat.CODE_39,
            BarcodeFormat.CODABAR
        ));
        
        // Enhanced scanning hints for lengthy and dense barcodes
        hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
        hints.put(DecodeHintType.PURE_BARCODE, Boolean.FALSE);
        hints.put(DecodeHintType.ALSO_INVERTED, Boolean.TRUE);
        
        // Character set for international cards
        hints.put(DecodeHintType.CHARACTER_SET, "UTF-8");
        
        // Allow partial decoding for lengthy barcodes
        hints.put(DecodeHintType.ASSUME_GS1, Boolean.FALSE);
        
        multiFormatReader.setHints(hints);
    }

    public void setCameraReadyListener(CameraReadyListener listener) {
        this.cameraReadyListener = listener;
    }

    public void setScanResultListener(ScanResultListener listener) {
        this.scanResultListener = listener;
    }

    public void startCamera() {
        Log.d(TAG, "Starting camera...");
        startBackgroundThread();
        
        if (backgroundHandler != null) {
            backgroundHandler.post(() -> {
                if (isAvailable()) {
                    openCamera(getWidth(), getHeight());
                } else {
                    Log.d(TAG, "Surface texture not available yet, waiting...");
                }
            });
        }
    }

    public void stopCamera() {
        isScanning = false;
        closeCamera();
        stopBackgroundThread();
    }

    public void startScanning() {
        isScanning = true;
        Log.d(TAG, "Started scanning for PDF417 barcodes");
    }

    public void stopScanning() {
        isScanning = false;
        Log.d(TAG, "Stopped scanning");
    }

    public void triggerAutoFocus() {
        if (captureSession != null && cameraDevice != null) {
            try {
                // Enhanced autofocus for barcode scanning
                CaptureRequest.Builder builder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
                builder.addTarget(new Surface(getSurfaceTexture()));
                builder.addTarget(imageReader.getSurface());
                
                // Trigger AF with enhanced settings
                builder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START);
                builder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO);
                
                // Enhanced focus settings for barcode scanning
                builder.set(CaptureRequest.LENS_FOCUS_DISTANCE, 0.0f); // Start with infinity
                builder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
                
                // Apply enhanced image quality settings during focus
                builder.set(CaptureRequest.EDGE_MODE, CaptureRequest.EDGE_MODE_HIGH_QUALITY);
                builder.set(CaptureRequest.NOISE_REDUCTION_MODE, CaptureRequest.NOISE_REDUCTION_MODE_HIGH_QUALITY);
                builder.set(CaptureRequest.SHADING_MODE, CaptureRequest.SHADING_MODE_HIGH_QUALITY);
                
                captureSession.capture(builder.build(), null, backgroundHandler);
                
                // Reset AF trigger after a short delay
                backgroundHandler.postDelayed(() -> {
                    try {
                        if (captureSession != null && cameraDevice != null) {
                            CaptureRequest.Builder resetBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
                            resetBuilder.addTarget(new Surface(getSurfaceTexture()));
                            resetBuilder.addTarget(imageReader.getSurface());
                            
                            resetBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_IDLE);
                            resetBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
                            resetBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_IDLE);
                            
                            // Maintain enhanced quality settings
                            resetBuilder.set(CaptureRequest.EDGE_MODE, CaptureRequest.EDGE_MODE_HIGH_QUALITY);
                            resetBuilder.set(CaptureRequest.NOISE_REDUCTION_MODE, CaptureRequest.NOISE_REDUCTION_MODE_HIGH_QUALITY);
                            resetBuilder.set(CaptureRequest.SHADING_MODE, CaptureRequest.SHADING_MODE_HIGH_QUALITY);
                            resetBuilder.set(CaptureRequest.FLASH_MODE, torchEnabled ? 
                                CaptureRequest.FLASH_MODE_TORCH : CaptureRequest.FLASH_MODE_OFF);
                            
                            captureSession.setRepeatingRequest(resetBuilder.build(), null, backgroundHandler);
                        }
                    } catch (CameraAccessException e) {
                        Log.e(TAG, "Error resetting autofocus", e);
                    }
                }, 500); // 500ms delay for focus to complete
                
            } catch (CameraAccessException e) {
                Log.e(TAG, "Error triggering autofocus", e);
            }
        }
    }

    public void setTorchEnabled(boolean enabled) {
        this.torchEnabled = enabled;
        updateTorch();
    }

    private void updateTorch() {
        if (captureSession != null && cameraDevice != null) {
            try {
                CaptureRequest.Builder builder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
                builder.addTarget(new Surface(getSurfaceTexture()));
                builder.set(CaptureRequest.FLASH_MODE, torchEnabled ? 
                    CaptureRequest.FLASH_MODE_TORCH : CaptureRequest.FLASH_MODE_OFF);
                captureSession.setRepeatingRequest(builder.build(), null, backgroundHandler);
            } catch (CameraAccessException e) {
                Log.e(TAG, "Error updating torch", e);
            }
        }
    }

    private void startBackgroundThread() {
        backgroundThread = new HandlerThread("CameraBackground");
        backgroundThread.start();
        backgroundHandler = new Handler(backgroundThread.getLooper());
    }

    private void stopBackgroundThread() {
        if (backgroundThread != null) {
            backgroundThread.quitSafely();
            try {
                backgroundThread.join();
                backgroundThread = null;
                backgroundHandler = null;
            } catch (InterruptedException e) {
                Log.e(TAG, "Error stopping background thread", e);
            }
        }
    }

    private void openCamera(int width, int height) {
        try {
            // Check camera permission first
            if (ActivityCompat.checkSelfPermission(getContext(), Manifest.permission.CAMERA) 
                != PackageManager.PERMISSION_GRANTED) {
                Log.e(TAG, "Camera permission not granted");
                if (cameraReadyListener != null) {
                    cameraReadyListener.onCameraError("Camera permission not granted");
                }
                return;
            }

            if (!cameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
                throw new RuntimeException("Time out waiting to lock camera opening.");
            }

            cameraId = selectCamera();
            if (cameraId == null) {
                Log.e(TAG, "No suitable camera found");
                if (cameraReadyListener != null) {
                    cameraReadyListener.onCameraError("No suitable camera found");
                }
                cameraOpenCloseLock.release();
                return;
            }

            Log.d(TAG, "Opening camera: " + cameraId);
            cameraManager.openCamera(cameraId, stateCallback, backgroundHandler);
        } catch (CameraAccessException e) {
            Log.e(TAG, "Camera access exception", e);
            cameraOpenCloseLock.release();
            if (cameraReadyListener != null) {
                cameraReadyListener.onCameraError("Camera access denied: " + e.getMessage());
            }
        } catch (SecurityException e) {
            Log.e(TAG, "Security exception opening camera", e);
            cameraOpenCloseLock.release();
            if (cameraReadyListener != null) {
                cameraReadyListener.onCameraError("Camera security error: " + e.getMessage());
            }
        } catch (Exception e) {
            Log.e(TAG, "Unexpected error opening camera", e);
            cameraOpenCloseLock.release();
            if (cameraReadyListener != null) {
                cameraReadyListener.onCameraError("Failed to open camera: " + e.getMessage());
            }
        }
    }

    private String selectCamera() throws CameraAccessException {
        for (String cameraId : cameraManager.getCameraIdList()) {
            CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(cameraId);
            Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
            if (facing != null && facing == CameraCharacteristics.LENS_FACING_BACK) {
                StreamConfigurationMap map = characteristics.get(
                    CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
                if (map != null) {
                    previewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class));
                    return cameraId;
                }
            }
        }
        return null;
    }

    private Size chooseOptimalSize(Size[] choices) {
        for (Size size : choices) {
            if (size.getWidth() <= 1920 && size.getHeight() <= 1080) {
                return size;
            }
        }
        return choices[0];
    }

    private final CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() {
        @Override
        public void onOpened(@NonNull CameraDevice camera) {
            cameraOpenCloseLock.release();
            cameraDevice = camera;
            createCameraPreviewSession();
        }

        @Override
        public void onDisconnected(@NonNull CameraDevice camera) {
            cameraOpenCloseLock.release();
            camera.close();
            cameraDevice = null;
        }

        @Override
        public void onError(@NonNull CameraDevice camera, int error) {
            cameraOpenCloseLock.release();
            camera.close();
            cameraDevice = null;
            if (cameraReadyListener != null) {
                cameraReadyListener.onCameraError("Camera error: " + error);
            }
        }
    };

    private void createCameraPreviewSession() {
        try {
            SurfaceTexture texture = getSurfaceTexture();
            if (texture == null) {
                Log.e(TAG, "Surface texture not available, retrying...");
                backgroundHandler.postDelayed(() -> {
                    if (cameraDevice != null) {
                        createCameraPreviewSession();
                    }
                }, 100);
                return;
            }
            
            if (previewSize == null) {
                Log.e(TAG, "Preview size not set");
                if (cameraReadyListener != null) {
                    cameraReadyListener.onCameraError("Preview size not configured");
                }
                return;
            }
            
            texture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
            Surface previewSurface = new Surface(texture);

            // Setup ImageReader for barcode scanning with optimal resolution
            setupImageReader();

            CaptureRequest.Builder builder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
            builder.addTarget(previewSurface);
            builder.addTarget(imageReader.getSurface());
            
            // Enhanced camera settings for barcode scanning
            builder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
            builder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON);
            builder.set(CaptureRequest.CONTROL_AWB_MODE, CaptureRequest.CONTROL_AWB_MODE_AUTO);
            
            // Optimize for document/barcode scanning
            builder.set(CaptureRequest.CONTROL_SCENE_MODE, CaptureRequest.CONTROL_SCENE_MODE_BARCODE);
            builder.set(CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE, CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE_ON);
            
            // Enhanced image quality for better barcode detection
            builder.set(CaptureRequest.EDGE_MODE, CaptureRequest.EDGE_MODE_HIGH_QUALITY);
            builder.set(CaptureRequest.NOISE_REDUCTION_MODE, CaptureRequest.NOISE_REDUCTION_MODE_HIGH_QUALITY);
            builder.set(CaptureRequest.SHADING_MODE, CaptureRequest.SHADING_MODE_HIGH_QUALITY);
            builder.set(CaptureRequest.COLOR_CORRECTION_MODE, CaptureRequest.COLOR_CORRECTION_MODE_HIGH_QUALITY);
            
            // Focus settings for sharp barcode capture
            builder.set(CaptureRequest.LENS_FOCUS_DISTANCE, 0.0f); // Infinity focus for documents
            builder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_IDLE);
            
            // Exposure and ISO settings for consistent lighting
            builder.set(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION, 0);
            builder.set(CaptureRequest.CONTROL_AE_LOCK, false);
            
            // Set flash mode
            builder.set(CaptureRequest.FLASH_MODE, torchEnabled ? 
                CaptureRequest.FLASH_MODE_TORCH : CaptureRequest.FLASH_MODE_OFF);

            cameraDevice.createCaptureSession(Arrays.asList(previewSurface, imageReader.getSurface()),
                new CameraCaptureSession.StateCallback() {
                    @Override
                    public void onConfigured(@NonNull CameraCaptureSession session) {
                        if (cameraDevice == null) {
                            Log.w(TAG, "Camera device null during session configuration");
                            return;
                        }
                        
                        captureSession = session;
                        try {
                            CaptureRequest request = builder.build();
                            session.setRepeatingRequest(request, null, backgroundHandler);
                            Log.d(TAG, "Camera preview started successfully with enhanced settings");
                            if (cameraReadyListener != null) {
                                cameraReadyListener.onCameraReady();
                            }
                        } catch (CameraAccessException e) {
                            Log.e(TAG, "Error starting camera preview", e);
                            if (cameraReadyListener != null) {
                                cameraReadyListener.onCameraError("Failed to start preview: " + e.getMessage());
                            }
                        }
                    }

                    @Override
                    public void onConfigureFailed(@NonNull CameraCaptureSession session) {
                        Log.e(TAG, "Camera session configuration failed");
                        if (cameraReadyListener != null) {
                            cameraReadyListener.onCameraError("Failed to configure camera session");
                        }
                    }
                }, backgroundHandler);
        } catch (CameraAccessException e) {
            Log.e(TAG, "Error creating camera preview session", e);
            if (cameraReadyListener != null) {
                cameraReadyListener.onCameraError("Failed to create preview session: " + e.getMessage());
            }
        }
    }

    private void setupImageReader() {
        // Use optimal resolution for barcode scanning
        Size scanSize = chooseBestScanSize();
        imageReader = ImageReader.newInstance(scanSize.getWidth(), scanSize.getHeight(),
            ImageFormat.YUV_420_888, 5); // Increased buffer for better processing
        imageReader.setOnImageAvailableListener(onImageAvailableListener, backgroundHandler);
    }
    
    private Size chooseBestScanSize() {
        // For lengthy barcodes, we need higher resolution
        if (previewSize.getWidth() > 2560 || previewSize.getHeight() > 1440) {
            return new Size(2560, 1440); // 4K support for very detailed barcodes
        } else if (previewSize.getWidth() > 1920 || previewSize.getHeight() > 1080) {
            return new Size(1920, 1080); // Full HD for detailed barcodes
        } else if (previewSize.getWidth() < 1280 || previewSize.getHeight() < 720) {
            return new Size(1280, 720); // Minimum HD for basic scanning
        }
        return previewSize;
    }

    private final ImageReader.OnImageAvailableListener onImageAvailableListener = 
        new ImageReader.OnImageAvailableListener() {
            @Override
            public void onImageAvailable(ImageReader reader) {
                if (!isScanning) return;
                
                // Throttle processing to avoid overwhelming the CPU
                long currentTime = System.currentTimeMillis();
                if (currentTime - lastProcessTime < PROCESS_INTERVAL_MS) {
                    // Skip this frame
                    Image image = reader.acquireLatestImage();
                    if (image != null) {
                        image.close();
                    }
                    return;
                }
                lastProcessTime = currentTime;
                
                Image image = reader.acquireLatestImage();
                if (image != null) {
                    try {
                        processImage(image);
                    } finally {
                        image.close();
                    }
                }
            }
        };

    private void processImage(Image image) {
        try {
            // Get image dimensions
            int width = image.getWidth();
            int height = image.getHeight();
            
            // Extract Y plane (luminance) from YUV_420_888
            Image.Plane yPlane = image.getPlanes()[0];
            ByteBuffer yBuffer = yPlane.getBuffer();
            int ySize = yBuffer.remaining();
            byte[] yData = new byte[ySize];
            yBuffer.get(yData);
            
            // Enhanced processing for lengthy and dense barcodes
            Result result = null;
            
            // Approach 1: Full image with enhanced preprocessing
            result = tryDecodeWithEnhancedProcessing(yData, width, height, 0, 0, width, height);
            
            // Approach 2: Horizontal strips for lengthy barcodes (like Canadian health cards)
            if (result == null) {
                result = tryHorizontalStrips(yData, width, height);
            }
            
            // Approach 3: Vertical strips for rotated lengthy barcodes
            if (result == null) {
                result = tryVerticalStrips(yData, width, height);
            }
            
            // Approach 4: Center crop with different sizes
            if (result == null) {
                result = tryMultipleCrops(yData, width, height);
            }
            
            // Approach 5: Enhanced rotation detection
            if (result == null) {
                result = tryEnhancedRotation(yData, width, height);
            }
            
            // Approach 6: Scale variations for different densities
            if (result == null) {
                result = tryScaleVariations(yData, width, height);
            }
            
            if (result != null && scanResultListener != null) {
                Log.d(TAG, "Barcode detected: " + result.getBarcodeFormat() + " - Length: " + result.getText().length());
                scanResultListener.onScanResult(result.getText(), result.getBarcodeFormat().toString());
            }
        } catch (Exception e) {
            Log.d(TAG, "No barcode found in frame: " + e.getMessage());
        }
    }
    
    private Result tryDecodeWithEnhancedProcessing(byte[] yData, int dataWidth, int dataHeight, 
                                                 int left, int top, int width, int height) {
        try {
            // Create luminance source
            PlanarYUVLuminanceSource source = new PlanarYUVLuminanceSource(
                yData, dataWidth, dataHeight, left, top, width, height, false);
            
            // Try multiple binarization approaches
            Result result = null;
            
            // 1. HybridBinarizer (best for mixed content)
            BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
            try {
                result = multiFormatReader.decode(bitmap);
                if (result != null) return result;
            } catch (Exception e) { /* Continue to next approach */ }
            
            // 2. GlobalHistogramBinarizer (best for uniform lighting)
            bitmap = new BinaryBitmap(new com.google.zxing.common.GlobalHistogramBinarizer(source));
            try {
                result = multiFormatReader.decode(bitmap);
                if (result != null) return result;
            } catch (Exception e) { /* Continue to next approach */ }
            
            // 3. Try with inverted source for negative barcodes
            PlanarYUVLuminanceSource invertedSource = new PlanarYUVLuminanceSource(
                invertYUVData(yData, width * height), dataWidth, dataHeight, left, top, width, height, false);
            
            bitmap = new BinaryBitmap(new HybridBinarizer(invertedSource));
            try {
                result = multiFormatReader.decode(bitmap);
                if (result != null) return result;
            } catch (Exception e) { /* Continue */ }
            
            return null;
        } catch (Exception e) {
            return null;
        }
    }
    
    private Result tryHorizontalStrips(byte[] yData, int width, int height) {
        // Scan in horizontal strips for lengthy horizontal barcodes
        int stripHeight = height / 5; // 5 horizontal strips
        
        for (int i = 0; i < 5; i++) {
            int stripTop = i * stripHeight;
            if (stripTop + stripHeight > height) stripHeight = height - stripTop;
            
            Result result = tryDecodeWithEnhancedProcessing(yData, width, height, 
                0, stripTop, width, stripHeight);
            if (result != null) return result;
        }
        
        // Try overlapping strips
        stripHeight = height / 3;
        for (int i = 0; i < 4; i++) {
            int stripTop = i * stripHeight / 2;
            if (stripTop + stripHeight > height) stripHeight = height - stripTop;
            
            Result result = tryDecodeWithEnhancedProcessing(yData, width, height, 
                0, stripTop, width, stripHeight);
            if (result != null) return result;
        }
        
        return null;
    }
    
    private Result tryVerticalStrips(byte[] yData, int width, int height) {
        // Scan in vertical strips for lengthy vertical barcodes
        int stripWidth = width / 5; // 5 vertical strips
        
        for (int i = 0; i < 5; i++) {
            int stripLeft = i * stripWidth;
            if (stripLeft + stripWidth > width) stripWidth = width - stripLeft;
            
            Result result = tryDecodeWithEnhancedProcessing(yData, width, height, 
                stripLeft, 0, stripWidth, height);
            if (result != null) return result;
        }
        
        return null;
    }
    
    private Result tryMultipleCrops(byte[] yData, int width, int height) {
        // Try different crop sizes for various barcode densities
        float[] cropFactors = {0.9f, 0.8f, 0.7f, 0.6f, 0.5f};
        
        for (float factor : cropFactors) {
            int cropWidth = (int)(width * factor);
            int cropHeight = (int)(height * factor);
            int cropLeft = (width - cropWidth) / 2;
            int cropTop = (height - cropHeight) / 2;
            
            Result result = tryDecodeWithEnhancedProcessing(yData, width, height, 
                cropLeft, cropTop, cropWidth, cropHeight);
            if (result != null) return result;
        }
        
        return null;
    }
    
    private Result tryEnhancedRotation(byte[] yData, int width, int height) {
        try {
            // Try 90-degree rotations with enhanced processing
            int[] rotations = {90, 180, 270};
            
            for (int rotation : rotations) {
                byte[] rotatedData = rotateYUVEnhanced(yData, width, height, rotation);
                int newWidth = (rotation == 90 || rotation == 270) ? height : width;
                int newHeight = (rotation == 90 || rotation == 270) ? width : height;
                
                Result result = tryDecodeWithEnhancedProcessing(rotatedData, newWidth, newHeight, 
                    0, 0, newWidth, newHeight);
                if (result != null) return result;
            }
            
            return null;
        } catch (Exception e) {
            return null;
        }
    }
    
    private Result tryScaleVariations(byte[] yData, int width, int height) {
        try {
            // Try different scaling for high/low density barcodes
            float[] scales = {1.5f, 0.75f, 0.5f};
            
            for (float scale : scales) {
                byte[] scaledData = scaleYUVData(yData, width, height, scale);
                int newWidth = (int)(width * scale);
                int newHeight = (int)(height * scale);
                
                if (newWidth > 0 && newHeight > 0 && scaledData != null) {
                    Result result = tryDecodeWithEnhancedProcessing(scaledData, newWidth, newHeight, 
                        0, 0, newWidth, newHeight);
                    if (result != null) return result;
                }
            }
            
            return null;
        } catch (Exception e) {
            return null;
        }
    }
    
    private byte[] invertYUVData(byte[] data, int length) {
        byte[] inverted = new byte[data.length];
        System.arraycopy(data, 0, inverted, 0, data.length);
        
        // Invert only the Y (luminance) component
        for (int i = 0; i < length; i++) {
            inverted[i] = (byte)(255 - (inverted[i] & 0xFF));
        }
        
        return inverted;
    }
    
    private byte[] rotateYUVEnhanced(byte[] data, int width, int height, int rotation) {
        if (rotation == 0) return data;
        
        byte[] rotated = new byte[data.length];
        int index = 0;
        
        // Only rotate the Y component for barcode detection
        int ySize = width * height;
        
        switch (rotation) {
            case 90:
                for (int x = 0; x < width; x++) {
                    for (int y = height - 1; y >= 0; y--) {
                        rotated[index++] = data[y * width + x];
                    }
                }
                break;
            case 180:
                for (int y = height - 1; y >= 0; y--) {
                    for (int x = width - 1; x >= 0; x--) {
                        rotated[index++] = data[y * width + x];
                    }
                }
                break;
            case 270:
                for (int x = width - 1; x >= 0; x--) {
                    for (int y = 0; y < height; y++) {
                        rotated[index++] = data[y * width + x];
                    }
                }
                break;
        }
        
        // Copy remaining UV data as-is (not used for barcode detection)
        if (data.length > ySize) {
            System.arraycopy(data, ySize, rotated, ySize, data.length - ySize);
        }
        
        return rotated;
    }
    
    private byte[] scaleYUVData(byte[] data, int width, int height, float scale) {
        try {
            int newWidth = (int)(width * scale);
            int newHeight = (int)(height * scale);
            
            if (newWidth <= 0 || newHeight <= 0) return null;
            
            byte[] scaled = new byte[newWidth * newHeight];
            
            // Simple nearest neighbor scaling for Y component
            for (int y = 0; y < newHeight; y++) {
                for (int x = 0; x < newWidth; x++) {
                    int srcX = (int)(x / scale);
                    int srcY = (int)(y / scale);
                    
                    if (srcX < width && srcY < height) {
                        scaled[y * newWidth + x] = data[srcY * width + srcX];
                    }
                }
            }
            
            return scaled;
        } catch (Exception e) {
            return null;
        }
    }

    private void closeCamera() {
        try {
            cameraOpenCloseLock.acquire();
            if (captureSession != null) {
                captureSession.close();
                captureSession = null;
            }
            if (cameraDevice != null) {
                cameraDevice.close();
                cameraDevice = null;
            }
            if (imageReader != null) {
                imageReader.close();
                imageReader = null;
            }
        } catch (InterruptedException e) {
            throw new RuntimeException("Interrupted while trying to lock camera closing.", e);
        } finally {
            cameraOpenCloseLock.release();
        }
    }

    // TextureView.SurfaceTextureListener methods
    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
        openCamera(width, height);
    }

    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
        // Handle size changes if needed
    }

    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
        closeCamera();
        return true;
    }

    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
        // Handle frame updates if needed
    }
}