package com.example.bugstick;

import android.animation.TimeAnimator;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathEffect;
import android.graphics.PathMeasure;
import android.graphics.PointF;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;

/**
 * A simple toy view for demo purposes.
 * Displays a circle and a trail that can be controlled via {@link #setVelocity(float, float)}.
 */
public class BugView extends View implements TimeAnimator.TimeListener {

    private static final float BUG_RADIUS_DP = 4f;
    private static final float BUG_TRAIL_DP = 200f;

    private Paint paint;
    private TimeAnimator animator;

    private float density;
    private int width, height;
    private PointF position;
    private PointF velocity;
    private Path path;
    private PathMeasure pathMeasure;

    public BugView(Context context) {
        super(context);
        init(context);
    }

    public BugView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public BugView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public BugView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context);
    }

    private void init(Context context) {
        animator = new TimeAnimator();
        animator.setTimeListener(this);

        paint = new Paint();
        paint.setColor(Color.WHITE);

        density = getResources().getDisplayMetrics().density;

        path = new Path();
        pathMeasure = new PathMeasure();
        position = new PointF();
        velocity = new PointF();
    }

    /**
     * Start applying velocity as soon as view is on-screen.
     */
    @Override
    public void onAttachedToWindow() {
        super.onAttachedToWindow();
        animator.start();
    }

    /**
     * Stop animations when the view hierarchy is torn down.
     */
    @Override
    public void onDetachedFromWindow() {
        animator.cancel();
        super.onDetachedFromWindow();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        width = w;
        height = h;

        position.set(width / 2, height / 2);
        path.rewind();
        path.moveTo(position.x, position.y);
    }

    /**
     * Set bug velocity in dips.
     */
    public void setVelocity(float vxDps, float vyDps) {
        velocity.set(vxDps * density, vyDps * density);
    }

    @Override
    public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
        final float dt = deltaTime / 1000f; // seconds

        position.x += velocity.x * dt;
        position.y += velocity.y * dt;

        bound();

        path.lineTo(position.x, position.y);

        invalidate();
    }

    /**
     * Bound position and reflect velocity.
     */
    private void reflect() {
        boolean flipX = false, flipY = false;
        if (position.x > width) {
            position.x = position.x - 2 * (position.x - width);
            flipX = true;
        } else if (position.x < 0) {
            position.x = -position.x;
            flipX = true;
        }
        if (position.y > height) {
            position.y = position.y - 2 * (position.y - height);
            flipY = true;
        } else if (position.y < 0) {
            position.y = -position.y;
            flipY = true;
        }
        if (flipX) velocity.x *= -1;
        if (flipY) velocity.y *= -1;
    }

    /**
     * Bound position.
     */
    private void bound() {
        if (position.x > width) {
            position.x = width;
        } else if (position.x < 0) {
            position.x = 0;
        }
        if (position.y > height) {
            position.y = height;
        } else if (position.y < 0) {
            position.y = 0;
        }
    }

    @Override
    public void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        canvas.drawColor(Color.BLACK);

        pathMeasure.setPath(path, false);
        float length = pathMeasure.getLength();

        if (length > BUG_TRAIL_DP * density) {
            // Note - this is likely a poor way to accomplish the result. Just for demo purposes.
            @SuppressLint("DrawAllocation")
            PathEffect effect = new DashPathEffect(new float[]{length, length}, -length + BUG_TRAIL_DP * density);
            paint.setPathEffect(effect);
        }

        paint.setStyle(Paint.Style.STROKE);
        canvas.drawPath(path, paint);

        paint.setStyle(Paint.Style.FILL);
        canvas.drawCircle(position.x, position.y, BUG_RADIUS_DP * density, paint);
    }
}