package com.terrylinla.rnsketchcanvas;

import android.database.Cursor;
import android.graphics.Typeface;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Matrix;
import android.media.ExifInterface;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Base64;
import android.util.Log;
import android.view.View;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.events.RCTEventEmitter;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;

class CanvasText {
    public String text;
    public Paint paint;
    public PointF anchor, position, drawPosition, lineOffset;
    public boolean isAbsoluteCoordinate;
    public Rect textBounds;
    public float height;
    public int id; // Added to store the text ID
    public boolean isSelected = false; // Added for text selection
    public Boolean draggable = true; // Whether the text can be dragged
}

public class SketchCanvas extends View {

    private ArrayList<SketchData> mPaths = new ArrayList<SketchData>();
    private SketchData mCurrentPath = null;
    private RectF mImageBounds = null; // Added to store the image bounds for coordinate mapping

    private ThemedReactContext mContext;
    private boolean mDisableHardwareAccelerated = false;

    private Paint mPaint = new Paint();
    public Bitmap mDrawingBitmap = null, mTranslucentDrawingBitmap = null;
    private Canvas mDrawingCanvas = null, mTranslucentDrawingCanvas = null;

    private boolean mNeedsFullRedraw = true;

    private int mOriginalWidth, mOriginalHeight;
    public Bitmap mBackgroundImage;
    private String mContentMode;

    private ArrayList<CanvasText> mArrCanvasText = new ArrayList<CanvasText>();
    private ArrayList<CanvasText> mArrTextOnSketch = new ArrayList<CanvasText>();
    private ArrayList<CanvasText> mArrSketchOnText = new ArrayList<CanvasText>();

    public SketchCanvas(ThemedReactContext context) {
        super(context);
        mContext = context;
    }

    private Uri getFileUri(String filepath) {
        Uri uri = Uri.parse(filepath);
        if (uri.getScheme() == null) {
            uri = Uri.parse("file://" + filepath);
        }
        return uri;
    }

    private String getOriginalFilepath(String filepath) {
        Uri uri = getFileUri(filepath);
        String originalFilepath = filepath;
        if (uri.getScheme().equals("content")) {
            try {
                Cursor cursor = mContext.getContentResolver().query(uri, null, null, null, null);
                if (cursor.moveToFirst()) {
                    originalFilepath = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA));
                }
                cursor.close();
            } catch (IllegalArgumentException ignored) {
            }
        }
        return originalFilepath;
    }

    public boolean openImageFile(String filename, String directory, String mode) {
        if (filename != null) {
            try {
                // Clean up any existing background image
                if (mBackgroundImage != null) {
                    mBackgroundImage.recycle();
                    mBackgroundImage = null;
                }
                
                // Get resource ID if the image is in resources
                int res = mContext.getResources().getIdentifier(
                        filename.lastIndexOf('.') == -1 ? filename : filename.substring(0, filename.lastIndexOf('.')),
                        "drawable",
                        mContext.getPackageName());
                
                // Set up bitmap options for efficient loading
                BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
                bitmapOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;
                
                // Get the file path
                String originalFilepath = getOriginalFilepath(filename);
                File file = new File(originalFilepath, directory == null ? "" : directory);
                
                // Load the bitmap
                Bitmap bitmap = null;
                if (res != 0) {
                    // Load from resources
                    bitmap = BitmapFactory.decodeResource(mContext.getResources(), res, bitmapOptions);
                    Log.d("SketchCanvas", "Loaded image from resources: " + filename);
                } else {
                    // Load from file
                    bitmap = BitmapFactory.decodeFile(file.toString(), bitmapOptions);
                    Log.d("SketchCanvas", "Loaded image from file: " + file.toString());
                }
                
                if (bitmap != null) {
                    try {
                        // Get the orientation from the EXIF data (only for file images)
                        if (res == 0) {
                            ExifInterface exif = new ExifInterface(file.getAbsolutePath());
                            Matrix matrix = new Matrix();
                            int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
                            
                            // Apply the appropriate rotation based on orientation
                            switch (orientation) {
                                case ExifInterface.ORIENTATION_ROTATE_90:
                                    matrix.postRotate(90);
                                    break;
                                case ExifInterface.ORIENTATION_ROTATE_180:
                                    matrix.postRotate(180);
                                    break;
                                case ExifInterface.ORIENTATION_ROTATE_270:
                                    matrix.postRotate(270);
                                    break;
                                case ExifInterface.ORIENTATION_FLIP_HORIZONTAL:
                                    matrix.preScale(-1.0f, 1.0f);
                                    break;
                                case ExifInterface.ORIENTATION_FLIP_VERTICAL:
                                    matrix.preScale(1.0f, -1.0f);
                                    break;
                                case ExifInterface.ORIENTATION_TRANSPOSE:
                                    matrix.preRotate(90);
                                    matrix.preScale(-1.0f, 1.0f);
                                    break;
                                case ExifInterface.ORIENTATION_TRANSVERSE:
                                    matrix.preRotate(270);
                                    matrix.preScale(-1.0f, 1.0f);
                                    break;
                                default:
                                    // No transformation needed
                                    break;
                            }
                            
                            // Only create a new bitmap if we need to transform it
                            if (!matrix.isIdentity()) {
                                Bitmap transformedBitmap = Bitmap.createBitmap(
                                    bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
                                
                                // Recycle the original bitmap if it's different from the transformed one
                                if (transformedBitmap != bitmap) {
                                    bitmap.recycle();
                                    bitmap = transformedBitmap;
                                }
                                
                                Log.d("SketchCanvas", "Applied EXIF orientation: " + orientation);
                            }
                        }
                        
                        // Check if we need to scale down the image to save memory
                        int maxSize = Math.max(getWidth(), getHeight()) * 2; // Allow for some oversampling
                        if (bitmap.getWidth() > maxSize || bitmap.getHeight() > maxSize) {
                            float scale = (float) maxSize / Math.max(bitmap.getWidth(), bitmap.getHeight());
                            Matrix matrix = new Matrix();
                            matrix.postScale(scale, scale);
                            
                            Bitmap scaledBitmap = Bitmap.createBitmap(
                                bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
                            
                            if (scaledBitmap != bitmap) {
                                bitmap.recycle();
                                bitmap = scaledBitmap;
                            }
                            
                            Log.d("SketchCanvas", "Scaled down large image to save memory");
                        }
                        
                        Log.d("SketchCanvas", "Image loaded successfully with dimensions: " + 
                              bitmap.getWidth() + "x" + bitmap.getHeight());
                    } catch (Exception e) {
                        Log.e("SketchCanvas", "Error processing image: " + e.getMessage());
                    }
                    
                    // Store the image and its properties
                    mBackgroundImage = bitmap;
                    mOriginalHeight = bitmap.getHeight();
                    mOriginalWidth = bitmap.getWidth();
                    mContentMode = mode;
                    
                    // Redraw the canvas
                    invalidateCanvas(true);
                    
                    return true;
                } else {
                    Log.e("SketchCanvas", "Failed to load image: " + filename);
                }
            } catch (Exception e) {
                Log.e("SketchCanvas", "Error opening image file: " + e.getMessage());
            }
        }
        return false;
    }

    public void setCanvasText(ReadableArray aText) {
        mArrCanvasText.clear();
        mArrSketchOnText.clear();
        mArrTextOnSketch.clear();

        if (aText != null) {
            for (int i = 0; i < aText.size(); i++) {
                ReadableMap property = aText.getMap(i);
                if (property.hasKey("text")) {
                    String alignment = property.hasKey("alignment") ? property.getString("alignment") : "Left";
                    int lineOffset = 0, maxTextWidth = 0;
                    String[] lines = property.getString("text").split("\n");
                    ArrayList<CanvasText> textSet = new ArrayList<CanvasText>(lines.length);
                    for (String line : lines) {
                        ArrayList<CanvasText> arr = property.hasKey("overlay") && "TextOnSketch".equals(property.getString("overlay")) ? mArrTextOnSketch : mArrSketchOnText;
                        CanvasText text = new CanvasText();
                        Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
                        p.setTextAlign(Paint.Align.LEFT);
                        text.text = line;
                        if (property.hasKey("font")) {
                            Typeface font;
                            try {
                                font = Typeface.createFromAsset(mContext.getAssets(), property.getString("font"));
                            } catch (Exception ex) {
                                font = Typeface.create(property.getString("font"), Typeface.NORMAL);
                            }
                            p.setTypeface(font);
                        }
                        p.setTextSize(property.hasKey("fontSize") ? (float) property.getDouble("fontSize") : 12);
                        p.setColor(property.hasKey("fontColor") ? property.getInt("fontColor") : 0xFF000000);
                        text.anchor = property.hasKey("anchor") ? new PointF((float) property.getMap("anchor").getDouble("x"), (float) property.getMap("anchor").getDouble("y")) : new PointF(0, 0);
                        text.position = property.hasKey("position") ? new PointF((float) property.getMap("position").getDouble("x"), (float) property.getMap("position").getDouble("y")) : new PointF(0, 0);
                        text.paint = p;
                        text.isAbsoluteCoordinate = !(property.hasKey("coordinate") && "Ratio".equals(property.getString("coordinate")));
                        text.textBounds = new Rect();
                        p.getTextBounds(text.text, 0, text.text.length(), text.textBounds);

                        text.lineOffset = new PointF(0, lineOffset);
                        lineOffset += text.textBounds.height() * 1.5 * (property.hasKey("lineHeightMultiple") ? property.getDouble("lineHeightMultiple") : 1);
                        maxTextWidth = Math.max(maxTextWidth, text.textBounds.width());

                        arr.add(text);
                        mArrCanvasText.add(text);
                        textSet.add(text);
                    }
                    for (CanvasText text : textSet) {
                        text.height = lineOffset;
                        if (text.textBounds.width() < maxTextWidth) {
                            float diff = maxTextWidth - text.textBounds.width();
                            text.textBounds.left += diff * text.anchor.x;
                            text.textBounds.right += diff * text.anchor.x;
                        }
                    }
                    if (getWidth() > 0 && getHeight() > 0) {
                        for (CanvasText text : textSet) {
                            text.height = lineOffset;
                            PointF position = new PointF(text.position.x, text.position.y);
                            if (!text.isAbsoluteCoordinate) {
                                position.x *= getWidth();
                                position.y *= getHeight();
                            }
                            position.x -= text.textBounds.left;
                            position.y -= text.textBounds.top;
                            position.x -= (text.textBounds.width() * text.anchor.x);
                            position.y -= (text.height * text.anchor.y);
                            text.drawPosition = position;
                        }
                    }
                    if (lines.length > 1) {
                        for (CanvasText text : textSet) {
                            switch (alignment) {
                                case "Left":
                                default:
                                    break;
                                case "Right":
                                    text.lineOffset.x = (maxTextWidth - text.textBounds.width());
                                    break;
                                case "Center":
                                    text.lineOffset.x = (maxTextWidth - text.textBounds.width()) / 2;
                                    break;
                            }
                        }
                    }
                }
            }
        }

        invalidateCanvas(false);
    }

    public void clear() {
        mPaths.clear();
        mCurrentPath = null;
        mNeedsFullRedraw = true;
        invalidateCanvas(true);
    }
    
    /**
     * Invalidates the canvas and forces a redraw
     * @param shouldDrawPaths Whether to redraw all paths
     */
    private void invalidateCanvas(boolean shouldDrawPaths) {
        if (shouldDrawPaths) {
            if (mDrawingCanvas != null) {
                mDrawingCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
                for (SketchData path : mPaths) {
                    path.draw(mDrawingCanvas);
                }
            }
            if (mCurrentPath != null && mCurrentPath.isTranslucent && mTranslucentDrawingCanvas != null) {
                mTranslucentDrawingCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
                mCurrentPath.draw(mTranslucentDrawingCanvas);
            }
        }
        invalidate();
    }
    
    public void addText(ReadableMap textData) {
        if (textData == null) return;
        
        // Create a new CanvasText object
        CanvasText newText = new CanvasText();
        
        // Set text properties
        newText.text = textData.hasKey("text") ? textData.getString("text") : "";
        
        // Set font properties
        Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
        p.setTextAlign(Paint.Align.LEFT);
        
        if (textData.hasKey("font")) {
            Typeface font;
            try {
                font = Typeface.createFromAsset(mContext.getAssets(), textData.getString("font"));
            } catch (Exception ex) {
                font = Typeface.create(textData.getString("font"), Typeface.NORMAL);
            }
            p.setTypeface(font);
        }
        
        p.setTextSize(textData.hasKey("fontSize") ? (float) textData.getDouble("fontSize") : 12);
        p.setColor(textData.hasKey("fontColor") ? textData.getInt("fontColor") : 0xFF000000);
        
        // Set position and anchor
        newText.anchor = textData.hasKey("anchor") ? 
            new PointF((float) textData.getMap("anchor").getDouble("x"), (float) textData.getMap("anchor").getDouble("y")) : 
            new PointF(0, 0);
            
        newText.position = textData.hasKey("position") ? 
            new PointF((float) textData.getMap("position").getDouble("x"), (float) textData.getMap("position").getDouble("y")) : 
            new PointF(0, 0);
            
        newText.paint = p;
        newText.isAbsoluteCoordinate = !(textData.hasKey("coordinate") && "Ratio".equals(textData.getString("coordinate")));
        newText.textBounds = new Rect();
        p.getTextBounds(newText.text, 0, newText.text.length(), newText.textBounds);
        
        // Set ID
        newText.id = textData.hasKey("id") ? textData.getInt("id") : (int)(Math.random() * 100000000);
        
        // Set draggable property
        if (textData.hasKey("draggable")) {
            newText.draggable = textData.getBoolean("draggable");
        }
        
        // Calculate drawing position
        float x = newText.position.x;
        float y = newText.position.y;
        
        if (!newText.isAbsoluteCoordinate) {
            x *= getWidth();
            y *= getHeight();
        }
        
        // Adjust for anchor
        x -= newText.textBounds.width() * newText.anchor.x;
        y -= newText.textBounds.height() * newText.anchor.y;
        
        newText.drawPosition = new PointF(x, y);
        newText.lineOffset = new PointF(0, 0);
        
        // Add to text array
        mArrTextOnSketch.add(newText);
        mArrCanvasText.add(newText);
        
        // Trigger redraw
        invalidate();
        
        // Notify JS
        WritableMap event = Arguments.createMap();
        event.putInt("textId", newText.id);
        mContext.getJSModule(RCTEventEmitter.class).receiveEvent(
                getId(),
                "topChange",
                event);
    }
    
    public void updateText(ReadableMap textData) {
        if (textData == null || !textData.hasKey("id")) return;
        
        int textId = textData.getInt("id");
        boolean found = false;
        
        // Find and remove the text with the given ID
        for (int i = 0; i < mArrTextOnSketch.size(); i++) {
            CanvasText existingText = mArrTextOnSketch.get(i);
            if (existingText.id == textId) {
                mArrTextOnSketch.remove(i);
                found = true;
                break;
            }
        }
        
        // Also remove from mArrCanvasText
        for (int i = 0; i < mArrCanvasText.size(); i++) {
            CanvasText existingText = mArrCanvasText.get(i);
            if (existingText.id == textId) {
                mArrCanvasText.remove(i);
                break;
            }
        }
        
        // If found, add the updated text
        if (found) {
            addText(textData);
        }
    }
    
    public void deleteText(int textId) {
        boolean found = false;
        
        // Find and remove the text with the given ID
        for (int i = 0; i < mArrTextOnSketch.size(); i++) {
            CanvasText existingText = mArrTextOnSketch.get(i);
            if (existingText.id == textId) {
                mArrTextOnSketch.remove(i);
                found = true;
                break;
            }
        }
        
        // Also remove from mArrCanvasText
        for (int i = 0; i < mArrCanvasText.size(); i++) {
            CanvasText existingText = mArrCanvasText.get(i);
            if (existingText.id == textId) {
                mArrCanvasText.remove(i);
                break;
            }
        }
        
        if (found) {
            invalidate();
        }
    }

    public void newPath(int id, int strokeColor, float strokeWidth) {
        mCurrentPath = new SketchData(id, strokeColor, strokeWidth);
        mPaths.add(mCurrentPath);
        boolean isErase = strokeColor == Color.TRANSPARENT;
        if (isErase && mDisableHardwareAccelerated == false) {
            mDisableHardwareAccelerated = true;
            setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        }
        invalidateCanvas(true);
    }

    public void addPoint(float x, float y) {
        Rect updateRect = mCurrentPath.addPoint(new PointF(x, y));

        if (mCurrentPath.isTranslucent) {
            mTranslucentDrawingCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.MULTIPLY);
            mCurrentPath.draw(mTranslucentDrawingCanvas);
        } else {
            mCurrentPath.drawLastPoint(mDrawingCanvas);
        }
        invalidate(updateRect);
    }

    public void addPath(int id, int strokeColor, float strokeWidth, ArrayList<PointF> points) {
        boolean exist = false;
        for (SketchData data : mPaths) {
            if (data.id == id) {
                exist = true;
                break;
            }
        }

        if (!exist) {
            SketchData newPath = new SketchData(id, strokeColor, strokeWidth, points);
            mPaths.add(newPath);
            boolean isErase = strokeColor == Color.TRANSPARENT;
            if (isErase && mDisableHardwareAccelerated == false) {
                mDisableHardwareAccelerated = true;
                setLayerType(View.LAYER_TYPE_SOFTWARE, null);
            }
            newPath.draw(mDrawingCanvas);
            invalidateCanvas(true);
        }
    }

    public void deletePath(int id) {
        int index = -1;
        for (int i = 0; i < mPaths.size(); i++) {
            if (mPaths.get(i).id == id) {
                index = i;
                break;
            }
        }

        if (index > -1) {
            mPaths.remove(index);
            mNeedsFullRedraw = true;
            invalidateCanvas(true);
        }
    }

    public void end() {
        if (mCurrentPath != null) {
            if (mCurrentPath.isTranslucent) {
                mCurrentPath.draw(mDrawingCanvas);
                mTranslucentDrawingCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.MULTIPLY);
            }
            mCurrentPath = null;
        }
    }

    public void onSaved(boolean success, String path) {
        WritableMap event = Arguments.createMap();
        event.putBoolean("success", success);
        event.putString("path", path);
        mContext.getJSModule(RCTEventEmitter.class).receiveEvent(
                getId(),
                "topChange",
                event);
    }

    public void save(String format, String folder, String filename, boolean transparent, boolean includeImage, boolean includeText, boolean cropToImageSize) {
        File f = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) + File.separator + folder);
        boolean success = f.exists() ? true : f.mkdirs();
        if (success) {
            Bitmap bitmap = createImage(format.equals("png") && transparent, includeImage, includeText, cropToImageSize);

            File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) +
                    File.separator + folder + File.separator + filename + (format.equals("png") ? ".png" : ".jpg"));
            try {
                bitmap.compress(
                        format.equals("png") ? Bitmap.CompressFormat.PNG : Bitmap.CompressFormat.JPEG,
                        format.equals("png") ? 100 : 90,
                        new FileOutputStream(file));
                this.onSaved(true, file.getPath());
            } catch (Exception e) {
                e.printStackTrace();
                onSaved(false, null);
            }
        } else {
            Log.e("SketchCanvas", "Failed to create folder!");
            onSaved(false, null);
        }
    }

    public String getBase64(String format, boolean transparent, boolean includeImage, boolean includeText, boolean cropToImageSize) {
        WritableMap event = Arguments.createMap();
        Bitmap bitmap = createImage(format.equals("png") && transparent, includeImage, includeText, cropToImageSize);
        ByteArrayOutputStream byteArrayOS = new ByteArrayOutputStream();

        bitmap.compress(
                format.equals("png") ? Bitmap.CompressFormat.PNG : Bitmap.CompressFormat.JPEG,
                format.equals("png") ? 100 : 90,
                byteArrayOS);
        return Base64.encodeToString(byteArrayOS.toByteArray(), Base64.DEFAULT);
    }
    
    /**
     * Creates a bitmap image of the current canvas content
     * @param transparent Whether the background should be transparent
     * @param includeImage Whether to include the background image
     * @param includeText Whether to include text elements
     * @param cropToImageSize Whether to crop the output to the background image size
     * @return A bitmap containing the canvas content
     */
    private Bitmap createImage(boolean transparent, boolean includeImage, boolean includeText, boolean cropToImageSize) {
        Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        
        // Fill with white or transparent background
        if (!transparent) {
            canvas.drawColor(Color.WHITE);
        }
        
        // Draw background image if requested
        if (includeImage && mBackgroundImage != null) {
            Paint paint = new Paint();
            paint.setAntiAlias(true);
            paint.setFilterBitmap(true);
            paint.setDither(true);
            
            RectF destRect = new RectF(0, 0, getWidth(), getHeight());
            canvas.drawBitmap(mBackgroundImage, null, destRect, paint);
        }
        
        // Draw text that should appear behind the sketch
        if (includeText) {
            for (CanvasText text : mArrSketchOnText) {
                drawTextWithBackground(canvas, text);
            }
        }
        
        // Draw the sketch
        canvas.drawBitmap(mDrawingBitmap, 0, 0, null);
        
        // Draw text that should appear on top of the sketch
        if (includeText) {
            for (CanvasText text : mArrTextOnSketch) {
                drawTextWithBackground(canvas, text);
            }
        }
        
        // Crop to image size if requested and we have a background image
        if (cropToImageSize && mBackgroundImage != null && mImageBounds != null) {
            try {
                return Bitmap.createBitmap(
                    bitmap, 
                    (int)mImageBounds.left, 
                    (int)mImageBounds.top, 
                    (int)mImageBounds.width(), 
                    (int)mImageBounds.height()
                );
            } catch (Exception e) {
                Log.e("SketchCanvas", "Error cropping image: " + e.getMessage());
            }
        }
        
        return bitmap;
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        if (getWidth() > 0 && getHeight() > 0) {
            // Check if this is an orientation change
            boolean isOrientationChange = (oldw > 0 && oldh > 0) && 
                ((w > h && oldw < oldh) || (w < h && oldw > oldh));
            
            // Log size change for debugging
            Log.d("SketchCanvas", "Size changed: " + oldw + "x" + oldh + " -> " + w + "x" + h + 
                  (isOrientationChange ? " (orientation change)" : ""));
            
            // Create new bitmaps for the new size
            if (mDrawingBitmap != null) {
                mDrawingBitmap.recycle();
            }
            mDrawingBitmap = Bitmap.createBitmap(getWidth(), getHeight(),
                    Bitmap.Config.ARGB_8888);
            mDrawingCanvas = new Canvas(mDrawingBitmap);
            
            if (mTranslucentDrawingBitmap != null) {
                mTranslucentDrawingBitmap.recycle();
            }
            mTranslucentDrawingBitmap = Bitmap.createBitmap(getWidth(), getHeight(),
                    Bitmap.Config.ARGB_8888);
            mTranslucentDrawingCanvas = new Canvas(mTranslucentDrawingBitmap);

            // If this is an orientation change, we need to adjust the coordinates
            if (isOrientationChange) {
                // Check if we have a background image
                if (mBackgroundImage != null) {
                    // Log the image dimensions for debugging
                    Log.d("SketchCanvas", "Background image dimensions during orientation change: " + 
                          mBackgroundImage.getWidth() + "x" + mBackgroundImage.getHeight());
                    
                    // Check if the image and canvas have different orientations
                    boolean isCanvasPortrait = h > w;
                    boolean isImagePortrait = mBackgroundImage.getHeight() > mBackgroundImage.getWidth();
                    
                    // Log the orientations for debugging
                    Log.d("SketchCanvas", "Canvas orientation: " + (isCanvasPortrait ? "portrait" : "landscape") + 
                          ", Image orientation: " + (isImagePortrait ? "portrait" : "landscape"));
                }
                
                // Handle the orientation change
                handleOrientationChange(oldw, oldh, w, h);
            } else {
                // Just recalculate text positions normally
                recalculateTextPositions();
            }

            // Redraw all paths on the new canvas
            mNeedsFullRedraw = true;
            
            // Redraw everything
            invalidate();
            
            // Notify JS about size change
            WritableMap event = Arguments.createMap();
            event.putInt("width", w);
            event.putInt("height", h);
            event.putBoolean("isOrientationChange", isOrientationChange);
            if (mBackgroundImage != null) {
                event.putInt("imageWidth", mBackgroundImage.getWidth());
                event.putInt("imageHeight", mBackgroundImage.getHeight());
            }
            mContext.getJSModule(RCTEventEmitter.class).receiveEvent(
                    getId(),
                    "onSizeChange",
                    event);
        }
    }
    
    /**
     * Recalculates the drawing positions for all text elements
     */
    private void recalculateTextPositions() {
        for (CanvasText text : mArrCanvasText) {
            PointF position = new PointF(text.position.x, text.position.y);
            if (!text.isAbsoluteCoordinate) {
                position.x *= getWidth();
                position.y *= getHeight();
            }

            position.x -= text.textBounds.left;
            position.y -= text.textBounds.top;
            position.x -= (text.textBounds.width() * text.anchor.x);
            position.y -= (text.height * text.anchor.y);
            text.drawPosition = position;
        }
    }
    
    /**
     * Handles orientation changes by adjusting coordinates of paths and text
     */
    private void handleOrientationChange(int oldWidth, int oldHeight, int newWidth, int newHeight) {
        boolean wasPortrait = oldHeight > oldWidth;
        boolean isPortrait = newHeight > newWidth;
        
        Log.d("SketchCanvas", "Handling orientation change: " + 
              (wasPortrait ? "Portrait" : "Landscape") + " -> " + 
              (isPortrait ? "Portrait" : "Landscape"));
        
        // Only process if orientation actually changed
        if (wasPortrait != isPortrait) {
            // Check if we have a background image
            boolean hasBackgroundImage = mBackgroundImage != null;
            boolean isImagePortrait = hasBackgroundImage && (mBackgroundImage.getHeight() > mBackgroundImage.getWidth());
            
            // Determine if we need to handle image-relative coordinates
            boolean needsImageRelativeTransform = hasBackgroundImage && (isImagePortrait == wasPortrait);
            
            if (needsImageRelativeTransform) {
                Log.d("SketchCanvas", "Using image-relative coordinate transformation");
            }
            
            // Adjust path coordinates
            for (SketchData path : mPaths) {
                for (PointF point : path.points) {
                    // Convert to relative coordinates based on old dimensions
                    float relX = point.x / oldWidth;
                    float relY = point.y / oldHeight;
                    
                    // Calculate new coordinates based on orientation change
                    float newRelX, newRelY;
                    
                    if (needsImageRelativeTransform) {
                        // When the image orientation stays the same relative to itself,
                        // but the canvas orientation changes, we need to maintain the
                        // drawing positions relative to the image
                        
                        // Keep the same relative coordinates
                        newRelX = relX;
                        newRelY = relY;
                    } else {
                        // Standard orientation change transformation
                        if (wasPortrait && !isPortrait) {
                            // Portrait to Landscape
                            // Keep the relative position consistent with the visual appearance
                            newRelX = relY;
                            newRelY = 1.0f - relX;
                        } else {
                            // Landscape to Portrait
                            // Keep the relative position consistent with the visual appearance
                            newRelX = 1.0f - relY;
                            newRelY = relX;
                        }
                    }
                    
                    // Convert back to absolute coordinates based on new dimensions
                    point.x = newRelX * newWidth;
                    point.y = newRelY * newHeight;
                }
                
                // Rebuild the path with the new coordinates
                path.rebuildPath();
            }
            
            // Adjust text positions
            for (CanvasText text : mArrCanvasText) {
                // Convert to relative coordinates based on old dimensions
                float relX = text.isAbsoluteCoordinate ? text.position.x / oldWidth : text.position.x;
                float relY = text.isAbsoluteCoordinate ? text.position.y / oldHeight : text.position.y;
                
                // Calculate new coordinates based on orientation change
                float newRelX, newRelY;
                
                if (needsImageRelativeTransform) {
                    // When the image orientation stays the same relative to itself,
                    // but the canvas orientation changes, we need to maintain the
                    // text positions relative to the image
                    
                    // Keep the same relative coordinates
                    newRelX = relX;
                    newRelY = relY;
                } else {
                    // Standard orientation change transformation
                    if (wasPortrait && !isPortrait) {
                        // Portrait to Landscape
                        newRelX = relY;
                        newRelY = 1.0f - relX;
                    } else {
                        // Landscape to Portrait
                        newRelX = 1.0f - relY;
                        newRelY = relX;
                    }
                }
                
                // Convert back to absolute coordinates or keep as ratio based on coordinate type
                if (text.isAbsoluteCoordinate) {
                    text.position.x = newRelX * newWidth;
                    text.position.y = newRelY * newHeight;
                } else {
                    text.position.x = newRelX;
                    text.position.y = newRelY;
                }
                
                // Recalculate drawing position
                PointF position = new PointF(text.position.x, text.position.y);
                if (!text.isAbsoluteCoordinate) {
                    position.x *= newWidth;
                    position.y *= newHeight;
                }
                
                position.x -= text.textBounds.left;
                position.y -= text.textBounds.top;
                position.x -= (text.textBounds.width() * text.anchor.x);
                position.y -= (text.height * text.anchor.y);
                text.drawPosition = position;
            }
            
            // Log the transformation for debugging
            Log.d("SketchCanvas", "Orientation change transformation complete. " +
                  "Old size: " + oldWidth + "x" + oldHeight + ", " +
                  "New size: " + newWidth + "x" + newHeight + 
                  (needsImageRelativeTransform ? " (image-relative transform)" : ""));
        } else {
            // Same orientation, just recalculate positions
            recalculateTextPositions();
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        // Store the original canvas state
        canvas.save();
        
        // Variables to track if we need to apply transformations for the drawings
        boolean canvasTransformed = false;
        Matrix canvasMatrix = null;
        
        if (mBackgroundImage != null) {
            Rect dstRect = new Rect();
            canvas.getClipBounds(dstRect);
            
            // Create a paint object for high-quality scaling
            Paint bitmapPaint = new Paint();
            bitmapPaint.setFilterBitmap(true);
            bitmapPaint.setAntiAlias(true);
            bitmapPaint.setDither(true);
            
            // Check if we need to adjust for orientation
            boolean isCanvasPortrait = getHeight() > getWidth();
            boolean isImagePortrait = mBackgroundImage.getHeight() > mBackgroundImage.getWidth();
            
            // Calculate the destination rectangle based on content mode
            RectF destRectF = Utility.fillImage(
                mBackgroundImage.getWidth(),
                mBackgroundImage.getHeight(),
                dstRect.width(),
                dstRect.height(),
                mContentMode
            );
            
            // Store the image bounds for later use
            mImageBounds = new RectF(destRectF);
            
            if (isCanvasPortrait != isImagePortrait) {
                // The canvas and image have different orientations
                // We need to rotate the canvas before drawing
                
                // Calculate the center point for rotation
                float centerX = dstRect.width() / 2f;
                float centerY = dstRect.height() / 2f;
                
                // Translate to center, rotate, then translate back
                canvas.translate(centerX, centerY);
                
                if (isCanvasPortrait) {
                    // Landscape image in portrait canvas - rotate 90 degrees clockwise
                    canvas.rotate(90);
                } else {
                    // Portrait image in landscape canvas - rotate 90 degrees counter-clockwise
                    canvas.rotate(-90);
                }
                
                // Swap width and height for the destination rectangle
                float width = destRectF.width();
                float height = destRectF.height();
                
                // Create a new destination rectangle with swapped dimensions
                destRectF = new RectF(-height/2, -width/2, height/2, width/2);
                
                // Remember that we transformed the canvas
                canvasTransformed = true;
                
                // Store the transformation matrix for later use
                canvasMatrix = canvas.getMatrix();
                
                Log.d("SketchCanvas", "Drawing rotated background image: " + 
                      mBackgroundImage.getWidth() + "x" + mBackgroundImage.getHeight() + 
                      " to " + destRectF.width() + "x" + destRectF.height() +
                      " with mode: " + mContentMode + 
                      " (canvas: " + (isCanvasPortrait ? "portrait" : "landscape") + 
                      ", image: " + (isImagePortrait ? "portrait" : "landscape") + ")");
            } else {
                // The canvas and image have the same orientation
                // We can display the image normally
                Log.d("SketchCanvas", "Drawing background image: " + 
                      mBackgroundImage.getWidth() + "x" + mBackgroundImage.getHeight() + 
                      " to " + destRectF.width() + "x" + destRectF.height() +
                      " with mode: " + mContentMode);
            }
            
            // Draw the bitmap with high-quality scaling
            canvas.drawBitmap(mBackgroundImage, null, destRectF, bitmapPaint);
        }
        
        // Draw text that should appear behind the sketch
        for (CanvasText text : mArrSketchOnText) {
            drawTextWithBackground(canvas, text);
        }
        
        // If we need to redraw all paths, do it now
        if (mNeedsFullRedraw && mDrawingCanvas != null) {
            mDrawingCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.MULTIPLY);
            for (SketchData path : mPaths) {
                path.draw(mDrawingCanvas);
            }
            mNeedsFullRedraw = false;
        }
        
        // Draw the paths
        if (mDrawingBitmap != null) {
            canvas.drawBitmap(mDrawingBitmap, 0, 0, mPaint);
        }
        
        // If we have a current translucent path, draw it
        if (mTranslucentDrawingBitmap != null && mCurrentPath != null && mCurrentPath.isTranslucent) {
            canvas.drawBitmap(mTranslucentDrawingBitmap, 0, 0, mPaint);
        }
        
        // Draw text that should appear on top of the sketch
        for (CanvasText text : mArrTextOnSketch) {
            drawTextWithBackground(canvas, text);
            
            // Draw selection indicator if text is selected
            if (text.isSelected) {
                Paint selectionPaint = new Paint();
                selectionPaint.setStyle(Paint.Style.STROKE);
                selectionPaint.setColor(Color.BLUE);
                selectionPaint.setStrokeWidth(2);
                
                // Calculate the bounds of the text
                RectF bounds = new RectF(
                    text.drawPosition.x,
                    text.drawPosition.y,
                    text.drawPosition.x + text.textBounds.width(),
                    text.drawPosition.y + text.height
                );
                
                // Draw the selection rectangle
                canvas.drawRect(bounds, selectionPaint);
                
                // Draw the selection handles
                float handleRadius = 10;
                canvas.drawCircle(bounds.left, bounds.top, handleRadius, selectionPaint);
                canvas.drawCircle(bounds.right, bounds.top, handleRadius, selectionPaint);
                canvas.drawCircle(bounds.left, bounds.bottom, handleRadius, selectionPaint);
                canvas.drawCircle(bounds.right, bounds.bottom, handleRadius, selectionPaint);
            }
        }
        
        // Restore the canvas to its original state
        canvas.restore();
    }
    
    /**
     * Draws text with its background on the canvas
     * @param canvas The canvas to draw on
     * @param text The text object to draw
     */
    private void drawTextWithBackground(Canvas canvas, CanvasText text) {
        if (text == null || canvas == null) return;
        
        // Calculate the position based on whether we're using absolute or relative coordinates
        float x, y;
        if (text.isAbsoluteCoordinate) {
            x = text.position.x;
            y = text.position.y;
        } else {
            x = text.position.x * getWidth();
            y = text.position.y * getHeight();
        }
        
        // Apply the anchor point offset
        x -= text.textBounds.width() * text.anchor.x;
        y -= text.height * text.anchor.y;
        
        // Apply the line offset for multi-line text
        x += text.lineOffset.x;
        y += text.lineOffset.y;
        
        // Set the draw position
        text.drawPosition = new PointF(x, y);
        
        // Draw the text
        canvas.drawText(
            text.text, 
            text.drawPosition.x, 
            text.drawPosition.y + text.textBounds.height(), 
            text.paint
        );
    }
    
    /**
     * Handle configuration changes
     * @param newConfig The new configuration
     */
    public void onConfigurationChanged(android.content.res.Configuration newConfig) {
        // This method is called when the device orientation changes
        // We need to handle it properly to maintain the canvas state
        
        // Notify any listeners about the orientation change
        WritableMap event = Arguments.createMap();
        event.putString("orientation", newConfig.orientation == android.content.res.Configuration.ORIENTATION_LANDSCAPE ? "landscape" : "portrait");
        mContext.getJSModule(RCTEventEmitter.class).receiveEvent(
                getId(),
                "onOrientationChange",
                event);
                
        // Force a redraw with the new dimensions
        mNeedsFullRedraw = true;
        invalidate();
    }
}
