
package com.sheep.gamegroup.view.customview;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.support.v7.widget.AppCompatTextView;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.TextView;

import com.sheep.gamegroup.util.LogUtil;

import rx.functions.Action1;

/**
 * @ClassName: TextRubbler
 * @Description: 可擦除的textview, 类似于刮刮乐的效果. 设计思路 : 在文字的上面绘制一层遮罩，当用户在该区域涂抹时，
 *               会在该遮罩层上绘制一条曲线path, 此时该path下的文字就会显示出来，与mPaint设置的setXfermode有关.
 * @author Honghui He
 */
public class ErasableTextView extends AppCompatTextView {

    /**
     * 
     */
    private Canvas mCanvas = null;
    /**
     * 
     */
    private Paint mPaint = null;
    /**
     * 整个用于遮罩的bitmap, 将文字遮住.当用户涂抹该view时，绘制一条透明的path到上面，这样path下的文字就显示出来.
     */
    private Bitmap mErasableBitmap = null;

    /**
     * 绘制可擦除范围内的路径，用户手指绘制的擦除路径
     */
    private Path mFingerPath;
    /**
     * 
     */
    private float mX;
    private float mY;

    /**
     * 是否绘制颜色背景
     */
    private boolean canErase = false;

    /**
     * 触摸公差
     */
    private float mTouchTolerance;

    /**
     * 遮罩层的宽度
     */
    private int mErasableWidth = 0;
    /**
     * 遮罩层的高度
     */
    private int mErasableHeight = 0;
    /**
     * 擦除时的画笔大小
     */
    private int mStokeWidth = 8;
    /**
     * 默认可擦除颜色的背景为gray
     */
    private int mErasableColor = Color.GRAY;

    private final String TAG = this.getClass().getSimpleName();

    /**
     * @Title: ErasableTextView
     * @Description: ErasableTextView Constructor
     * @param context
     */
    public ErasableTextView(Context context) {
        super(context);

    }

    /**
     * @Title: ErasableTextView
     * @Description: ErasableTextView Constructor
     * @param context
     * @param attrs
     * @param defStyle
     */
    public ErasableTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    /**
     * @Title: ErasableTextView
     * @Description: ErasableTextView Constructor
     * @param context
     * @param attrs
     */
    public ErasableTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    /**
     * (非 Javadoc)
     * 
     * @Title: onDraw
     * @Description: 首先绘制文本，再绘制遮罩层
     * @param canvas
     * @see TextView#onDraw(Canvas)
     */
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // 如果是可擦除状态则绘制背景色
        if (canErase && mCanvas != null && !isFinish) {
            // 绘制用户滑动的路径到擦除层, 即用户已经涂掉的区域
            mCanvas.drawPath(mFingerPath, mPaint);
            // 绘制整个擦除层
            canvas.drawBitmap(mErasableBitmap, 0, 0, null);
        }
    }

    /**
     * @Title: setErasable
     * @Description: 设置是否为可擦除textview
     * @param enable
     * @throws
     */
    public void setErasable(boolean enable) {
        canErase = enable;

        // 初始化画笔
        initPaint();

        // 创建路径
        mFingerPath = new Path();

        // 检测宽度和高度
        checkErasableRect();

        // 初始化画布
        initCanvas();
    }

    /**
     * @Title: initPaint
     * @Description: 初始化画笔
     * @throws
     */
    private void initPaint() {
        mPaint = new Paint();
        // 设置透明度为0
        // 设置为透明的画笔，因此当用户用手涂时会绘制透明的路径，然后被遮住的文字就会显示出来
        // 与setXfermode向协作即可实现擦除效果.
        mPaint.setAlpha(0);
        //
        mPaint.setColor(Color.BLACK);
        // 可以通过修改Paint的Xfermode来影响在Canvas已有的图像上面绘制新的颜色模式, 这里设置为显示新绘制的颜色
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));

        // 设置反锯齿
        mPaint.setAntiAlias(true);
        mPaint.setDither(true);
        mPaint.setStyle(Paint.Style.STROKE);
        // 设置圆角
        mPaint.setStrokeJoin(Paint.Join.ROUND);
        // 设置画笔的笔尖风格
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        // 设置画笔stoke宽度
        mPaint.setStrokeWidth(mStokeWidth);
    }

    /**
     * @Title: initCanvas
     * @Description: 初始化画布
     * @throws
     */
    private void initCanvas() {
        try {
            // 以自身的宽和高创建bitmap对象, widht, height < 0则抛出异常
            mErasableBitmap = Bitmap.createBitmap(mErasableWidth,
                    mErasableHeight, Config.ARGB_8888);

            // 以mBitmap创建画布
            mCanvas = new Canvas(mErasableBitmap);
            // 背景色
            mCanvas.drawColor(mErasableColor);
            //文字
            Rect targetRect = new Rect(0, 0, mErasableWidth, mErasableHeight);
            Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
            paint.setStrokeWidth(3);
            paint.setTextSize(getTextSize());
            paint.setColor(Color.BLACK);
            Paint.FontMetricsInt fontMetrics = paint.getFontMetricsInt();
            // 转载请注明出处：http://blog.csdn.net/hursing
            int baseline = (targetRect.bottom + targetRect.top - fontMetrics.bottom - fontMetrics.top) / 2;
            // 下面这行是实现水平居中，drawText对应改为传入targetRect.centerX()
            paint.setTextAlign(Paint.Align.CENTER);
            mCanvas.drawText("刮奖区", targetRect.centerX(), baseline, paint);

        } catch (IllegalArgumentException e) {
            Log.e(TAG, "请在调用setErasable之前\n\n设置可擦除的宽度和高度, 调用方法为setErasableWidth, setErasableHeight");
            e.printStackTrace();
        }

    }

    /**
     * @Title: checkErasableRect
     * @Description: 检测开发者在xml设置的宽度和高度.
     *               如果不是设置确切的高度值，而是设置match_parent等,则width和height都为-1
     * @throws
     */
    private void checkErasableRect() {
        int width = getLayoutParams().width;
        int height = getLayoutParams().height;
        if (width > 0 && height > 0) {
            setErasableSize(width, height);
        }
    }

    /**
     * 获取 canErase
     * 
     * @return 返回 canErase
     */
    public boolean isErasable() {
        return canErase;
    }

    /**
     * @Title: setStokeWidth
     * @Description:
     * @throws
     */
    public void setStokeWidth(int stokeWidth) {
        mStokeWidth = stokeWidth;
    }

    public int getStokeWidth() {
        return mStokeWidth;
    }

    /**
     * @Title: setErasableColor
     * @Description: 设置可擦除的背景色
     * @param color
     * @throws
     */
    public void setErasableColor(int color) {
        mErasableColor = color;
    }

    /**
     * @Title: getErasableColor
     * @Description: 获取可擦除的背景色
     * @return
     * @throws
     */
    public int getErasableColor() {
        return mErasableColor;
    }

    /**
     * @Title: setTouchTolerance
     * @Description: 设置滑动多大距离为有效滑动
     * @param tolerance
     * @throws
     */
    public void setTouchTolerance(float tolerance) {
        mTouchTolerance = tolerance;
    }

    /**
     * @Title: getTouchTolerance
     * @Description:
     * @return
     * @throws
     */
    public float getTouchTolerance() {
        return mTouchTolerance;
    }

    /**
     * 获取 mErasableWidth
     * 
     * @return 返回 mErasableWidth
     */
    public int getErasableWidth() {
        return mErasableWidth;
    }

    /**
     * 设置 mErasableWidth
     * 
     * @param width
     * @param height
     */
    public void setErasableSize(int width, int height) {
        this.mErasableWidth = width;
        this.mErasableHeight = height;
    }

    /**
     * 获取 mErasableHeight
     * 
     * @return 返回 mErasableHeight
     */
    public int getErasableHeight() {
        return mErasableHeight;
    }


    /**
     * (非 Javadoc)
     * 
     * @Title: onTouchEvent
     * @Description: 触摸事件
     * @param event
     * @return
     * @see TextView#onTouchEvent(MotionEvent)
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (!canErase) {
            return true;
        }
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN: // 手指按下
                touchDown(event.getX(), event.getY());
                invalidate();
                break;
            case MotionEvent.ACTION_MOVE: // 移动
                touchMove(event.getX(), event.getY());
                invalidate();
                break;
            case MotionEvent.ACTION_UP: // 抬起
                touchUp(event.getX(), event.getY());
                invalidate();
                break;
            default:
                break;
        }
        return true;
    }
//    int lastX = -1;
//    int lastY = -1;
//    int direction = 0;
//    public static final int NONE = 0;//无方向
//    public static final int TOP_BOTTOM = 1;//上下
//    public static final int LEFT_RIGHT = 2;//左右
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
//        int x = (int) ev.getRawX();
//        int y = (int) ev.getRawY();
//
//        switch (ev.getAction()) {
//            case MotionEvent.ACTION_DOWN:
//                lastX = x;
//                lastY = y;
//                direction = NONE;
//                // 保证子View能够接收到Action_move事件
//                getParent().requestDisallowInterceptTouchEvent(true);
//                break;
//            case MotionEvent.ACTION_MOVE:
//                if(direction == NONE) {
//                    int dealtX = Math.abs(x - lastX);
//                    int dealtY = Math.abs(y - lastY);
//                    // 这里是够拦截的判断依据是上下滑动，读者可根据自己的逻辑进行是否拦截
//                    if (dealtX <= dealtY) {
//                        direction = TOP_BOTTOM;
//                        getParent().requestDisallowInterceptTouchEvent(true);
//                    } else {
//                        direction = LEFT_RIGHT;
//                        getParent().requestDisallowInterceptTouchEvent(false);
//                    }
//                }
//                break;
//            case MotionEvent.ACTION_CANCEL:
//                break;
//            case MotionEvent.ACTION_UP:
//                break;
//
//        }
        //拦截所有滑动事件
        getParent().requestDisallowInterceptTouchEvent(true);
        return super.dispatchTouchEvent(ev);
    }

    /**
     * @Title: touchDown
     * @Description:
     * @param x
     * @param y
     * @throws
     */
    private void touchDown(float x, float y) {
        mFingerPath.reset();
        mFingerPath.moveTo(x, y);
        mX = x;
        mY = y;
    }

    /**
     * @Title: touchMove
     * @Description:
     * @param x
     * @param y
     * @throws
     */
    private void touchMove(float x, float y) {
        float dx = Math.abs(x - mX);
        float dy = Math.abs(y - mY);
        if (dx >= mTouchTolerance || dy >= mTouchTolerance) {
            // quadTo这个方法将一条线段变成一个曲线
            mFingerPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
            mX = x;
            mY = y;
        }

    }

    /**
     * @Title: touchUp
     * @Description:
     * @param x
     * @param y
     * @throws
     */
    private void touchUp(float x, float y) {
        mFingerPath.lineTo(x, y);
        mCanvas.drawPath(mFingerPath, mPaint);
        RectF rectF = new RectF();
        mFingerPath.computeBounds(rectF, true);
        toCheckRectF(rectF);
        mFingerPath.reset();
    }

    //检查擦除有效范围来更新次数
    private void toCheckRectF(RectF rectF) {
        if(rectF.right - rectF.left > mErasableWidth * 0.33f){
            touchUpCount++;
        }
        if(rectF.right - rectF.left > mErasableWidth * 0.66f){
            touchUpCount++;
        }
        if(rectF.bottom - rectF.top > mErasableHeight * 0.33f){
            touchUpCount++;
        }
        if(rectF.bottom - rectF.top > mErasableHeight * 0.66f){
            touchUpCount++;
        }
        if (onTouchUpCountChangeListener != null)
            onTouchUpCountChangeListener.call(touchUpCount);
        LogUtil.println(TAG, "checkRectF", touchUpCount);
    }

    //擦除次数
    private int touchUpCount;
    //擦除次数监听
    private Action1<Integer> onTouchUpCountChangeListener;

    public void setOnTouchUpCountChangeListener(Action1<Integer> onTouchUpCountChangeListener) {
        this.onTouchUpCountChangeListener = onTouchUpCountChangeListener;
    }

    //是否已经完成
    private boolean isFinish;

    public void setFinish(boolean finish) {
        isFinish = finish;
    }
}
