package hk.hku.cs.srli.supermonkey;

import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.OvalShape;
import android.util.Log;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;

public class CalibrationView extends View implements ValueAnimator.AnimatorUpdateListener {
    
    private DotWrapper dot;
    
    private Animator.AnimatorListener listener;

    public CalibrationView(Context context) {
        super(context);
        // Enter low profile mode to avoid UI distraction.
        setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE);
        dot = new DotWrapper();
        // Put the dot outside the top left corner.
        dot.setX(0 - dot.DEFAULT_R);
        dot.setY(0 - dot.DEFAULT_R);
        dot.setR(dot.DEFAULT_R);
        listener = null;
    }
    
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        dot.setX(w / 2);
        dot.setY(h / 4);
    }

    public void setListener(Animator.AnimatorListener listener) {
        this.listener = listener;
    }
    
    public void movePointTo(float x, float y) {
        // Convert from normalized coordinates to position on screen.
        float destx = x * getWidth();
        float desty = y * getHeight();
        Log.v("CalibrationView", "movePointTo: " + destx + ", " + desty);
        startAnimation(animatedMoveTo(destx, desty), 1000);
    }
    
    public void shrinkPoint() {
        Log.v("CalibrationView", "shrinkPoint");
        startAnimation(animatedResize(5), 1000);
    }
    
    public void expandPoint() {
        Log.v("CalibrationView", "expandPoint");
        startAnimation(animatedResize(dot.DEFAULT_R), 1000);
    }
    
    public void hidePoint() {
        Log.v("CalibrationView", "hidePoint");
        // Remove the point from view.
        startAnimation(animatedResize(0), 500);
    }
    
    private Animator animatedMoveTo(float x, float y) {
        ObjectAnimator oax = ObjectAnimator.ofFloat(dot, "x", dot.x, x);
        ObjectAnimator oay = ObjectAnimator.ofFloat(dot, "y", dot.y, y);
        oax.addUpdateListener(this);
        AnimatorSet aset = new AnimatorSet();
        aset.playTogether(oax, oay);
        return aset;
    }
    
    private Animator animatedResize(float size) {
        ObjectAnimator oar = null;
        oar = ObjectAnimator.ofFloat(dot, "r", dot.r, size);
        // Accelerate when shrinks.
        if (dot.r > size) {
            oar.setInterpolator(new AccelerateInterpolator());
        } else {
            oar.setInterpolator(new DecelerateInterpolator());
        }
        oar.addUpdateListener(this);
        return oar;
    }
    
    private void startAnimation(Animator ani, long duration) {
        ani.setDuration(duration);
        ani.setStartDelay(500);
        if (listener != null) ani.addListener(listener);
        ani.start();
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        dot.draw(canvas);
    }

    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        invalidate();
    }
    
    private class DotWrapper {
        
        // Default radius is 20dp.
        public final float DEFAULT_R = 20 * getResources().getDisplayMetrics().density;
        
        private ShapeDrawable drawable;
        private Paint bgPaint;
        
        private float r, x, y;
        
        public DotWrapper() {
            this.r = DEFAULT_R;
            this.x = 0;
            this.y = 0;

            drawable = new ShapeDrawable(new OvalShape());
            drawable.getPaint().setColor(0xFF74AC23);
            
            bgPaint = new Paint();
            bgPaint.setColor(Color.BLACK);
        }
        
        public void setX(float x) {
            this.x = x;
        }
        
        public void setY(float y) {
            this.y = y;
        }
        
        public void setR(float r) {
            this.r = r;
        }
        
        public void draw(Canvas canvas) {
            updateDrawable();
            drawable.draw(canvas);
            
            // Draw a dot at the center of the shape.
            canvas.drawOval(new RectF(x - 2, y - 2, x + 2, y + 2), bgPaint);
        }
        
        private void updateDrawable() {
            int left = Math.round(x - r);
            int top = Math.round(y - r);
            int right = Math.round(x + r);
            int bottom = Math.round(y + r);
            drawable.setBounds(new Rect(left, top, right, bottom));
        }
    }
}