package similar.io.view;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.BitmapFactory;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.CornerPathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Shader;
import android.support.annotation.IdRes;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.animation.LinearInterpolator;

public class SimilarLoadingView extends View {


    private static final int MAX_PROGRESS_VALUE = 1450;
    private static final int PROGRESS_TIME = 2000;
    private static final int MAX_ALPHA = 70;

    private Paint paint = new Paint();
    private double hexHeight;
    private double hexWidth;
    private double hexPadding = 0;
    private float actualProgress = 0;
    private int maxAlpha = MAX_ALPHA;
    @IdRes
    private int loadingDrawable;
    private int animationTime = PROGRESS_TIME;
    private int color;
    private int cornerRadius;

    private AnimatorSet indeterminateAnimator;

    public SimilarLoadingView(Context context) {
        super(context);
    }

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

    public SimilarLoadingView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initAttributes(attrs, defStyle);
        initPaint();
    }

    private void initAttributes(AttributeSet attrs, int defStyle) {
        final TypedArray a = getContext().obtainStyledAttributes(attrs,
                R.styleable.SimilarLoadingView,
                defStyle, 0);
        animationTime = a.getInteger(R.styleable.SimilarLoadingView_similar_animDuration, PROGRESS_TIME);
        maxAlpha = a.getInteger(R.styleable.SimilarLoadingView_similar_maxAlpha, MAX_ALPHA);
        color = a.getColor(R.styleable.SimilarLoadingView_similar_color, Color.BLACK);
        cornerRadius = a.getInteger(R.styleable.SimilarLoadingView_similar_cornerRadius, 0);
        loadingDrawable = a.getResourceId(R.styleable.SimilarLoadingView_similar_loadingDrawable, -1);
        a.recycle();
    }

    private void initPaint() {
        paint.setAlpha(0);
        paint.setPathEffect(new CornerPathEffect(cornerRadius));
        paint.setColor(color);
        paint.setStyle(Paint.Style.FILL);
        paint.setAntiAlias(true);
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        startAnimation();
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        stopAnimation();
    }

    @Override
    public void setVisibility(int visibility) {
        int currentVisibility = getVisibility();
        super.setVisibility(visibility);
        if (visibility != currentVisibility) {
            if (visibility == View.VISIBLE) {
                resetAnimator();
            } else if (visibility == View.GONE || visibility == View.INVISIBLE) {
                stopAnimation();
            }
        }
    }

    private void startAnimation() {
        resetAnimator();
    }

    private void stopAnimation() {
        actualProgress = 0;
        if (indeterminateAnimator != null) {
            indeterminateAnimator.cancel();
            indeterminateAnimator = null;
        }
    }

    private void resetAnimator() {
        if (indeterminateAnimator != null && indeterminateAnimator.isRunning()) {
            indeterminateAnimator.cancel();
        }
        ValueAnimator progressAnimator = ValueAnimator.ofFloat(0, MAX_PROGRESS_VALUE);
        progressAnimator.setDuration(animationTime);
        progressAnimator.setInterpolator(new LinearInterpolator());
        progressAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                actualProgress = (Float) animation.getAnimatedValue();
                invalidate();
            }
        });
        indeterminateAnimator = new AnimatorSet();
        indeterminateAnimator.play(progressAnimator);
        indeterminateAnimator.addListener(new AnimatorListenerAdapter() {
            boolean wasCancelled = false;

            @Override
            public void onAnimationCancel(Animator animation) {
                wasCancelled = true;
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                if (!wasCancelled) {
                    resetAnimator();
                }
            }
        });
        indeterminateAnimator.start();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        double viewWidth = MeasureSpec.getSize(widthMeasureSpec);
        double viewHeight = MeasureSpec.getSize(heightMeasureSpec);
        double widthC = convertDpToPixel(200, getRootView().getContext()) / viewWidth;
        double heightC = convertDpToPixel(220, getRootView().getContext()) / viewHeight;
        hexWidth = (viewWidth / 4.125 * widthC);
        hexHeight = (viewHeight / 3.5 * heightC);
        hexPadding = viewHeight / 23;
        setMeasuredDimension((int) (viewWidth / 1.385 * widthC), (int) (viewHeight * heightC));
    }

    @SuppressLint("DrawAllocation")
    @Override
    protected void onDraw(Canvas canvas) {
        paint.setShader(new BitmapShader(BitmapFactory.decodeResource(getResources(), getHexagonImage(1)), Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));

        //1
        Path hexPath = hiveRect(hexWidth / 2, hexPadding, hexWidth * 3 / 2, hexHeight + hexPadding);
        paint.setAlpha(getAlpha(1, actualProgress));
        canvas.drawPath(hexPath, paint);
        //2
        hexPath = hiveRect(hexWidth * 3 / 2, hexPadding, hexWidth * 5 / 2, hexHeight + hexPadding);
        paint.setAlpha(getAlpha(2, actualProgress));
        canvas.drawPath(hexPath, paint);
        //6
        hexPath = hiveRect(0, hexHeight * 3 / 4 + hexPadding, hexWidth,
                hexHeight * 7 / 4 + hexPadding);
        paint.setAlpha(getAlpha(6, actualProgress));
        canvas.drawPath(hexPath, paint);
        //7
        hexPath = hiveRect(hexWidth, hexHeight * 3 / 4 + hexPadding, hexWidth * 2,
                hexHeight * 7 / 4 + hexPadding);
        paint.setAlpha(getAlpha(7, actualProgress));
        canvas.drawPath(hexPath, paint);
        //3
        hexPath = hiveRect(hexWidth * 2, hexHeight * 3 / 4 + hexPadding, hexWidth * 3,
                hexHeight * 7 / 4 + hexPadding);
        paint.setAlpha(getAlpha(3, actualProgress));
        canvas.drawPath(hexPath, paint);
        //5
        hexPath = hiveRect(hexWidth / 2, hexHeight * 6 / 4 + hexPadding, hexWidth * 3 / 2,
                hexHeight * 10 / 4 + hexPadding);
        paint.setAlpha(getAlpha(5, actualProgress));
        canvas.drawPath(hexPath, paint);
        //4
        hexPath = hiveRect(hexWidth * 3 / 2, hexHeight * 6 / 4 + hexPadding, hexWidth * 5 / 2,
                hexHeight * 10 / 4 + hexPadding);
        paint.setAlpha(getAlpha(4, actualProgress));
        canvas.drawPath(hexPath, paint);
    }

    private int getHexagonImage(int position) {
        if (position <= 1) {
            return loadingDrawable;
        } else {
            return color;
        }
    }

    private int getAlpha(int num, float progress) {
        float alpha;
        if (progress > num * 100) {
            alpha = maxAlpha;
        } else {
            int min = (num - 1) * 100;
            alpha = (progress - min) > 0 ? progress - min : 0;
            alpha = alpha * maxAlpha / 100;
        }
        if (progress > 700) {
            float fadeProgress = progress - 700;
            if (fadeProgress > num * 100) {
                alpha = 0;
            } else {
                int min = (num - 1) * 100;
                alpha = (fadeProgress - min) > 0 ? fadeProgress - min : 0;
                alpha = maxAlpha - alpha * maxAlpha / 100;
            }
        }
        if (progress > 1400) {
            alpha = 0;
        }
        return (int) alpha;
    }

    private Path hiveRect(double left, double top, double right, double bottom) {
        Path path = new Path();
        double height = Math.abs(bottom - top);
        double width = Math.abs(right - left);
        double r = width > height ? height : width;
        r = r / 2;
        float x = (float) ((right - left) / 2 + left);
        float y = (float) top;
        int edge = (int) (r * Math.sqrt(3) / 2);
        path.moveTo(x, y);
        x = x + edge;
        y = (float) (y + r / 2);
        path.lineTo(x, y);
        y = (float) (y + r);
        path.lineTo(x, y);
        x = x - edge;
        y = (float) (y + r / 2);
        path.lineTo(x, y);
        x = x - edge;
        y = (float) (y - r / 2);
        path.lineTo(x, y);
        y = (float) (y - r);
        path.lineTo(x, y);
        path.close();
        return path;
    }

    public static float convertDpToPixel(float dp, Context context) {
        Resources resources = context.getResources();
        DisplayMetrics metrics = resources.getDisplayMetrics();
        float px = dp * ((float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT);
        return px;
    }
}