// Created by plusminus on 21:31:36 - 25.09.2008
package org.andnav.osm.services.util;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.concurrent.Executors;

import org.andnav.osm.services.IOpenStreetMapTileProviderCallback;
import org.andnav.osm.services.util.constants.OpenStreetMapServiceConstants;
import org.andnav.osm.views.util.OpenStreetMapRendererInfo;

import android.content.Context;
import android.graphics.BitmapFactory;
import android.os.RemoteException;
import android.util.Log;

/**
 * The OpenStreetMapTileDownloader loads tiles from a server and passes them to
 * a OpenStreetMapTileFilesystemProvider.
 * @author Nicolas Gramlich
 * @author Manuel Stahl
 *
 */
public class OpenStreetMapTileDownloader extends OpenStreetMapAsyncTileProvider implements OpenStreetMapServiceConstants {
	// ===========================================================
	// Constants
	// ===========================================================

	private static final String DEBUGTAG = "OSM_DOWNLOADER";

	// ===========================================================
	// Fields
	// ===========================================================

	protected final Context mCtx;
	protected final OpenStreetMapTileFilesystemProvider mMapTileFSProvider;

	// ===========================================================
	// Constructors
	// ===========================================================

	public OpenStreetMapTileDownloader(final Context ctx, final OpenStreetMapTileFilesystemProvider aMapTileFSProvider){
		this.mCtx = ctx;
		this.mMapTileFSProvider = aMapTileFSProvider;
		mThreadPool = Executors.newFixedThreadPool(4);
	}

	// ===========================================================
	// Getter & Setter
	// ===========================================================

	// ===========================================================
	// Methods from SuperClass/Interfaces
	// ===========================================================

	@Override
	protected Runnable getTileLoader(OpenStreetMapTile aTile, IOpenStreetMapTileProviderCallback aCallback) {
		return new TileLoader(aTile, aCallback);
	};
	
	// ===========================================================
	// Methods
	// ===========================================================

	private String buildURL(final OpenStreetMapTile tile) {
		OpenStreetMapRendererInfo renderer = OpenStreetMapRendererInfo.values()[tile.rendererID];
		return renderer.getTileURLString(tile);
	}

	// ===========================================================
	// Inner and Anonymous Classes
	// ===========================================================
	
	private class TileLoader extends OpenStreetMapAsyncTileProvider.TileLoader {

		public TileLoader(final OpenStreetMapTile aTile, final IOpenStreetMapTileProviderCallback aCallback) {
			super(aTile, aCallback);
		}

		@Override
		public void run() {
			InputStream in = null;
			OutputStream out = null;

			String tileURLString = buildURL(mTile);
			
			try {
				if(DEBUGMODE)
					Log.d(DEBUGTAG, "Downloading Maptile from url: " + tileURLString);

				in = new BufferedInputStream(new URL(tileURLString).openStream(), StreamUtils.IO_BUFFER_SIZE);

				final ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
				out = new BufferedOutputStream(dataStream, StreamUtils.IO_BUFFER_SIZE);
				StreamUtils.copy(in, out);
				out.flush();

				final byte[] data = dataStream.toByteArray();

				OpenStreetMapTileDownloader.this.mMapTileFSProvider.saveFile(mTile, data);
				if(DEBUGMODE)
					Log.d(DEBUGTAG, "Maptile saved: " + mTile);

				mCallback.mapTileLoaded(mTile.rendererID, mTile.zoomLevel, mTile.x, mTile.y, BitmapFactory.decodeByteArray(data, 0, data.length));
			} catch (IOException e) {
				try {
					mCallback.mapTileFailed(mTile.rendererID, mTile.zoomLevel, mTile.x, mTile.y);
				} catch (RemoteException e1) {
					Log.e(DEBUGTAG, "Service failed", e1);
				}
				if(DEBUGMODE)
					Log.e(DEBUGTAG, "Error Downloading MapTile: " + mTile + " : " + e);
				/* TODO What to do when downloading tile caused an error?
				 * Also remove it from the mPending?
				 * Doing not blocks it for the whole existence of this TileDownloder.
				 * -> we remove it and the application has to re-request it.
				 */
			} catch (RemoteException re) {
				Log.e(DEBUGTAG, "Service failed", re);
			} catch(final Throwable t) {
				Log.e(DEBUGTAG, "Error Downloading MapTile: " + mTile + " : " + t);
			} finally {
				StreamUtils.closeStream(in);
				StreamUtils.closeStream(out);
				finished();
			}
		}
	};

}

@@ -592,16 +592,49 @@
 ntext;%0D%0A
+import android.graphics.Bitmap;%0D%0A
 import a
@@ -3883,24 +3883,342 @@
  mTile);%0D%0A%0D%0A
+%09%09%09%09Bitmap bitmap = null;%0D%0A%09%09%09%09try %7B%0D%0A%09%09%09%09%09bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);%0D%0A%09%09%09%09%7Dcatch(OutOfMemoryError e) %7B%0D%0A%09%09%09%09%09// it's downloaded, so success, but we just can't return it immediately%0D%0A%09%09%09%09%09Log.e(DEBUGTAG, %22OutOfMemoryError creating bitmap from downloaded MapTile: %22 + mTile);%0D%0A%09%09%09%09%7D%0D%0A
 %09%09%09%09mCallbac
@@ -4290,59 +4290,14 @@
 .y, 
-B
+b
 itmap
-Factory.decodeByteArray(data, 0, data.length)
 );%0D%0A
@@ -4548,33 +4548,33 @@
 EBUGTAG, %22Error 
-D
+d
 ownloading MapTi
@@ -4606,263 +4606,8 @@
 );%0D%0A
-%09%09%09%09/* TODO What to do when downloading tile caused an error?%0D%0A%09%09%09%09 * Also remove it from the mPending?%0D%0A%09%09%09%09 * Doing not blocks it for the whole existence of this TileDownloder.%0D%0A%09%09%09%09 * -%3E we remove it and the application has to re-request it.%0D%0A%09%09%09%09 */%0D%0A
 %09%09%09%7D
@@ -4672,17 +4672,47 @@
 e failed
-%22
+ downloading MapTile: %22 + mTile
 , re);%0D%0A
@@ -4740,24 +4740,57 @@
 wable t) %7B%0D%0A
+%09%09%09%09// don't expect to get this%0D%0A
 %09%09%09%09Log.e(DE
@@ -4804,17 +4804,17 @@
  %22Error 
-D
+d
 ownloadi
@@ -4834,26 +4834,17 @@
  + mTile
- + %22 : %22 +
+,
  t);%0D%0A%09%09

Handle download errors better