package allen.town.podcast.core.glide;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Shader;
import androidx.annotation.NonNull;
import com.bumptech.glide.Priority;
import com.bumptech.glide.load.DataSource;
import com.bumptech.glide.load.Options;
import com.bumptech.glide.load.data.DataFetcher;
import com.bumptech.glide.load.model.ModelLoader;
import com.bumptech.glide.load.model.ModelLoaderFactory;
import com.bumptech.glide.load.model.MultiModelLoaderFactory;
import com.bumptech.glide.signature.ObjectKey;
import allen.town.podcast.model.feed.Feed;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.Random;

public final class GenerativePlaceholderImageModelLoader implements ModelLoader<String, InputStream> {

    public static class Factory implements ModelLoaderFactory<String, InputStream> {
        @NonNull
        @Override
        public ModelLoader<String, InputStream> build(@NonNull MultiModelLoaderFactory unused) {
            return new GenerativePlaceholderImageModelLoader();
        }

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

    @Override
    public LoadData<InputStream> buildLoadData(@NonNull String model, int width, int height, @NonNull Options options) {
        return new LoadData<>(new ObjectKey(model), new EmbeddedImageFetcher(model, width, height));
    }

    @Override
    public boolean handles(@NonNull String model) {
        return model.startsWith(Feed.PREFIX_GENERATIVE_COVER);
    }

    static class EmbeddedImageFetcher implements DataFetcher<InputStream> {
        private static final int[] PALETTES = {0xff085088, 0xffff3d00, 0xff1DE9B6,
                0xff9575CD, 0xff00C853, 0xffff1744, 0xff2979ff};
        private final String model;
        private final int width;
        private final int height;

        public EmbeddedImageFetcher(String model, int width, int height) {
            this.model = model;
            this.width = width;
            this.height = height;
        }

        @Override
        public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) {
            final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
            final Canvas canvas = new Canvas(bitmap);
            final Random generator = new Random(model.hashCode());
            final int lineGridSteps = 4 + generator.nextInt(4);
            final int slope = width / 4;
            final float shadowWidth = width * 0.0001f;
            final float lineDistance = ((float) width / (lineGridSteps - 2));
            final int baseColor = PALETTES[generator.nextInt(PALETTES.length)];

            Paint paint = new Paint();
            int color = randomShadeOfGrey(generator);
            paint.setColor(color);
            paint.setStrokeWidth(lineDistance);
            paint.setColorFilter(new PorterDuffColorFilter(baseColor, PorterDuff.Mode.MULTIPLY));
            Paint paintShadow = new Paint();
            paintShadow.setColor(0xff000000);
            paintShadow.setStrokeWidth(lineDistance);

            int forcedColorChange = 1 + generator.nextInt(lineGridSteps - 2);
            for (int i = lineGridSteps - 1; i >= 0; i--) {
                float linePos = (i - 0.5f) * lineDistance;
                boolean switchColor = generator.nextFloat() < 0.3f || i == forcedColorChange;
                if (switchColor) {
                    int newColor = color;
                    while (newColor == color) {
                        newColor = randomShadeOfGrey(generator);
                    }
                    color = newColor;
                    paint.setColor(newColor);
                    canvas.drawLine(linePos + slope + shadowWidth, -slope,
                            linePos - slope + shadowWidth, height + slope, paintShadow);
                }
                canvas.drawLine(linePos + slope, -slope,
                        linePos - slope, height + slope, paint);
            }

            Paint gradientPaint = new Paint();
            paint.setDither(true);
            gradientPaint.setShader(new LinearGradient(0, 0, 0, height, 0x00000000, 0x55000000, Shader.TileMode.CLAMP));
            canvas.drawRect(0, 0, width, height, gradientPaint);

            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
            InputStream is = new ByteArrayInputStream(baos.toByteArray());
            callback.onDataReady(is);
        }

        private static int randomShadeOfGrey(Random generator) {
            return 0xff777777 + 0x222222 * generator.nextInt(5);
        }

        @Override
        public void cleanup() {
            // nothing to clean up
        }

        @Override
        public void cancel() {
            // cannot cancel
        }

        @NonNull
        @Override
        public Class<InputStream> getDataClass() {
            return InputStream.class;
        }

        @NonNull
        @Override
        public DataSource getDataSource() {
            return DataSource.LOCAL;
        }
    }
}
