package com.mobify.astro.utilities;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.content.res.AppCompatResources;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;

public class DrawableUriResolver {
    Context context;

    public interface DrawableRunnable {
        void run(@Nullable Drawable drawable, @Nullable Exception e);
    }

    static class DrawableUriResolverException extends Exception {
        DrawableUriResolverException(Uri uri) {
            super("Failed to download and decode image. Requested: " + uri);
        }
    }

    private class DrawableUriResolverTask extends AsyncTask<Uri, Void, Drawable> {
        DrawableRunnable onComplete;
        DrawableUriResolverException exception;

        public DrawableUriResolverTask(@NonNull DrawableRunnable onComplete) {
            this.onComplete = onComplete;
        }

        protected Drawable doInBackground(Uri... uris) {
            Uri uri = uris[0];

            try {
                if (AstroFileUtilities.isFileUri(uri)) {
                    return getLocalDrawable(uri);
                }
                else {
                    return getNetworkDrawable(uri);
                }
            } catch(Exception e) {
                DrawableUriResolverException drawableUriResolverException = new DrawableUriResolverException(uri);
                drawableUriResolverException.initCause(e);
                exception = drawableUriResolverException;
            }
            return null;
        }

        protected void onPostExecute(Drawable drawable) {
            if (drawable != null) {
                onComplete.run(drawable, null);
                return;
            }

            if (exception != null) {
                onComplete.run(null, exception);
            }
        }
    }

    public DrawableUriResolver(Context context) {
        this.context = context;
    }

    public Drawable getLocalDrawable(Uri uri) throws Exception {
        InputStream drawableStream;
        Drawable drawable;

        if (!AstroFileUtilities.isFileUri(uri)) {
            throw new Exceptions.UnsupportedUriSchemeException(uri);
        }

        // For Astro Uri we search in Android Resources first and then search in Android assets if
        // not found in resources.
        try {
            int resourceId = AstroFileUtilities.getAndroidResourceDrawableIdentifier(context, uri);
            drawable = AppCompatResources.getDrawable(context, resourceId);
        } catch (Resources.NotFoundException resourceException) {
            try {
                drawableStream = getStreamFromAssets(uri);
            } catch (IOException assetException) {
                Exception e = new FileNotFoundException();
                e.initCause(resourceException);
                throw e;
            }
            try {
                drawable = Drawable.createFromResourceStream(context.getResources(), null, drawableStream, null);
            } finally {
                drawableStream.close();
            }
        }

        return drawable;
    }

    public Drawable getNetworkDrawable(Uri uri) throws Exception {
        URL url = new URL(uri.toString());

        URLConnection conn = url.openConnection();
        conn.connect();

        InputStream drawableStream = conn.getInputStream();
        try {
            return Drawable.createFromResourceStream(context.getResources(), null, drawableStream, null);
        } finally {
            drawableStream.close();
        }
    }

    public void getDrawable(Uri uri, DrawableRunnable onComplete) {
        DrawableUriResolverTask task = new DrawableUriResolverTask(onComplete);
        task.execute(uri);
    }

    protected InputStream getStreamFromResources(Uri uri) throws URISyntaxException, FileNotFoundException {
        return context.getContentResolver().openInputStream(
                AstroFileUtilities.getAndroidResourceDrawableUri(uri, context.getPackageName())
        );
    }

    protected InputStream getStreamFromAssets(Uri uri) throws URISyntaxException, IOException {
        return context.getAssets().open(AstroFileUtilities.getAndroidAssetPath(uri));
    }
}
