package com.open.androidtvwidget.keyboard;

import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.FontMetricsInt;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;

import com.open.androidtvwidget.keyboard.SoftKey.SaveSoftKey;
import com.open.androidtvwidget.utils.OPENLOG;

import java.util.List;

/**
 * 软键盘绘制控件.(主软键盘,弹出键盘)
 *
 * @author hailong.qiu [email protected]
 */
public class SoftKeyboardView extends View {

    private static final String TAG = "SoftKeyboardView";

    public SoftKeyboardView(Context context) {
        super(context);
        init(context, null);
    }

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

    public SoftKeyboardView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context, attrs);
    }

    private void init(Context context, AttributeSet attrs) {
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mFmi = mPaint.getFontMetricsInt();
    }

    private SoftKeyboard mSoftKeyboard;
    private Bitmap mCacheBitmap;

    /*
     * 传入需要绘制的键盘(从XML读取出来的).
     */
    public void setSoftKeyboard(SoftKeyboard softSkb) {
        this.mSoftKeyboard = softSkb;
        clearCacheBitmap();
    }

    public void clearCacheBitmap() {
        mCacheBitmap = null;
        invalidate();
    }

    @Override
    protected void onDraw(Canvas rootCanvas) {
        if (mSoftKeyboard == null)
            return;
        if (mCacheBitmap == null) {
            OPENLOG.D("onDraw mCacheBitmap:" + mCacheBitmap);
            mCacheBitmap = createCacheBitmap();
            Canvas canvas = new Canvas(mCacheBitmap);
            // 绘制键盘背景.
            drawKeyboardBg(canvas);
            // 绘制键盘的按键.
            int rowNum = this.mSoftKeyboard.getRowNum();
            for (int row = 0; row < rowNum; row++) {
                KeyRow keyRow = this.mSoftKeyboard.getKeyRowForDisplay(row);
                if (keyRow == null)
                    continue;
                List<SoftKey> softKeys = keyRow.getSoftKeys();
                int keyNum = softKeys.size();
                for (int i = 0; i < keyNum; i++) {
                    SoftKey softKey = softKeys.get(i);
                    drawSoftKey(canvas, softKey, false);
                }
            }
        }
        // 绘制缓存.
        drawCacheBitmap(rootCanvas);
        // 绘制按键.
        SoftKey key = mSoftKeyboard.getSelectSoftKey();
        drawSoftKey(rootCanvas, key, true);
    }

    /**
     * Bitmap.Config ARGB_8888:<br>
     * 每个像素占四位,即A=8,R=8,G=8,B=8,<br>
     * 那么一个像素点占8+8+8+8=32位<br>
     */
    private Bitmap createCacheBitmap() {
        return Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
    }

    private void drawCacheBitmap(Canvas rootCanvas) {
        if (mCacheBitmap != null) {
            Paint paint = new Paint();
            paint.setAntiAlias(true);
            rootCanvas.drawBitmap(mCacheBitmap, 0, 0, paint);
        }
    }

    private Paint mPaint;

    /**
     * 绘制键盘的背景.
     */
    private void drawKeyboardBg(Canvas canvas) {
        Drawable bg = mSoftKeyboard.getKeyboardBg();
        Rect rect = new Rect(0, 0, getWidth(), getHeight());
        if (bg != null) {
            bg.setBounds(rect);
            bg.draw(canvas);
        } else {
            Paint paint = new Paint();
            canvas.drawRect(rect, paint);
        }
    }

    /**
     * 绘制键值.
     */
    private void drawSoftKey(Canvas canvas, SoftKey softKey, boolean isDrawState) {
        if (softKey == null) {
            OPENLOG.E("softKey is null...");
            return;
        }
        // 绘制按键背景.
        drawSoftKeyBg(canvas, softKey);
        // 绘制选中状态.
        if (isDrawState) {
            if (softKey.isKeySelected()) {
                drawSoftKeySelectState(canvas, softKey);
            }
            if (softKey.isKeyPressed()) {
                drawSoftKeyPressState(canvas, softKey);
            }
        }
        // 绘制选中状态 and 选中边框最前面.
        // BUG(避免重复绘制) 主要用于挡住文字的,在绘制一次而已.
        // 如果是透明的选中背景,那就不需要再重复绘制一次文字或者图标了.
        if (isDrawState && mIsFront) {
            return;
        }
        // 绘制按键内容.
        String keyLabel = softKey.getKeyLabel();
        Drawable keyIcon = softKey.getKeyIcon();
        if (keyIcon != null) {
            drawSoftKeyIcon(canvas, softKey, keyIcon);
        } else if (!TextUtils.isEmpty(keyLabel)) {
            drawSoftKeyText(canvas, softKey, keyLabel);
        }
    }

    /**
     * 绘制按键的图片.
     */
    private void drawSoftKeyIcon(Canvas canvas, SoftKey softKey, Drawable keyIcon) {
        int marginLeft = Math.abs((int) ((softKey.getWidth() - keyIcon.getIntrinsicWidth()) / 2)) + 2;
        int marginRight = Math.abs((int) (softKey.getWidth() - keyIcon.getIntrinsicWidth() - marginLeft)) + 4;
        int marginTop = Math.abs((int) ((softKey.getHeight() - keyIcon.getIntrinsicHeight()) / 2)) + 2;
        int marginBottom = Math.abs((int) (softKey.getHeight() - keyIcon.getIntrinsicHeight() - marginTop)) + 4;
        keyIcon.setBounds(softKey.getLeft() + marginLeft, softKey.getTop() + marginTop,
                softKey.getRight() - marginRight, softKey.getBottom() - marginBottom);
        keyIcon.draw(canvas);
    }

    private FontMetricsInt mFmi;

    /**
     * 绘制按键的文本字符.
     */
    private void drawSoftKeyText(Canvas canvas, SoftKey softKey, String keyLabel) {
        mPaint.setTextSize(softKey.getTextSize()); // 文本大小.
        mPaint.setColor(softKey.getTextColor()); // 文本颜色.
        mFmi = mPaint.getFontMetricsInt();
        int fontHeight = mFmi.bottom - mFmi.top; // 字體的高度.
        float fontWidth = mPaint.measureText(keyLabel);
        float marginX = (softKey.getWidth() - fontWidth) / 2.0f;
        float marginY = (softKey.getHeight() - fontHeight) / 2.0f;
        float x = softKey.getLeftF() + marginX;
        // float y = softKey.getTopF() - (mFmi.top) + marginY;
        /**
         * +1,绘制文字的地方才不会出现问题。
         */
        float y = softKey.getTopF() - (mFmi.top + 1) + marginY;
        canvas.drawText(keyLabel, x, y, mPaint);
    }

    /**
     * 绘制按键背景.
     */
    private void drawSoftKeyBg(Canvas canvas, SoftKey softKey) {
        Drawable bgDrawable = softKey.getKeyBgDrawable();
        if (bgDrawable != null) {
            Rect rect = softKey.getRect();
            bgDrawable.setBounds(rect);
            bgDrawable.draw(canvas);
        }
    }

    /**
     * 绘制按键的选中状态.
     */
    private void drawSoftKeySelectState(Canvas canvas, SoftKey softKey) {
        Drawable selectDrawable = softKey.getKeySelectDrawable();
        if (selectDrawable != null) {
            Rect rect = softKey.getMoveRect();//mIsMoveRect ? softKey.getMoveRect() : softKey.getRect();
            int leftPadding, rightPadding, topPadding, bottomPadding;
            leftPadding = rightPadding = topPadding = bottomPadding = 0;
            if (this.mSelectBgRect != null) {
                leftPadding = (int) Math.rint(mSelectBgRect.left);
                rightPadding = (int) Math.rint(mSelectBgRect.right);
                topPadding = (int) Math.rint(mSelectBgRect.top);
                bottomPadding = (int) Math.rint(mSelectBgRect.bottom);
            }
            rect = new Rect(rect.left - leftPadding, rect.top - rightPadding, rect.right + topPadding, rect.bottom + bottomPadding);
            selectDrawable.setBounds(rect);
            selectDrawable.draw(canvas);
        }
    }

    /**
     * 绘制按下的状态.
     */
    private void drawSoftKeyPressState(Canvas canvas, SoftKey softKey) {
        Drawable pressDrawable = softKey.getKeyPressDrawable();
        if (pressDrawable != null) {
            pressDrawable.setBounds(softKey.getRect());
            pressDrawable.draw(canvas);
        }
    }

    public SoftKeyboard getSoftKeyboard() {
        return mSoftKeyboard;
    }

    private RectF mSelectBgRect;

    /**
     * 设置移动边框的相差的间距.
     */
    public void setSoftKeySelectPadding(int padding) {
        setSoftKeySelectPadding(new RectF(padding, padding, padding, padding));
    }

    public void setSoftKeySelectPadding(RectF rect) {
        this.mSelectBgRect = rect;
    }

    public void setSoftKeyPress(boolean isPress) {
        if (mSoftKeyboard == null) {
            OPENLOG.E("setSoftKeyPress isPress:" + isPress);
            return;
        }
        SoftKey softKey = mSoftKeyboard.getSelectSoftKey();
        if (softKey != null) {
            softKey.setKeyPressed(isPress);
            invalidate();
        }
    }

    public SoftKey onTouchKeyPress(int x, int y) {
        SoftKey softKey = mSoftKeyboard.mapToKey(x, y);
        return softKey;
    }

    /**
     * 按键移动. <br>
     * 感觉按照left,top,right,bottom区域<br>
     * 来查找按键,会影响效率.<br>
     * 所以使用了最简单的 行,列概念.<br>
     * 总比谷歌英文输入法(ASOP)的一维好.
     */
    public boolean moveToNextKey(int direction) {
        if (mSoftKeyboard == null) {
            OPENLOG.E("moveToNextKey mSoftKeyboard is null");
            return false;
        }

        int currentRow = mSoftKeyboard.getSelectRow();
        int currentIndex = mSoftKeyboard.getSelectIndex();

        KeyRow keyRow = mSoftKeyboard.getKeyRowForDisplay(currentRow);
        if (keyRow == null) {
            OPENLOG.E("moveToNextKey keyRow is null");
            return false;
        }

        List<SoftKey> softKeys = keyRow.getSoftKeys();
        if (softKeys == null) {
            OPENLOG.E("moveToNextKey keyRow -> softKeys is null");
            return false;
        }
        SoftKey softKey = null;
        SoftKey selectKey = mSoftKeyboard.getSelectSoftKey();

        switch (direction) {
            case KeyEvent.KEYCODE_DPAD_LEFT:
                // 快速查询按键.
                SaveSoftKey saveLSoftKey = selectKey.getNextLeftKey();
                softKey = saveLSoftKey.key;
                //
                if (softKey != null) {
                    currentIndex = saveLSoftKey.index;
                    currentRow = saveLSoftKey.row;
                } else {
                    currentIndex--;
                    if (currentIndex < 0) {
                        // 判断是否可以左右移动(第一个向左移动到最后一个)
                        if (!mSoftKeyboard.isLRMove()) {
                            return false;
                        }
                        if (mSoftKeyboard.isLRMove()) {
                            softKey = mSoftKeyboard.getMoveLeftSoftKey(mSoftKeyboard.getSelectRow() - 1, 0);
                            currentIndex = mSoftKeyboard.getSelectIndex();
                            currentRow = mSoftKeyboard.getSelectRow();
                            if (softKey == null) {
                                currentIndex = softKeys.size() - 1;
                            }
                        } else {
                            currentIndex = 0;
                        }
                    }
                    // 防止重复刷新.
                    if (softKey == null && currentIndex != mSoftKeyboard.getSelectIndex()) {
                        softKey = softKeys.get(currentIndex);
                    }
                    // 保存下个方向的按键.
                    if (softKey != null) {
                        selectKey.setNextLeftKey(softKey, currentRow, currentIndex);
                    }
                }
                break;
            case KeyEvent.KEYCODE_DPAD_RIGHT:
                // 快速查询按键.
                SaveSoftKey saveRSoftKey = selectKey.getNextRightKey();
                softKey = saveRSoftKey.key;
                if (softKey != null) {
                    currentIndex = saveRSoftKey.index;
                    currentRow = saveRSoftKey.row;
                } else {
                    currentIndex++;
                    if (currentIndex > (softKeys.size() - 1)) {
                        if (!mSoftKeyboard.isLRMove()) {
                            return false;
                        }
                        if (mSoftKeyboard.isLRMove()) {
                            softKey = mSoftKeyboard.getMoveRightSoftKey(mSoftKeyboard.getSelectRow() - 1, 0);
                            currentIndex = mSoftKeyboard.getSelectIndex();
                            currentRow = mSoftKeyboard.getSelectRow();
                            // 如果区域查找,右边没有高度很高的按键,则跳到第一个.
                            if (softKey == null)
                                currentIndex = 0;
                        } else {
                            currentIndex = (softKeys.size() - 1);
                        }
                    }
                    // 防止重复刷新.
                    if (softKey == null && currentIndex != mSoftKeyboard.getSelectIndex()) {
                        softKey = softKeys.get(currentIndex);
                    }
                    // 保存下个方向的按键.
                    if (softKey != null) {
                        selectKey.setNextRightKey(softKey, currentRow, currentIndex);
                    }
                }
                break;
            case KeyEvent.KEYCODE_DPAD_DOWN:
                // 快速查询按键.
                SaveSoftKey saveBSoftKey = selectKey.getNextBottomKey();
                softKey = saveBSoftKey.key;
                if (softKey != null) {
                    currentIndex = saveBSoftKey.index;
                    currentRow = saveBSoftKey.row;
                } else {
                    softKey = mSoftKeyboard.getMoveDownSoftKey(mSoftKeyboard.getSelectRow() + 1, mSoftKeyboard.getRowNum());
                    currentIndex = mSoftKeyboard.getSelectIndex();
                    currentRow = mSoftKeyboard.getSelectRow();

                    if (mSoftKeyboard.isTBMove() && softKey == null) {
                        softKey = mSoftKeyboard.getMoveDownSoftKey(0, mSoftKeyboard.getSelectRow());
                        currentIndex = mSoftKeyboard.getSelectIndex();
                        currentRow = mSoftKeyboard.getSelectRow();
                    }
                    /**
                     * 区域查找,没有查找到的话<br>
                     * 根据行,列查找.
                     */
                    if (softKey == null) {
                        currentRow++;
                        if (currentRow > (mSoftKeyboard.getRowNum() - 1)) {
                            if (!mSoftKeyboard.isTBMove()) {
                                return false;
                            }
                            // 判断是否可以上下(最后一个是否可以向下移动到第一个).
                            if (mSoftKeyboard.isTBMove()) {
                                currentRow = 0;
                            } else {
                                currentRow = (mSoftKeyboard.getRowNum() - 1);
                            }
                        }
                        // 防止重复刷新.
                        if (softKey == null && currentRow != mSoftKeyboard.getSelectRow()) {
                            keyRow = mSoftKeyboard.getKeyRowForDisplay(currentRow);
                            softKeys = keyRow.getSoftKeys();
                            currentIndex = Math.max(Math.min(currentIndex, softKeys.size() - 1), 0);
                            softKey = softKeys.get(currentIndex);
                        }
                    }
                    // 保存下个方向的按键.
                    if (softKey != null) {
                        selectKey.setNextBottomKey(softKey, currentRow, currentIndex);
                    }
                }
                break;
            case KeyEvent.KEYCODE_DPAD_UP:
                // 快速查询按键.
                SaveSoftKey saveTSoftKey = selectKey.getNextTopKey();
                softKey = saveTSoftKey.key;
                if (softKey != null) {
                    currentIndex = saveTSoftKey.index;
                    currentRow = saveTSoftKey.row;
                } else {
                    softKey = mSoftKeyboard.getMoveUpSoftKey(mSoftKeyboard.getSelectRow() - 1, 0);
                    currentIndex = mSoftKeyboard.getSelectIndex();
                    currentRow = mSoftKeyboard.getSelectRow();

                    if (mSoftKeyboard.isTBMove() && softKey == null) {
                        softKey = mSoftKeyboard.getMoveUpSoftKey(mSoftKeyboard.getRowNum() - 1,
                                mSoftKeyboard.getSelectRow() + 1);
                        currentIndex = mSoftKeyboard.getSelectIndex();
                        currentRow = mSoftKeyboard.getSelectRow();
                        /**
                         * 区域查找未找到的情况下.
                         */
                        if (softKey == null) {
                            currentRow--;
                            if (currentRow < 0) {
                                if (!mSoftKeyboard.isTBMove()) {
                                    return false;
                                }
                                if (mSoftKeyboard.isTBMove()) {
                                    currentRow = (mSoftKeyboard.getRowNum() - 1);
                                } else {
                                    currentRow = 0;
                                }
                            }
                            // 防止重复刷新.
                            if (softKey == null && currentRow != mSoftKeyboard.getSelectRow()) {
                                keyRow = mSoftKeyboard.getKeyRowForDisplay(currentRow);
                                softKeys = keyRow.getSoftKeys();
                                currentIndex = Math.max(Math.min(currentIndex, softKeys.size() - 1), 0);
                                softKey = softKeys.get(currentIndex);
                            }
                        }
                    }
                    // 保存下个方向的按键.
                    if (softKey != null) {
                        selectKey.setNextTopKey(softKey, currentRow, currentIndex);
                    }
                }
                break;
            default:
                break;
        }
        // 刷新移动的位置.
        if (softKey != null) {
            SoftKey oldsoftkey = mSoftKeyboard.getSelectSoftKey();
            mSoftKeyboard.setOneKeySelected(softKey);
            mSoftKeyboard.setSelectRow(currentRow);
            mSoftKeyboard.setSelectIndex(currentIndex);
            if (mOnKeyBoardAnimListener != null) {
                if (mOnKeyBoardAnimListener.onKeyBoardAnim(this, oldsoftkey, softKey)) {
                    return true;
                }
            }
            if (!mIsMoveRect) {
                if (mScale == 1.0) {
                    invalidate(softKey.getRect()); // 优化绘制区域.
                } else { // 放大.
                    scaleAnim(oldsoftkey, softKey);
                }
            } else {
                setMoveSoftKeyAnimation(oldsoftkey, softKey); // 移动边框.
            }
        }
        return true;
    }

    private void setMoveSoftKeyAnimation(final SoftKey oldSoftkey, final SoftKey targetSoftKey) {
        PropertyValuesHolder valuesXHolder = PropertyValuesHolder.ofFloat("Left", oldSoftkey.getLeft(), targetSoftKey.getLeft());
        PropertyValuesHolder valuesX1Holder = PropertyValuesHolder.ofFloat("Right", oldSoftkey.getRight(), targetSoftKey.getRight());
        PropertyValuesHolder valuesYHolder = PropertyValuesHolder.ofFloat("Top", oldSoftkey.getTop(), targetSoftKey.getTop());
        PropertyValuesHolder valuesY1Holder = PropertyValuesHolder.ofFloat("Bottom", oldSoftkey.getBottom(), targetSoftKey.getBottom());
        final ObjectAnimator scaleAnimator = ObjectAnimator.ofPropertyValuesHolder(targetSoftKey, valuesXHolder, valuesX1Holder, valuesYHolder, valuesY1Holder);
        scaleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float left = (float) animation.getAnimatedValue("Left");
                float top = (float) animation.getAnimatedValue("Top");
                float right = (float) animation.getAnimatedValue("Right");
                float bottom = (float) animation.getAnimatedValue("Bottom");
                SoftKey softKey = (SoftKey) scaleAnimator.getTarget();
                softKey.setMoveLeft(left);
                softKey.setMoveTop(top);
                softKey.setMoveRight(right);
                softKey.setMoveBottom(bottom);
                postInvalidate();
            }
        });
        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.playTogether(scaleAnimator);
        animatorSet.setDuration(mMoveDuration);
        animatorSet.start();
    }

    private float mScale = 1.0f;

    public void setKeyScale(float scale) {
        this.mScale = scale;
    }

    private void scaleAnim(final SoftKey oldSoftkey, final SoftKey targetSoftKey) {
        try {
            PropertyValuesHolder valuesY1Holder = PropertyValuesHolder.ofFloat("width", targetSoftKey.getWidth(), targetSoftKey.getWidth() * mScale);
            PropertyValuesHolder valuesY2Holder = PropertyValuesHolder.ofFloat("height", targetSoftKey.getHeight(), targetSoftKey.getHeight() * mScale);
            invalidate();
            final ObjectAnimator scaleAnimator = ObjectAnimator.ofPropertyValuesHolder(targetSoftKey, valuesY1Holder, valuesY2Holder);
            scaleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    float width = (float) animation.getAnimatedValue("width");
                    float height = (float) animation.getAnimatedValue("height");
                    SoftKey softKey = (SoftKey) scaleAnimator.getTarget();
                    softKey.setWidth(width);
                    softKey.setHeight(height);
                    postInvalidate();
                }
            });
            AnimatorSet animatorSet = new AnimatorSet();
            animatorSet.playTogether(scaleAnimator);
            animatorSet.setDuration(mMoveDuration);
            animatorSet.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public interface OnKeyBoardAnimListener {
        public boolean onKeyBoardAnim(final SoftKeyboardView softKeyboardView, final SoftKey oldSoftkey, final SoftKey targetSoftKey);
    }

    /**
     *  设置动画回调.
     */
    public void setOnKeyBoardAnimListener(OnKeyBoardAnimListener listener) {
        this.mOnKeyBoardAnimListener = listener;
    }

    private OnKeyBoardAnimListener mOnKeyBoardAnimListener;
    private static final int DEFAULT_MOVE_DURATION = 300;
    private boolean mIsMoveRect = false;
    private int mMoveDuration = DEFAULT_MOVE_DURATION;
    private boolean mIsFront = false;

    /**
     * 设置按键边框的移动时间.
     */
    public void setMoveDuration(int moveDuration) {
        this.mMoveDuration = moveDuration;
    }

    /**
     * 是否移动按键的边框.
     */
    public void setMoveSoftKey(boolean isMoveRect) {
        this.mIsMoveRect = isMoveRect;
    }

    /**
     * 设置选中的按键边框在最前面还是最后面
     *
     * @param isFront true 最前面, false 反之 (默认为 false).
     */
    public void setSelectSofkKeyFront(boolean isFront) {
        this.mIsFront = isFront;
        postInvalidate();
    }

}