package cgeo.geocaching.ui;

import cgeo.geocaching.R;
import cgeo.geocaching.geopoint.Geopoint;
import cgeo.geocaching.settings.Settings;
import cgeo.geocaching.utils.AngleUtils;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PaintFlagsDrawFilter;
import android.util.AttributeSet;
import android.view.View;

final public class CompassMiniView extends View {
    private Geopoint targetCoords = null;
    private float azimuth = 0;
    private float heading = 0;
    /**
     * remember the last state of drawing so we can avoid repainting for very small changes
     */
    private float lastDrawingAzimuth;

    /**
     * lazy initialized bitmap resource depending on selected skin
     */
    private static int arrowSkin = 0;
    /**
     * bitmap shared by all instances of the view
     */
    private static Bitmap compassArrow;
    /**
     * bitmap width
     */
    private static int compassArrowWidth;
    /**
     * bitmap height
     */
    private static int compassArrowHeight;
    /**
     * view instance counter for bitmap recycling
     */
    private static int instances = 0;

    /**
     * pixel size of the square arrow bitmap
     */
    private static final int ARROW_BITMAP_SIZE = 21;
    private static final PaintFlagsDrawFilter FILTER_SET = new PaintFlagsDrawFilter(0, Paint.FILTER_BITMAP_FLAG);
    private static final PaintFlagsDrawFilter FILTER_REMOVE = new PaintFlagsDrawFilter(Paint.FILTER_BITMAP_FLAG, 0);
    private static final float MINIMUM_ROTATION_DEGREES_FOR_REPAINT = 5;

    public CompassMiniView(Context context) {
        super(context);
        initializeResources(context);
    }

    public CompassMiniView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initializeResources(context);
    }

    private static void initializeResources(final Context context) {
        if (arrowSkin == 0) {
            if (Settings.isLightSkin()) {
                arrowSkin = R.drawable.compass_arrow_mini_black;
            } else {
                arrowSkin = R.drawable.compass_arrow_mini_white;
            }
        }
        if (compassArrow == null) {
            compassArrow = BitmapFactory.decodeResource(context.getResources(), arrowSkin);
            compassArrowWidth = compassArrow.getWidth();
            compassArrowHeight = compassArrow.getWidth();
        }
    }

    @Override
    public void onAttachedToWindow() {
        instances++;
    }

    @Override
    public void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        instances--;
        if (instances == 0) {
            if (compassArrow != null) {
                compassArrow.recycle();
                compassArrow = null;
            }
        }
    }

    public void setTargetCoords(final Geopoint point) {
        targetCoords = point;
    }

    public void updateAzimuth(float azimuth) {
        this.azimuth = azimuth;
        updateDirection();
    }

    public void updateHeading(float heading) {
        this.heading = heading;
        updateDirection();
    }

    public void updateCurrentCoords(final Geopoint currentCoords) {
        if (currentCoords == null || targetCoords == null) {
            return;
        }

        heading = currentCoords.bearingTo(targetCoords);
        updateDirection();
    }

    private void updateDirection() {
        if (compassArrow == null || compassArrow.isRecycled()) {
            return;
        }

        float azimuthRelative = calculateAzimuthRelative();

        // avoid updates on very small changes, which are not visible to the user
        float change = Math.abs(azimuthRelative - lastDrawingAzimuth);
        if (change < MINIMUM_ROTATION_DEGREES_FOR_REPAINT) {
            return;
        }

        // compass margins
        final int marginLeft = (getWidth() - compassArrowWidth) / 2;
        final int marginTop = (getHeight() - compassArrowHeight) / 2;

        invalidate(marginLeft, marginTop, (marginLeft + compassArrowWidth), (marginTop + compassArrowHeight));
    }

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

        float azimuthRelative = calculateAzimuthRelative();
        lastDrawingAzimuth = azimuthRelative;

        // compass margins
        canvas.setDrawFilter(FILTER_SET);

        final int canvasCenterX = getWidth() / 2;
        final int canvasCenterY = getHeight() / 2;

        final int marginLeft = (getWidth() - compassArrowWidth) / 2;
        final int marginTop = (getHeight() - compassArrowHeight) / 2;

        canvas.rotate(-azimuthRelative, canvasCenterX, canvasCenterY);
        canvas.drawBitmap(compassArrow, marginLeft, marginTop, null);
        canvas.rotate(azimuthRelative, canvasCenterX, canvasCenterY);

        canvas.setDrawFilter(FILTER_REMOVE);
    }

    private float calculateAzimuthRelative() {
        return AngleUtils.normalize(azimuth - heading);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
    }

    private int measureWidth(int measureSpec) {
        final int specMode = MeasureSpec.getMode(measureSpec);
        final int specSize = MeasureSpec.getSize(measureSpec);

        if (specMode == MeasureSpec.EXACTLY) {
            return specSize;
        }

        int result = ARROW_BITMAP_SIZE + getPaddingLeft() + getPaddingRight();

        if (specMode == MeasureSpec.AT_MOST) {
            result = Math.min(result, specSize);
        }

        return result;
    }

    private int measureHeight(int measureSpec) {
        final int specMode = MeasureSpec.getMode(measureSpec);
        final int specSize = MeasureSpec.getSize(measureSpec);

        if (specMode == MeasureSpec.EXACTLY) {
            return specSize;
        }

        int result = ARROW_BITMAP_SIZE + getPaddingTop() + getPaddingBottom();

        if (specMode == MeasureSpec.AT_MOST) {
            result = Math.min(result, specSize);
        }

        return result;
    }
}
@@ -1470,125 +1470,8 @@
 G);%0A
-    private static final PaintFlagsDrawFilter FILTER_REMOVE = new PaintFlagsDrawFilter(Paint.FILTER_BITMAP_FLAG, 0);%0A
     
@@ -4266,51 +4266,8 @@
 ins%0A
-        canvas.setDrawFilter(FILTER_SET);%0A%0A
     
@@ -4308,24 +4308,24 @@
 idth() / 2;%0A
+
         fina
@@ -4496,32 +4496,74 @@
 owHeight) / 2;%0A%0A
+        canvas.setDrawFilter(FILTER_SET);%0A
         canvas.r
@@ -4617,16 +4617,16 @@
 nterY);%0A
+
         
@@ -4691,124 +4691,8 @@
 l);%0A
-        canvas.rotate(azimuthRelative, canvasCenterX, canvasCenterY);%0A%0A        canvas.setDrawFilter(FILTER_REMOVE);%0A
     

remove unused drawing code