package com.alexvasilkov.gestures.commons.circle;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Shader.TileMode;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.ImageView;

import androidx.annotation.Nullable;

public class CircleImageView extends ImageView {

    private static final int DEFAULT_PAINT_FLAGS = Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG;
    private static final Matrix tmpMatrix = new Matrix();

    private Paint bitmapPaint;
    private RectF rect;
    private boolean isCircle;

    public CircleImageView(Context context) {
        this(context, null, 0);
    }

    public CircleImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CircleImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    private void init() {
        if (bitmapPaint == null) {
            bitmapPaint = new Paint(DEFAULT_PAINT_FLAGS);
            rect = new RectF();
            isCircle = true;
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int specWidth = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
        int specHeight = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
        super.onMeasure(specWidth, specHeight);
    }

    @Override
    protected boolean setFrame(int left, int top, int right, int bottom) {
        boolean changed = super.setFrame(left, top, right, bottom);
        rect.set(getPaddingLeft(), getPaddingTop(),
                getWidth() - getPaddingRight(), getHeight() - getPaddingBottom());
        setup();
        return changed;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        init();

        if (bitmapPaint.getShader() == null) {
            super.onDraw(canvas);
        } else {
            float rx = 0.5f * rect.width();
            float ry = 0.5f * rect.height();
            canvas.drawRoundRect(rect, rx, ry, bitmapPaint);
        }
    }

    @Override
    public void setImageResource(int resId) {
        super.setImageResource(resId);
        setup();
    }

    @Override
    public void setImageDrawable(Drawable drawable) {
        super.setImageDrawable(drawable);
        setup();
    }

    @Override
    public void setImageMatrix(Matrix matrix) {
        super.setImageMatrix(matrix);
        setup();
    }

    @SuppressWarnings("unused") // Public API
    public void setCircle(boolean isCircle) {
        this.isCircle = isCircle;
        setup();
    }

    private void setup() {
        init();

        Bitmap bitmap = isCircle ? getBitmapFromDrawable(getDrawable()) : null;

        if (bitmap != null) {
            BitmapShader bitmapShader = new BitmapShader(bitmap, TileMode.CLAMP, TileMode.CLAMP);
            tmpMatrix.set(getImageMatrix());
            tmpMatrix.postTranslate(getPaddingLeft(), getPaddingTop());
            bitmapShader.setLocalMatrix(tmpMatrix);
            bitmapPaint.setShader(bitmapShader);
        } else {
            bitmapPaint.setShader(null);
        }

        invalidate();
    }

    @Nullable
    protected Bitmap getBitmapFromDrawable(Drawable drawable) {
        if (drawable == null) {
            return null;
        } else if (drawable instanceof BitmapDrawable) {
            return ((BitmapDrawable) drawable).getBitmap();
        } else {
            throw new RuntimeException("For better performance only BitmapDrawables are supported,"
                    + " but you can override getBitmapFromDrawable() and build bitmap on your own");
        }
    }

}
