/**
 * Copyright 2011 The PlayN Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package playn.android;

import static playn.core.PlayN.log;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpGet;

import playn.core.AbstractAssetManager;
import playn.core.Image;
import playn.core.ResourceCallback;
import playn.core.Sound;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.util.Log;

public class AndroidAssetManager extends AbstractAssetManager {

  public String pathPrefix = null;
  public AssetManager assets;

  public void setPathPrefix(String prefix) {
    pathPrefix = prefix;
  }

  /**
   * Attempts to open the asset with the given name, throwing an
   * {@link IOException} in case of failure.
   */
  private InputStream openAsset(String path) throws IOException {
    // Insert a slash to make this consistent with the Java asset manager
    InputStream is = getClass().getClassLoader().getResourceAsStream(pathPrefix + "/" + path);
    if (is == null)
      throw new IOException("Unable to loader resource: " + path);
    return is;
  }

  @Override
  protected Image doGetImage(String path) {
    return new AndroidImage(AndroidPlatform.instance.graphics().ctx, doGetBitmap(path));
  }

  /**
   * Decodes a resource to a bitmap. Always succeeds, returning an error
   * placeholder if something goes wrong.
   */
  Bitmap doGetBitmap(String path) {
    try {
      InputStream is = openAsset(path);
      try {
        Bitmap bitmap = decodeBitmap(is);
        return bitmap;
      } finally {
        is.close();
      }
    } catch (IOException e) {
      return createErrorBitmap(e);
    }
  }

  private Bitmap decodeBitmap(InputStream is) {
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inDither = true;
    // Prefer the bitmap config we computed from the window parameter
    options.inPreferredConfig = AndroidPlatform.instance.graphics().preferredBitmapConfig;
    // Never scale bitmaps based on device parameters
    options.inScaled = false;
    return BitmapFactory.decodeStream(is, null, options);
  }

  private Bitmap createErrorBitmap(Exception e) {
    int height = 100, width = 100;

    Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_4444);

    android.graphics.Canvas c = new android.graphics.Canvas(bitmap);
    android.graphics.Paint p = new android.graphics.Paint();
    p.setColor(android.graphics.Color.RED);
    for (int yy = 0; yy <= height / 15; yy++) {
      for (int xx = 0; xx <= width / 45; xx++) {
        c.drawText("ERROR", xx * 45, yy * 15, p);
      }
    }

    return bitmap;
  }

  private class ErrorSound implements Sound {
    private final String path;

    public ErrorSound(String path) {
      this.path = path;
    }

    @Override
    public boolean play() {
      log().error("Attempted to play sound that was unable to load: " + path);
      return false;
    }

    @Override
    public void stop() {
    }

    @Override
    public void setLooping(boolean looping) {
    }

    @Override
    public void setVolume(float volume) {
    }

    @Override
    public boolean isPlaying() {
      return false;
    }
  }

  // FIXME: This should be much more intelligent than appending .wav to the path.
  // The current AndroidSound implementation using MediaPlayer (AndroidCompressedSound)
  // can already read many more formats than just .wav
  @Override
  protected Sound doGetSound(String path) {
    try {
      InputStream in = openAsset(path + ".wav");
      return AndroidPlatform.instance.audio().getSound(path + ".wav", in);
    } catch (IOException e) {
      log().error("Unable to load sound: " + path, e);
      return new ErrorSound(path);
    }
  }

  @Override
  protected void doGetText(final String path, final ResourceCallback<String> callback) {
    try {
      InputStream is = openAsset(path);
      try {
        StringBuilder fileData = new StringBuilder(1000);
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        char[] buf = new char[1024];
        int numRead = 0;
        while ((numRead = reader.read(buf)) != -1) {
          String readData = String.valueOf(buf, 0, numRead);
          fileData.append(readData);
        }
        reader.close();
        String text = fileData.toString();
        callback.done(text);
      } finally {
        is.close();
      }
    } catch (IOException e) {
      callback.error(e);
    }
  }

  public abstract static class DownloaderTask<T> extends AsyncTask<String, Void, T> {
    private ResourceCallback<T> callback;

    public DownloaderTask() {
    }

    public DownloaderTask(ResourceCallback<T> callback) {
      this.callback = callback;
    }

    @Override
    // Actual download method, run in the task thread
    protected T doInBackground(String... params) {
      // params comes from the execute() call: params[0] is the url.
      return download(params[0]);
    }

    public abstract T download(String url);

    @Override
    protected void onPostExecute(T data) {
      if (callback != null) {
        callback.done(data);
      }
    }
  }

  // Taken from
  // http://android-developers.blogspot.com/2010/07/multithreading-for-performance.html
  public Bitmap downloadBitmap(String url) {
    final AndroidHttpClient client = AndroidHttpClient.newInstance("Android");
    final HttpGet getRequest = new HttpGet(url);

    try {
      HttpResponse response = client.execute(getRequest);
      final int statusCode = response.getStatusLine().getStatusCode();
      if (statusCode != HttpStatus.SC_OK) {
        Log.w("ImageDownloader", "Error " + statusCode + " while retrieving bitmap from " + url);
        return null;
      }

      final HttpEntity entity = response.getEntity();
      if (entity != null) {
        InputStream inputStream = null;
        try {
          inputStream = entity.getContent();
          return decodeBitmap(inputStream);
        } finally {
          if (inputStream != null) {
            inputStream.close();
          }
          entity.consumeContent();
        }
      }
    } catch (Exception e) {
      // Could provide a more explicit error message for IOException or
      // IllegalStateException
      getRequest.abort();
      Log.w("ImageDownloader", "Error while retrieving bitmap from " + url, e);
    } finally {
      if (client != null) {
        client.close();
      }
    }
    return null;
  }
}

@@ -3984,233 +3984,8 @@
  %7D%0A%0A
-  // FIXME: This should be much more intelligent than appending .wav to the path.%0A  // The current AndroidSound implementation using MediaPlayer (AndroidCompressedSound)%0A  // can already read many more formats than just .wav%0A
   @O
@@ -4088,19 +4088,19 @@
 ath + %22.
-wav
+mp3
 %22);%0A    
@@ -4163,11 +4163,11 @@
 + %22.
-wav
+mp3
 %22, i

Use .mp3 audio files like all the other platforms now do.