package com.bumptech.glide.load.model;

import android.content.ContentResolver;
import android.content.res.AssetManager;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.support.annotation.NonNull;
import com.bumptech.glide.load.Options;
import com.bumptech.glide.load.data.DataFetcher;
import com.bumptech.glide.load.data.FileDescriptorAssetPathFetcher;
import com.bumptech.glide.load.data.StreamAssetPathFetcher;
import com.bumptech.glide.signature.ObjectKey;
import java.io.InputStream;

/**
 * Loads a specific data type from an Asset Manager Uri.
 *
 * @param <Data> The type of data this loader will obtain.
 */
public class AssetUriLoader<Data> implements ModelLoader<Uri, Data> {
  private static final String ASSET_PATH_SEGMENT = "android_asset";
  private static final String ASSET_PREFIX =
      ContentResolver.SCHEME_FILE + ":///" + ASSET_PATH_SEGMENT + "/";
  private static final int ASSET_PREFIX_LENGTH = ASSET_PREFIX.length();

  private final AssetManager assetManager;
  private final AssetFetcherFactory<Data> factory;

  // Public API.
  @SuppressWarnings("WeakerAccess")
  public AssetUriLoader(AssetManager assetManager, AssetFetcherFactory<Data> factory) {
    this.assetManager = assetManager;
    this.factory = factory;
  }

  @Override
  public LoadData<Data> buildLoadData(@NonNull Uri model, int width, int height,
      @NonNull Options options) {
    String assetPath = model.toString().substring(ASSET_PREFIX_LENGTH);
    return new LoadData<>(new ObjectKey(model), factory.buildFetcher(assetManager, assetPath));
  }

  @Override
  public boolean handles(@NonNull Uri model) {
    return ContentResolver.SCHEME_FILE.equals(model.getScheme()) && !model.getPathSegments()
        .isEmpty() && ASSET_PATH_SEGMENT.equals(model.getPathSegments().get(0));
  }

  /**
   * A factory to build a {@link DataFetcher} for a specific asset path.
   *
   * @param <Data> The type of data that will be obtained by the fetcher.
   */
  public interface AssetFetcherFactory<Data> {
    DataFetcher<Data> buildFetcher(AssetManager assetManager, String assetPath);
  }

  /**
   * Factory for loading {@link InputStream}s from asset manager Uris.
   */
  public static class StreamFactory implements ModelLoaderFactory<Uri, InputStream>,
      AssetFetcherFactory<InputStream> {

    private final AssetManager assetManager;

    public StreamFactory(AssetManager assetManager) {
      this.assetManager = assetManager;
    }

    @NonNull
    @Override
    public ModelLoader<Uri, InputStream> build(MultiModelLoaderFactory multiFactory) {
      return new AssetUriLoader<>(assetManager, this);
    }

    @Override
    public void teardown() {
      // Do nothing.
    }

    @Override
    public DataFetcher<InputStream> buildFetcher(AssetManager assetManager, String assetPath) {
      return new StreamAssetPathFetcher(assetManager, assetPath);
    }
  }

  /**
   * Factory for loading {@link ParcelFileDescriptor}s from asset manager Uris.
   */
  public static class FileDescriptorFactory implements ModelLoaderFactory<Uri,
      ParcelFileDescriptor>,
      AssetFetcherFactory<ParcelFileDescriptor> {

    private final AssetManager assetManager;

    public FileDescriptorFactory(AssetManager assetManager) {
      this.assetManager = assetManager;
    }

    @NonNull
    @Override
    public ModelLoader<Uri, ParcelFileDescriptor> build(MultiModelLoaderFactory multiFactory) {
      return new AssetUriLoader<>(assetManager, this);
    }

    @Override
    public void teardown() {
      // Do nothing.
    }

    @Override
    public DataFetcher<ParcelFileDescriptor> buildFetcher(AssetManager assetManager,
        String assetPath) {
      return new FileDescriptorAssetPathFetcher(assetManager, assetPath);
    }
  }
}
