/*
 * Copyright (C) 2015 RECRUIT LIFESTYLE CO., LTD.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package jp.co.recruit_lifestyle.android.widget;

import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.graphics.Region;
import android.os.Handler;
import android.os.SystemClock;
import android.view.View;
import java.util.Random;

/**
 * @author amyu_san
 */
public class BeerView extends View {

  private final static int BOTTLE_COLOR = 0xff623B30;
  private final static int BEER_COLOR = 0xffEEBC54;
  private final static int GLASS_COLOR = 0xffffffff;
  private final static int BOTTLE_LABEL_INNER_CIRCLE_COLOR = 0xffDDCF87;
  private final static int BOTTLE_LABEL_OUTER_CIRCLE_COLOR = 0xffC49E34;
  private final static int BOTTLE_LABEL_RIBBON_COLOR = 0xff621819;
  private final static int BOTTLE_EMPTY_COLOR = 0xff703126;
  private final static int BOTTLE_BACKGROUND_COLOR = 0xffD6737B;
  private final static int GLASS_BACKGROUND_COLOR = 0xffA8D4DC;
  private final static float BOTTLE_POUR_HEIGHT_DX = 1.7f;
  private final static float GLASS_POUR_HEIGHT_DX = 3.43f;
  private final static int FROTH_NUM = 5;

  private Paint mBottlePaint;
  private Paint mBeerPaint;
  private Paint mBottleLabelOuterCirclePaint;
  private Paint mBottleLabelInnerCirclePaint;
  private Paint mBottleLabelRibbonPaint;
  private Paint mBottleBackgroundPaint;
  private Paint mBottleEmptyPaint;
  private Paint mGlassPaint;
  private Paint mGlassBackgroundPaint;
  private BottlePath mBottlePath;
  private Path mBottlePourPath;
  private GlassPath mGlassPath;
  private Path mGlassPourPath;
  private Path mFrothPath;
  private Handler mPourHandler;
  private float mBottlePourHeight;
  private float mGlassPourHeight;
  private int mViewWidth;
  private float mObjectWidth;
  private int mGlassHeight;
  private boolean mIsMax = false;
  private boolean mIsReverseAnimationRunning = false;
  private boolean mIsStartedPour = false;
  private float mBeerFrothRadius = 0.f;
  private float mCurrentDegree;
  private float mBottleCenterX;
  private float mBottleCenterY;
  private float mGlassCenterX;
  private float mGlassCenterY;
  private float mCircleWidth;
  private Region mGlassBeerRegion;
  private Region mClipRegion = new Region();
  private RectF mRectF = new RectF();
  private Random mRandom;
  private float[][] mFrothPoints;
  private long mLastInvalidate = SystemClock.uptimeMillis();
  private ValueAnimator mAlphaAnimator;
  private ValueAnimator[] mFrothAnimators = new ValueAnimator[FROTH_NUM];
  private Point mBottlePoints[] = new Point[Bottle.BOTTLE_POINTS.length * 2];
  private Point mBottleBeerPoints[] = new Point[Bottle.BEER_POINTS.length * 2];
  private Point mGlassPoints[] = new Point[Glass.GLASS_POINTS.length * 2];
  private Point mGlassBeerPoints[] = new Point[Glass.GLASS_BEER_POINTS.length * 2];
  private Point mGlassFrothPoints[] = new Point[Glass.GLASS_BEER_FROTH_POINTS.length];
  private Point mBottleLabelOuterCirclePoints[] =
      new Point[Bottle.LABEL_OUTER_CIRCLE_POINTS.length * 2];
  private Point mBottleLabelInnerCirclePoints[] =
      new Point[Bottle.LABEL_INNER_CIRCLE_POINTS.length * 2];
  private Point mBottleLabelRibbonPoints[] = new Point[Bottle.LABEL_RIBBON_POINTS.length * 2];
  private float mBottlePourHeightLimit;

  private Runnable mPourRunnable = new Runnable() {
    @Override public void run() {

      if (mBottlePourHeightLimit < mBottlePourHeight) {
        mIsMax = true;
        startReverseBottleAnimator(mCurrentDegree);
        stopPour();
        return;
      }

      float width = mViewWidth / 2.f + mObjectWidth / 2.f + mObjectWidth;

      mIsStartedPour = true;
      mBottlePourHeight += BOTTLE_POUR_HEIGHT_DX;
      mBottlePourPath.reset();
      mBottlePourPath.addRect(0, 0, width, mBottlePourHeight, Path.Direction.CCW);

      mGlassPourHeight -= GLASS_POUR_HEIGHT_DX;
      mGlassPourPath.reset();
      mGlassPourPath.addRect(0, 0, width, mGlassPourHeight, Path.Direction.CCW);
      drawGlass(0);

      if (mBottlePourHeight > mBottlePourHeightLimit - mBottlePourHeightLimit * 0.05f) {
        mBeerFrothRadius += 0.15;
        drawGlassFroth(0, mBeerFrothRadius);
      }
      mPourHandler.postDelayed(this, 30);
    }
  };

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

  private void init() {
    //setLayerType(LAYER_TYPE_HARDWARE, null);
    mPourHandler = new Handler();
    mRandom = new Random();
    mFrothPoints = new float[FROTH_NUM][2];
    mGlassBeerRegion = new Region();

    setUpPaint();
    setUpPath();
  }

  private void setUpPaint() {
    mBottlePaint = new Paint();
    mBottlePaint.setColor(BOTTLE_COLOR);
    mBottlePaint.setStyle(Paint.Style.FILL);
    mBottlePaint.setAntiAlias(true);

    mBeerPaint = new Paint();
    mBeerPaint.setColor(BEER_COLOR);
    mBeerPaint.setStyle(Paint.Style.FILL);
    mBeerPaint.setAntiAlias(true);

    mGlassPaint = new Paint();
    mGlassPaint.setColor(GLASS_COLOR);
    mGlassPaint.setStyle(Paint.Style.FILL);
    mGlassPaint.setAntiAlias(true);

    mBottleLabelInnerCirclePaint = new Paint();
    mBottleLabelInnerCirclePaint.setColor(BOTTLE_LABEL_INNER_CIRCLE_COLOR);
    mBottleLabelInnerCirclePaint.setStyle(Paint.Style.FILL);
    mBottleLabelInnerCirclePaint.setAntiAlias(true);

    mBottleLabelOuterCirclePaint = new Paint();
    mBottleLabelOuterCirclePaint.setColor(BOTTLE_LABEL_OUTER_CIRCLE_COLOR);
    mBottleLabelOuterCirclePaint.setStyle(Paint.Style.FILL);
    mBottleLabelOuterCirclePaint.setAntiAlias(true);

    mBottleLabelRibbonPaint = new Paint();
    mBottleLabelRibbonPaint.setColor(BOTTLE_LABEL_RIBBON_COLOR);
    mBottleLabelRibbonPaint.setStyle(Paint.Style.FILL);
    mBottleLabelRibbonPaint.setAntiAlias(true);

    mBottleEmptyPaint = new Paint();
    mBottleEmptyPaint.setColor(BOTTLE_EMPTY_COLOR);
    mBottleEmptyPaint.setStyle(Paint.Style.FILL);
    mBottleEmptyPaint.setAntiAlias(true);

    mBottleBackgroundPaint = new Paint();
    mBottleBackgroundPaint.setColor(BOTTLE_BACKGROUND_COLOR);
    mBottleBackgroundPaint.setStyle(Paint.Style.FILL);
    mBottleBackgroundPaint.setAntiAlias(true);

    mGlassBackgroundPaint = new Paint();
    mGlassBackgroundPaint.setColor(GLASS_BACKGROUND_COLOR);
    mGlassBackgroundPaint.setStyle(Paint.Style.FILL);
    mGlassBackgroundPaint.setAntiAlias(true);
  }

  private void setUpPath() {
    mBottlePath = new BottlePath();
    mGlassPath = new GlassPath();

    mBottlePourPath = new Path();
    mGlassPourPath = new Path();
    mFrothPath = new Path();
  }

  @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    mViewWidth = w;

    mCircleWidth = w / 3;
    mObjectWidth = mCircleWidth - mViewWidth / 15.f;
    mGlassHeight = h / 2;
    mBottlePourHeight = 0.3f * mCircleWidth;
    mGlassPourHeight = 0.865f * mCircleWidth + mGlassHeight;
    mBottleCenterX = mViewWidth / 2.f;
    mBottleCenterY = mCircleWidth / 2.f;
    mGlassCenterX = mViewWidth / 2.f;
    mGlassCenterY = mGlassHeight + mCircleWidth / 2.f;

    mBottlePourPath.reset();
    mBottlePourPath.addRect(0, 0, w, mBottlePourHeight, Path.Direction.CCW);

    mGlassPourPath.reset();
    mGlassPourPath.addRect(0, 0, w, mGlassPourHeight, Path.Direction.CCW);

    initPoint();

    mBottlePourHeightLimit = mBottleBeerPoints[6].getRotateY(-90);
    super.onSizeChanged(w, h, oldw, oldh);
  }

  private void initPoint() {
    int bottleIndex = Bottle.BOTTLE_POINTS.length;
    for (int index = 0; index < bottleIndex; index++) {
      mBottlePoints[index] = new Point(mObjectWidth * Bottle.BOTTLE_POINTS[index][0],
          mObjectWidth * Bottle.BOTTLE_POINTS[index][1] + mCircleWidth * 0.5f - mObjectWidth * 0.5f,
          Point.BOTTLE);
    }
    for (int index = bottleIndex; index < bottleIndex * 2; index++) {
      mBottlePoints[index] =
          new Point(mObjectWidth * (1 - Bottle.BOTTLE_POINTS[index - bottleIndex][0]),
              mObjectWidth * Bottle.BOTTLE_POINTS[index - bottleIndex][1] + mCircleWidth * 0.5f
                  - mObjectWidth * 0.5f, Point.BOTTLE);
    }

    int circleIndex = Bottle.LABEL_OUTER_CIRCLE_POINTS.length;
    for (int index = 0; index < circleIndex; index++) {
      mBottleLabelOuterCirclePoints[index] =
          new Point(mObjectWidth * Bottle.LABEL_OUTER_CIRCLE_POINTS[index][0],
              mObjectWidth * Bottle.LABEL_OUTER_CIRCLE_POINTS[index][1] + mCircleWidth * 0.5f
                  - mObjectWidth * 0.5f, Point.BOTTLE);
    }
    for (int index = circleIndex; index < circleIndex * 2; index++) {
      mBottleLabelOuterCirclePoints[index] =
          new Point(mObjectWidth * (1 - Bottle.LABEL_OUTER_CIRCLE_POINTS[index - circleIndex][0]),
              mObjectWidth * Bottle.LABEL_OUTER_CIRCLE_POINTS[index - circleIndex][1]
                  + mCircleWidth * 0.5f - mObjectWidth * 0.5f, Point.BOTTLE);
    }

    circleIndex = Bottle.LABEL_INNER_CIRCLE_POINTS.length;
    for (int index = 0; index < Bottle.LABEL_INNER_CIRCLE_POINTS.length; index++) {
      mBottleLabelInnerCirclePoints[index] =
          new Point(mObjectWidth * Bottle.LABEL_INNER_CIRCLE_POINTS[index][0],
              mObjectWidth * Bottle.LABEL_INNER_CIRCLE_POINTS[index][1] + mCircleWidth * 0.5f
                  - mObjectWidth * 0.5f, Point.BOTTLE);
    }
    for (int index = circleIndex; index < circleIndex * 2; index++) {
      mBottleLabelInnerCirclePoints[index] =
          new Point(mObjectWidth * (1 - Bottle.LABEL_INNER_CIRCLE_POINTS[index - circleIndex][0]),
              mObjectWidth * Bottle.LABEL_INNER_CIRCLE_POINTS[index - circleIndex][1]
                  + mCircleWidth * 0.5f - mObjectWidth * 0.5f, Point.BOTTLE);
    }

    int ribbonIndex = Bottle.LABEL_RIBBON_POINTS.length;
    for (int index = 0; index < ribbonIndex; index++) {
      mBottleLabelRibbonPoints[index] =
          new Point(mObjectWidth * Bottle.LABEL_RIBBON_POINTS[index][0],
              mObjectWidth * Bottle.LABEL_RIBBON_POINTS[index][1] + mCircleWidth * 0.5f
                  - mObjectWidth * 0.5f, Point.BOTTLE);
    }
    for (int index = ribbonIndex; index < ribbonIndex * 2; index++) {
      mBottleLabelRibbonPoints[index] =
          new Point(mObjectWidth * (1 - Bottle.LABEL_RIBBON_POINTS[index - ribbonIndex][0]),
              mObjectWidth * Bottle.LABEL_RIBBON_POINTS[index - ribbonIndex][1]
                  + mCircleWidth * 0.5f - mObjectWidth * 0.5f, Point.BOTTLE);
    }

    int bottleBeerIndex = Bottle.BEER_POINTS.length;
    for (int index = 0; index < bottleBeerIndex; index++) {
      mBottleBeerPoints[index] = new Point(mObjectWidth * Bottle.BEER_POINTS[index][0],
          mObjectWidth * Bottle.BEER_POINTS[index][1] + mCircleWidth * 0.5f - mObjectWidth * 0.5f,
          Point.BOTTLE);
    }
    for (int index = bottleBeerIndex; index < bottleBeerIndex * 2; index++) {
      mBottleBeerPoints[index] =
          new Point(mObjectWidth * (1 - Bottle.BEER_POINTS[index - bottleBeerIndex][0]),
              mObjectWidth * Bottle.BEER_POINTS[index - bottleBeerIndex][1] + mCircleWidth * 0.5f
                  - mObjectWidth * 0.5f, Point.BOTTLE);
    }

    int glassIndex = Glass.GLASS_POINTS.length;
    for (int index = 0; index < glassIndex; index++) {
      mGlassPoints[index] = new Point(mObjectWidth * Glass.GLASS_POINTS[index][0],
          mGlassHeight + mObjectWidth * Glass.GLASS_POINTS[index][1] + mCircleWidth * 0.5f
              - mObjectWidth * 0.5f, Point.GLASS);
    }
    for (int index = glassIndex; index < glassIndex * 2; index++) {
      mGlassPoints[index] =
          new Point(mObjectWidth * (1 - Glass.GLASS_POINTS[index - glassIndex][0]),
              mGlassHeight + mObjectWidth * Glass.GLASS_POINTS[index - glassIndex][1]
                  + mCircleWidth * 0.5f - mObjectWidth * 0.5f, Point.GLASS);
    }

    int glassBeerIndex = Glass.GLASS_BEER_POINTS.length;
    for (int index = 0; index < glassBeerIndex; index++) {
      mGlassBeerPoints[index] = new Point(mObjectWidth * Glass.GLASS_BEER_POINTS[index][0],
          mGlassHeight + mObjectWidth * Glass.GLASS_BEER_POINTS[index][1] + mCircleWidth * 0.5f
              - mObjectWidth * 0.5f, Point.GLASS);
    }
    for (int index = glassIndex; index < glassBeerIndex * 2; index++) {
      mGlassBeerPoints[index] =
          new Point(mObjectWidth * (1 - Glass.GLASS_BEER_POINTS[index - glassBeerIndex][0]),
              mGlassHeight + mObjectWidth * Glass.GLASS_BEER_POINTS[index - glassBeerIndex][1]
                  + mCircleWidth * 0.5f - mObjectWidth * 0.5f, Point.GLASS);
    }

    for (int index = 0; index < Glass.GLASS_BEER_FROTH_POINTS.length; index++) {
      mGlassFrothPoints[index] = new Point(mObjectWidth * Glass.GLASS_BEER_FROTH_POINTS[index][0],
          mGlassHeight + mObjectWidth * Glass.GLASS_BEER_FROTH_POINTS[index][1]
              + mCircleWidth * 0.5f - mObjectWidth * 0.5f, Glass.GLASS_BEER_FROTH_POINTS[index][2],
          Point.GLASS);
    }

    drawGlass(0);
    drawBottle(0);
  }

  public boolean isMax() {
    return mIsMax;
  }

  public boolean isReverseAnimationRunning() {
    return mIsReverseAnimationRunning;
  }

  @Override protected void onDraw(Canvas canvas) {
    mBottlePath.getBeerPath().op(mBottlePourPath, Path.Op.DIFFERENCE);
    mFrothPath.reset();
    int radius = 8;
    for (int i = 0; i < FROTH_NUM; i++) {
      ValueAnimator animator = mFrothAnimators[i];
      if (animator == null) {
        continue;
      }
      float value = (float) animator.getAnimatedValue();
      float centerX = mFrothPoints[i][0];
      float centerY = mFrothPoints[i][1] * (1.f - value);
      if (mGlassBeerRegion.contains((int) centerX + radius, (int) centerY)
          && mGlassBeerRegion.contains((int) centerX - radius, (int) centerY)) {
        mFrothPath.addCircle(centerX, centerY, radius * ((value + 0.1f) * 5.f), Path.Direction.CCW);
      }
    }
    mGlassPath.getBeerPath().op(mGlassPourPath, Path.Op.DIFFERENCE);
    canvas.drawCircle(mBottleCenterX, mBottleCenterY, mCircleWidth / 2, mBottleBackgroundPaint);
    canvas.drawCircle(mGlassCenterX, mGlassCenterY, mCircleWidth / 2, mGlassBackgroundPaint);
    canvas.drawPath(mGlassPath.getGlassPath(), mGlassPaint);
    canvas.drawPath(mGlassPath.getBeerPath(), mBeerPaint);
    canvas.drawPath(mBottlePath.getBottlePath(), mBottlePaint);
    canvas.drawPath(mBottlePath.getEmptyPath(), mBottleEmptyPaint);
    canvas.drawPath(mBottlePath.getBeerPath(), mBeerPaint);
    canvas.drawPath(mBottlePath.getLabelOuterCirclePath(), mBottleLabelOuterCirclePaint);
    canvas.drawPath(mBottlePath.getLabelInnerCirclePath(), mBottleLabelInnerCirclePaint);
    canvas.drawPath(mBottlePath.getLabelRibbonPath(), mBottleLabelRibbonPaint);
    canvas.drawPath(mGlassPath.getBeerFrothPath(), mGlassPaint);
    canvas.drawPath(mFrothPath, mGlassPaint);
  }

  @Override protected void onDetachedFromWindow() {
    if (mAlphaAnimator != null) {
      mAlphaAnimator.end();
      mAlphaAnimator.removeAllUpdateListeners();
    }
    for (ValueAnimator animator : mFrothAnimators) {
      if (animator != null) {
        animator.end();
        animator.removeAllUpdateListeners();
      }
    }
    super.onDetachedFromWindow();
  }

  public void startPour() {
    if (mIsStartedPour) {
      return;
    }
    startFrothAnimation();
    mPourHandler.post(mPourRunnable);
  }

  public void stopPour() {
    mIsStartedPour = false;
    mPourHandler.removeCallbacks(mPourRunnable);
    if (mBottlePourHeightLimit < mBottlePourHeight) {
      mBottlePourPath.reset();
      mBottlePourPath.addRect(0, 0, mViewWidth, 1000, Path.Direction.CCW);
      invalidate();
    }
  }

  public void startReverseBottleAnimator(float degree) {
    ValueAnimator returnAnimator = ValueAnimator.ofFloat(degree, 0.f);
    returnAnimator.setDuration(500);
    returnAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
      @Override public void onAnimationUpdate(ValueAnimator animation) {
        float value = (float) animation.getAnimatedValue();
        drawBottle(value);
      }
    });
    returnAnimator.addListener(new Animator.AnimatorListener() {
      @Override public void onAnimationStart(Animator animation) {
        mIsReverseAnimationRunning = true;
      }

      @Override public void onAnimationEnd(Animator animation) {
        mIsReverseAnimationRunning = false;
      }

      @Override public void onAnimationCancel(Animator animation) {

      }

      @Override public void onAnimationRepeat(Animator animation) {

      }
    });
    returnAnimator.start();
  }

  public void startDisappearAnimator() {
    mAlphaAnimator = ValueAnimator.ofFloat(getAlpha(), 0.f);
    mAlphaAnimator.setDuration(500);
    mAlphaAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
      @Override public void onAnimationUpdate(ValueAnimator animation) {
        setAlpha((float) animation.getAnimatedValue());
      }
    });
    mAlphaAnimator.addListener(new Animator.AnimatorListener() {
      @Override public void onAnimationStart(Animator animation) {

      }

      @Override public void onAnimationEnd(Animator animation) {
        stopFrothAnimation();
        stopPour();
        setVisibility(GONE);

        setUpPath();
        mBottlePourHeight = 0.3f * mCircleWidth;
        mGlassPourHeight = 0.865f * mCircleWidth + mGlassHeight;
        mBeerFrothRadius = 0.f;

        mBottlePourPath.reset();
        mBottlePourPath.addRect(0, 0, getWidth(), mBottlePourHeight, Path.Direction.CCW);

        mGlassPourPath.reset();
        mGlassPourPath.addRect(0, 0, getWidth(), mGlassPourHeight, Path.Direction.CCW);

        mIsMax = false;
      }

      @Override public void onAnimationCancel(Animator animation) {

      }

      @Override public void onAnimationRepeat(Animator animation) {

      }
    });
    mAlphaAnimator.start();
  }


  public void startFrothAnimation() {
    if (mFrothAnimators[0] != null && mFrothAnimators[0].isRunning()) {
      return;
    }
    for (int i = 0; i < FROTH_NUM; i++) {
      ValueAnimator animator = ValueAnimator.ofFloat(0.f, 0.15f);
      animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override public void onAnimationUpdate(ValueAnimator valueAnimator) {
          if (!mIsMax) {
            drawGlass(0);
          }
        }
      });
      animator.setRepeatCount(ValueAnimator.INFINITE);
      animator.setRepeatMode(ValueAnimator.RESTART);
      final int finalI = i;
      animator.addListener(new Animator.AnimatorListener() {
        @Override public void onAnimationStart(Animator animation) {

        }

        @Override public void onAnimationEnd(Animator animation) {

        }

        @Override public void onAnimationCancel(Animator animation) {

        }

        @Override public void onAnimationRepeat(Animator animation) {
          mFrothPoints[finalI][0] =
              (mRandom.nextFloat() * 0.2264f + 0.3868f) * mObjectWidth + mViewWidth / 2
                  - mObjectWidth / 2;
          mFrothPoints[finalI][1] =
              (mRandom.nextFloat() * 0.365f + 0.5f) * mObjectWidth + mGlassHeight;
        }
      });
      animator.setDuration(2000);
      animator.setStartDelay(i * 2000 / FROTH_NUM);
      animator.start();
      mFrothAnimators[i] = animator;
    }
  }

  private void stopFrothAnimation() {
    for (ValueAnimator animator : mFrothAnimators) {
      if (animator != null) {
        animator.end();
      }
    }
    mFrothAnimators = new ValueAnimator[FROTH_NUM];
  }

  @Override public void invalidate() {
    if (SystemClock.uptimeMillis() - mLastInvalidate < 30) {
      return;
    }
    mLastInvalidate = SystemClock.uptimeMillis();
    super.invalidate();
  }

  public void drawBottle(float degree) {
    mCurrentDegree = degree;
    mBottlePath.drawBottle(mBottlePoints, degree);
    mBottlePath.drawLabelOuterCircle(mBottleLabelOuterCirclePoints, degree);
    mBottlePath.drawLabelOuterCircle(mBottleLabelOuterCirclePoints, degree);
    mBottlePath.drawLabelInnerCircle(mBottleLabelInnerCirclePoints, degree);
    mBottlePath.drawLabelRibbon(mBottleLabelRibbonPoints, degree);
    mBottlePath.drawBeer(mBottleBeerPoints, degree);
    invalidate();
  }

  public void drawGlass(float degree) {
    mGlassPath.drawGlass(mGlassPoints, degree);
    mGlassPath.drawBeer(mGlassBeerPoints, degree);
    mRectF.setEmpty();
    mGlassPath.getBeerPath().computeBounds(mRectF, true);
    mGlassBeerRegion.setEmpty();
    mClipRegion.setEmpty();
    mClipRegion.set((int) mRectF.left, (int) mRectF.top, (int) mRectF.right, (int) mRectF.bottom);
    mGlassBeerRegion.setPath(mGlassPath.getBeerPath(), mClipRegion);
    invalidate();
  }

  public void drawGlassFroth(float degree, float radiusPercent) {
    mGlassPath.drawGlassFroth(degree, mGlassFrothPoints, Math.min(radiusPercent, 1));
    invalidate();
  }

  public class Point {

    public final static int BOTTLE = 0;
    public final static int GLASS = 1;
    private float x;
    private float y;
    private float radius;
    private float centerX = mObjectWidth * 0.5f;
    private float centerY;

    public Point(float x, float y, float radius, int mode) {
      this(x, y, mode);
      this.radius = radius;
    }

    public Point(float x, float y, int mode) {
      switch (mode) {
        case BOTTLE:
          centerY = mBottleCenterY;
          break;
        case GLASS:
          centerY = mGlassCenterY;
          break;
      }
      this.x = x;
      this.y = y;
    }

    public float getRotateX(float degree) {
      return (float) (mBottleCenterX + (x - centerX) * Math.cos(Math.toRadians(degree))
          - (y - centerY) * Math.sin(Math.toRadians(degree)));
    }

    public float getRotateY(float degree) {
      return (float) (centerY + (x - centerX) * Math.sin(Math.toRadians(degree))
          + (y - centerY) * Math.cos(Math.toRadians(degree)));
    }

    public float getRadius() {
      return radius * mObjectWidth;
    }
  }
}