/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.mozilla.mozstumbler.client.mapview.tiles;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.WindowManager;

import org.mozilla.osmdroid.DefaultResourceProxyImpl;
import org.mozilla.osmdroid.tileprovider.BetterTileProvider;
import org.mozilla.osmdroid.tileprovider.MapTile;
import org.mozilla.osmdroid.tileprovider.ReusableBitmapDrawable;
import org.mozilla.osmdroid.util.TileLooper;
import org.mozilla.osmdroid.views.Projection;
import org.mozilla.osmdroid.views.overlay.TilesOverlay;

import java.util.HashSet;
import java.util.Set;

public abstract class AbstractMapOverlay extends TilesOverlay {
    // We want the map to zoom to level 20, even if tiles have less zoom available
    public static final int MAX_ZOOM_LEVEL_OF_MAP = 20;
    private static int sMinZoomLevelOfMapDisplaySizeBased;

    public static final int TILE_PIXEL_SIZE = 256;
    // Use png32 which is a 32-color indexed image, the tiles are ~30% smaller
    public static String FILE_TYPE_SUFFIX_PNG = ".png32";
    private final Rect mTileRect = new Rect();
    private final Point mTilePoint = new Point();
    private final Point mTilePointMercator = new Point();
    private final Set<MapTile> mDrawnSet = new HashSet<MapTile>();
    private Projection mProjection;

    // TODO make this a single value configurable in deveoper settings
    private static final int SMALL_SCREEN_MIN_ZOOM = 11;
    private static final int LARGE_SCREEN_MIN_ZOOM = 13;


    public enum LowResType {
        HIGHER_ZOOM, LOWER_ZOOM
    }

    public AbstractMapOverlay(final Context context) {
        super(new BetterTileProvider(context), new DefaultResourceProxyImpl(context));
    }

    public static void setDisplayBasedMinimumZoomLevel(Context c) {
        WindowManager wm = (WindowManager) c.getSystemService(Context.WINDOW_SERVICE);
        Display display = wm.getDefaultDisplay();
        DisplayMetrics outMetrics = new DisplayMetrics();
        display.getMetrics(outMetrics);

        sMinZoomLevelOfMapDisplaySizeBased = (outMetrics.widthPixels < 500)? SMALL_SCREEN_MIN_ZOOM : LARGE_SCREEN_MIN_ZOOM;
    }

    public static int getDisplaySizeBasedMinZoomLevel() {
        return sMinZoomLevelOfMapDisplaySizeBased;
    }

    // Though the tile provider can only provide up to 13, this overlay will display higher.
    @Override
    public int getMaximumZoomLevel() {
        return MAX_ZOOM_LEVEL_OF_MAP;
    }

    @Override
    public void drawTiles(final Canvas c, final Projection projection, final int zoomLevel,
                          final int tileSizePx, final Rect viewPort) {
        if (zoomLevel <= mTileProvider.getMaximumZoomLevel()) {
            super.drawTiles(c, projection, zoomLevel, tileSizePx, viewPort);
        } else {
            mProjection = projection;
            mDrawnSet.clear();
            mCoverageTileLooper.loop(c, zoomLevel, tileSizePx, viewPort);
        }
    }

    protected final TileLooper mCoverageTileLooper = new TileLooper() {
        @Override
        public void initialiseLoop(int pZoomLevel, int pTileSizePx) {
            // make sure the cache is big enough for all the tiles
            final int numNeeded = (mLowerRight.y - mUpperLeft.y + 1) * (mLowerRight.x - mUpperLeft.x + 1);
            mTileProvider.ensureCapacity(numNeeded + OVERSHOOT_TILE_CACHE_SIZE);
        }

        @Override
        public void finaliseLoop() {}

        @Override
        public void handleTile(Canvas pCanvas, int pTileSizePx, MapTile pTile, int pX, int pY) {
            int zoomLevel = Math.min(pTile.getZoomLevel(), mTileProvider.getMaximumZoomLevel());
            final int zoomDifference = pTile.getZoomLevel() - zoomLevel;
            final int scaleDiff = 1 << zoomDifference;

            pTileSizePx *= scaleDiff;
            pX /= scaleDiff;
            pY /= scaleDiff;

            final MapTile tile = new MapTile(zoomLevel, pTile.getX() >> zoomDifference, pTile.getY() >> zoomDifference);
            if (mDrawnSet.contains(tile)) {
                return;
            }
            mDrawnSet.add(tile);

            Drawable currentMapTile = mTileProvider.getMapTile(tile);

            boolean isReusable = currentMapTile instanceof ReusableBitmapDrawable;
            final ReusableBitmapDrawable reusableBitmapDrawable =
                    isReusable ? (ReusableBitmapDrawable) currentMapTile : null;

            if (currentMapTile != null) {
                mTilePoint.set(pX * pTileSizePx, pY * pTileSizePx);
                mTileRect.set(mTilePoint.x, mTilePoint.y, mTilePoint.x + pTileSizePx, mTilePoint.y
                        + pTileSizePx);
                if (isReusable) {
                    reusableBitmapDrawable.beginUsingDrawable();
                }
                try {
                    onTileReadyToDraw2(pCanvas, currentMapTile, mTileRect);
                } finally {
                    if (isReusable)
                        reusableBitmapDrawable.finishUsingDrawable();
                }
            }
        }
    };

    private void onTileReadyToDraw2(final Canvas c, final Drawable currentMapTile,
                                     final Rect tileRect) {
        mProjection.toPixelsFromMercator(tileRect.left, tileRect.top, mTilePointMercator);
        tileRect.offsetTo(mTilePointMercator.x, mTilePointMercator.y);
        currentMapTile.setBounds(tileRect);
        currentMapTile.draw(c);
    }
}

@@ -1742,16 +1742,17 @@
  in deve
+l
 oper set
@@ -1814,16 +1814,74 @@
 M = 11;%0A
+    private static final int MEDIUM_SCREEN_MIN_ZOOM = 12;%0A
     priv
@@ -2464,106 +2464,311 @@
     
-sMinZoomLevelOfMapDisplaySizeBased = (outMetrics.widthPixels %3C 500)? SMALL_SCREEN_MIN_ZOOM : LARGE
+if (outMetrics.widthPixels %3E 700) %7B%0A            sMinZoomLevelOfMapDisplaySizeBased = LARGE_SCREEN_MIN_ZOOM;%0A        %7D else if (outMetrics.widthPixels %3C 500) %7B%0A            sMinZoomLevelOfMapDisplaySizeBased = SMALL_SCREEN_MIN_ZOOM;%0A        %7D else %7B%0A            sMinZoomLevelOfMapDisplaySizeBased = MEDIUM
 _SCR
@@ -2777,24 +2777,34 @@
 N_MIN_ZOOM;%0A
+        %7D%0A
     %7D%0A%0A    p

Add addition zoom for medium screen