package org.osmdroid.views.overlay;

import org.osmdroid.ResourceProxy;
import org.osmdroid.views.MapView;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.PorterDuff.Mode;
import android.os.Build;

/**
 * This will allow an {@link Overlay} that is not HW acceleration compatible to work in a HW accelerated
 * MapView. It will create a screen-sized backing Bitmap for the Overlay to draw to and then will draw the
 * Bitmap to the HW accelerated canvas. Due to the extra work, it does not draw the shadow layer. If the
 * Canvas passed into the Overlay is not HW accelerated or if {@link #isUsingBackingBitmap()} returns false
 * then it draws normally (including the shadow layer) without the backing Bitmap. <br/>
 * <br/>
 * TODO:
 * <ol>
 * <li>Implement a flag to determine if the drawing has actually changed. If not, then reuse the last frame's
 * backing bitmap. This will prevent having to re-upload the bitmap texture to GPU.</li>
 * </ol>
 */
public abstract class NonAcceleratedOverlay extends Overlay
{
    private Bitmap mBackingBitmap;
    private Canvas mBackingCanvas;
    private final Matrix mBackingMatrix = new Matrix();
    private final Matrix mCanvasIdentityMatrix = new Matrix();

    /**
     * A delegate for {@link #draw(Canvas, MapView, boolean)}.
     */
    protected abstract void onDraw(Canvas c, MapView osmv, boolean shadow);

    public NonAcceleratedOverlay(Context ctx)
    {
        super(ctx);
    }

    public NonAcceleratedOverlay(ResourceProxy pResourceProxy)
    {
        super(pResourceProxy);
    }

	/**
	 * Override if you really want access to the original (possibly) accelerated canvas.
	 */
	protected void onDraw(Canvas c, Canvas acceleratedCanvas, MapView osmv, boolean shadow) {
		onDraw(c, osmv, shadow);
	}

    /**
     * Allow forcing this overlay to skip drawing using backing Bitmap by returning false.
     */
    public boolean isUsingBackingBitmap()
    {
        return true;
    }

    @Override
    protected final void draw(Canvas c, MapView osmv, boolean shadow)
    {
        // First check to see if we want to use the backing bitmap
		final boolean atLeastHoneycomb = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;
        if (isUsingBackingBitmap() && atLeastHoneycomb && c.isHardwareAccelerated()) {
            // Drawing a shadow layer would require a second backing Bitmap due to the way HW
            // accelerated drawBitmap works. One could extend this Overlay to implement that if needed.
            if (shadow)
                return;

			// If we don't have any drawing area, then don't draw
			if (c.getWidth() == 0 || c.getHeight() == 0)
				return;

            if (mBackingBitmap == null || mBackingBitmap.getWidth() != c.getWidth()
                    || mBackingBitmap.getHeight() != c.getHeight()) {
                mBackingBitmap = Bitmap.createBitmap(c.getWidth(), c.getHeight(), Config.ARGB_8888);
                mBackingCanvas = new Canvas(mBackingBitmap);
            }

            mBackingCanvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
            c.getMatrix(mBackingMatrix);
            mBackingCanvas.setMatrix(mBackingMatrix);
			onDraw(mBackingCanvas, c, osmv, shadow);
            c.save();
            c.getMatrix(mCanvasIdentityMatrix);
            mCanvasIdentityMatrix.invert(mCanvasIdentityMatrix);
            c.concat(mCanvasIdentityMatrix);
            c.drawBitmap(mBackingBitmap, 0, 0, null);
            c.restore();
        } else
			onDraw(c, c, osmv, shadow);
    }
}

@@ -99,16 +99,73 @@
 MapView;
+%0Aimport org.slf4j.Logger;%0Aimport org.slf4j.LoggerFactory;
 %0A%0Aimport
@@ -1241,16 +1241,112 @@
 erlay%0A%7B%0A
+    private static final Logger logger = LoggerFactory.getLogger(NonAcceleratedOverlay.class);%0A%0A
     priv
@@ -2266,24 +2266,157 @@
 rue;%0A    %7D%0A%0A
+%09@Override%0A%09public void onDetach(MapView mapView) %7B%0A%09%09mBackingBitmap = null;%0A%09%09mBackingCanvas = null;%0A%09%09super.onDetach(mapView);%0A%09%7D%0A%0A
     @Overrid
@@ -3257,32 +3257,85 @@
 ht()) %7B%0A
-                
+%09%09%09%09mBackingBitmap = null;%0A%09%09%09%09mBackingCanvas = null;%0A%09%09%09%09try %7B%0A%09%09%09%09%09
 mBacking
@@ -3391,17 +3391,24 @@
 eight(),
- 
+%0A%09%09%09%09%09%09%09
 Config.A
@@ -3418,32 +3418,182 @@
 _8888);%0A
-                
+%09%09%09%09%7D catch (OutOfMemoryError e) %7B%0A%09%09%09%09%09logger.error(%22OutOfMemoryError creating backing bitmap in NonAcceleratedOverlay.%22);%0A%09%09%09%09%09System.gc();%0A%09%09%09%09%09return;%0A%09%09%09%09%7D%0A%0A%09%09%09%09
 mBacking
@@ -3629,28 +3629,19 @@
 itmap);%0A
-            
+%09%09%09
 %7D%0A%0A     

Handle memory issues more gracefully in NonAcceleratedOverlay.