package com.wasu.widgets.focuswidget;

import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.FocusFinder;
import android.view.KeyEvent;
import android.view.View;

import com.wasu.widgets.tools.AnimTools;
import com.wasu.widgets.tools.UIUtil;

import cn.com.wasu.main.R;

/**
 * SpanFocusRecyclerView
 * @Description: 也是绘制了焦点的recyclerView，但是此空间可以在adapter的onCreateViewHolder方法中
 *               设置itemView所占的行数和列数，本空间不支持用代码设置adapter，只能在xml文件中给出
 * @Author: Danxingxi
 * @CreateDate: 2016/8/16 21:14
 */
public class TvRecyclerView extends RecyclerView implements IRecyclerView {
    private static final String TAG = TvRecyclerView.class.getSimpleName();

    /**焦点框**/
    Drawable focusShadowDrawable = null;
    /**获得焦点的item**/
    View focusView = null;
    /**优化TvRecyclerView在滚动时焦点的绘制*/
    private View  canvasView;
    /**优化TvRecyclerView在滚动时焦点的绘制次数*/
    int drawCount = 1;
    public TvRecyclerView(Context context) {
        this(context, null);
    }

    public TvRecyclerView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

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

    private void init(Context context){
        setChildrenDrawingOrderEnabled(true);
        setFocusable(false);
        setClipChildren(false);
        setClipToPadding(false);
        setWillNotDraw(true);
        setHasFixedSize(true);
        addItemDecoration(new ItemDecoration() {
            @Override
            public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
                super.getItemOffsets(outRect, view, parent, state);
//                outRect.left = getResources().getDimensionPixelSize(com.wasu.module.wechattv.R.dimen.d_16dp);
                outRect.left= getResources().getDimensionPixelSize(R.dimen.d_12dp);
                outRect.top = getResources().getDimensionPixelSize(R.dimen.d_8dp);

            }
        });
    }

    public boolean isVertical() {
        if (getLayoutManager() instanceof LinearLayoutManager) {
            LinearLayoutManager layout = (LinearLayoutManager) getLayoutManager();
            return layout.getOrientation() == LinearLayoutManager.VERTICAL;
        }
        return true;
    }
    
    private int getFreeSize() {
        if(!isVertical()) {
            return getFreeHeight();
        } else {
            return getFreeWidth();
        }
    }
    
    private int getFreeHeight() {
        return getHeight() - getPaddingTop() - getPaddingBottom();
    }
    
    private int getFreeWidth() {
        return getWidth() - getPaddingLeft() - getPaddingRight();
    }

/*
    /**
     * center scroll
     * @param child
     * @param rect
     * @param immediate
     * @return
     *//*
    @Override
    public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate) {
        final int parentLeft = getPaddingLeft();
        final int parentTop = getPaddingTop();
        final int parentRight = getWidth() - getPaddingRight();
        final int parentBottom = getHeight() - getPaddingBottom();
        final int childLeft = child.getLeft() + rect.left;
        final int childTop = child.getTop() + rect.top;
        final int childRight = childLeft + rect.width();
        final int childBottom = childTop + rect.height();

        final int offScreenLeft = Math.min(0, childLeft - parentLeft);
        final int offScreenTop = Math.min(0, childTop - parentTop);
        final int offScreenRight = Math.max(0, childRight - parentRight);
        final int offScreenBottom = Math.max(0, childBottom - parentBottom);

        int centerTop = getHeight() / 2 - child.getHeight() / 2;
        int centerBottom = getHeight() / 2 + child.getHeight() / 2;
        int centerLeft = getWidth() / 2 - child.getWidth() / 2;
        int centerRight = getWidth() / 2 + child.getWidth() / 2;

        int position = getChildAdapterPosition(child);
        int childCount = getAdapter().getItemCount();

        int dx = 0;
        if (position == 0 || position == childCount - 1){
            // Favor the "start" layout direction over the end when bringing one side or the other
            // of a large rect into view. If we decide to bring in end because start is already
            // visible, limit the scroll such that start won't go out of bounds.
            if (ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_RTL) {
                dx = offScreenRight != 0 ? offScreenRight
                        : Math.max(offScreenLeft, childRight - parentRight);
            } else {
                dx = offScreenLeft != 0 ? offScreenLeft
                        : Math.min(childLeft - parentLeft, offScreenRight);
            }
        }else {
            switch (Gravity.CENTER_HORIZONTAL){
                case Gravity.CENTER_HORIZONTAL:
                case Gravity.CENTER:
                    dx = childLeft - centerLeft;
                    break;
                case Gravity.LEFT:
                    dx = childLeft - parentLeft;
                    break;
            }
        }

        int dy = 0;
        if (position == 0 || position == childCount - 1){
            // Favor bringing the top into view over the bottom. If top is already visible and
            // we should scroll to make bottom visible, make sure top does not go out of bounds.
            dy = offScreenTop != 0 ? offScreenTop
                    : Math.min(childTop - parentTop, offScreenBottom);
        }else {
            switch (gravity){
                case Gravity.CENTER_VERTICAL:
                case Gravity.CENTER:
                    dy = childTop - centerTop;
                    break;
            }
        }

        if (dx != 0 || dy != 0) {
            if (immediate) {
                scrollBy(dx, dy);
            } else {
                smoothScrollBy(dx, dy);
            }
            return true;
        }
        return false;
    }*/

    @Override
    public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate) {
        final int parentLeft = getPaddingLeft();
        final int parentTop = getPaddingTop();
        final int parentRight = getWidth() - getPaddingRight();
        final int parentBottom = getHeight() - getPaddingBottom();
        final int childLeft = child.getLeft() + rect.left;
        final int childTop = child.getTop() + rect.top;
        final int childRight = childLeft + rect.width();
        final int childBottom = childTop + rect.height();

        final int offScreenLeft = Math.min(0, childLeft - parentLeft );
        final int offScreenTop = Math.min(0, childTop - parentTop );
        final int offScreenRight = Math.max(0, childRight - parentRight );
        final int offScreenBottom = Math.max(0, childBottom - parentBottom );

        final boolean canScrollHorizontal = getLayoutManager().canScrollHorizontally();
        final boolean canScrollVertical = getLayoutManager().canScrollVertically();

        // Favor the "start" layout direction over the end when bringing one side or the other
        // of a large rect into view. If we decide to bring in end because start is already
        // visible, limit the scroll such that start won't go out of bounds.
        int dx;
        if(canScrollHorizontal) {
            if (ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_RTL) {
                dx = offScreenRight != 0 ? offScreenRight
                        : Math.max(offScreenLeft, childRight - parentRight);
            } else {
                dx = offScreenLeft != 0 ? offScreenLeft
                        : Math.min(childLeft - parentLeft, offScreenRight);
            }
            //增加预滚动
            if(dx > 0){
                dx += dx/3;
            }else if(dx <0) {
                dx += dx/3;
            }
        } else {
            dx = 0;
        }

        // Favor bringing the top into view over the bottom. If top is already visible and
        // we should scroll to make bottom visible, make sure top does not go out of bounds.
        int dy;
        if(canScrollVertical) {
            dy = offScreenTop != 0 ? offScreenTop : Math.min(childTop - parentTop, offScreenBottom);
            //增加预滚动
        } else {
            dy = 0;
        }

        if (dx != 0 || dy != 0) {
            if (immediate) {
                scrollBy(dx, dy);
            } else {
                smoothScrollBy(dx, dy);
            }
            return true;
        }

        // 重绘是为了选中item置顶，具体请参考getChildDrawingOrder方法
        postInvalidate();

        return false;
    }

    public int getFirstVisiblePosition() {
        if(getChildCount() == 0)
            return 0;
        else
            return getChildLayoutPosition(getChildAt(0));
    }

    public int getLastVisiblePosition() {
        final int childCount = getChildCount();
        if(childCount == 0)
            return 0;
        else
            return getChildLayoutPosition(getChildAt(childCount - 1));
    }

    @Override
    public void modifyUI(View focusView) {
        this.focusView = focusView;
        //要是需要支持焦点放大就需要注释掉全局刷新，使用局部刷新就可以了
        postInvalidate();
    }

    /***
     * 更新UI并重绘
     * @param focusView  焦点Item
     * @param position   Item的位置
     */
    @Override
    public void modifyUI(View focusView, int position) {
        this.focusView = focusView;
        //要是需要支持焦点放大就需要注释掉全局刷新，使用局部刷新就可以了
        postInvalidate();
    }
    /**
     * 画焦点框，由于recyclerView的滚动会导致这个方法重复调用，焦点绘制也会存在过渡绘制
     * @param canvas
     */
    @Override
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);
        if (focusView != null) {
            if(isScrolling()){
                if(canvasView!=null){
                    if (focusShadowDrawable == null) {
                        focusShadowDrawable = getContext().getResources().getDrawable(R.drawable.focus_border);
                    }
        //            Rect toFocusedViewRect = UIUtil.createViewRect(this, focusView.findViewById(R.id.img_view), 0);
                    Rect toFocusedViewRect = UIUtil.createViewRect(this, canvasView, 0);
                    UIUtil.drawDrawableAt(canvas, toFocusedViewRect, focusShadowDrawable);
                    if(drawCount>=2){
                        canvasView = null;
                        drawCount = 1;
                    }else{
                        ++drawCount;
                    }
                    return;
                }
            }
            if (focusShadowDrawable == null) {
                focusShadowDrawable = getContext().getResources().getDrawable(R.drawable.focus_border);
            }
//            canvasView = focusView;
//            Rect toFocusedViewRect = UIUtil.createViewRect(this, focusView.findViewById(R.id.img_view), 0);
            Rect toFocusedViewRect = UIUtil.createViewRect(this, focusView, 0);
            UIUtil.drawDrawableAt(canvas, toFocusedViewRect, focusShadowDrawable);
        }
    }
    int position = 0;
    @Override
    protected int getChildDrawingOrder(int childCount, int i) {
        View view = getFocusedChild();
        if(null != view) {
            position = getChildAdapterPosition(view) - getFirstVisiblePosition();
            if (position < 0) {
                return i;
            } else {
                if (i == childCount - 1) {//这是最后一个需要刷新的item
                    if (position > i) {
                        position = i;
                    }
                    return position;
                }
                if (i == position) {//这是原本要在最后一个刷新的item
                    return childCount - 1;
                }
            }
        }
        return i;
    }

    public boolean isScrolling() {
        return getScrollState() == SCROLL_STATE_SETTLING;
    }

    private ItemViewFocusSearchListener itemViewFocusSearchListener;

    @Override
    public void setItemViewFocusSearchListener(ItemViewFocusSearchListener itemViewFocusSearchListener){
        this.itemViewFocusSearchListener = itemViewFocusSearchListener;
    }

    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        if(focusView != null){
            int itemCount = getLayoutManager().getItemCount();
            if(itemViewFocusSearchListener != null){
                if(itemViewFocusSearchListener.onItemViewFocusSearch(this, focusView, itemCount, getChildAdapterPosition(focusView), event)){
                    return true;
                }
            }
        }
        switch (event.getAction()) {
            case KeyEvent.ACTION_DOWN:
                if(onKeyDown(event.getKeyCode(), event)) {
                    return true;
                }
                break;
            case KeyEvent.ACTION_UP:
                if(onKeyUp(event.getKeyCode(), event)) {
                    return true;
                }
                break;
        }
        return super.dispatchKeyEvent(event);
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        int direction = -1;
        switch (keyCode){
            case KeyEvent.KEYCODE_DPAD_DOWN:
                direction = FOCUS_DOWN;
                break;
            case KeyEvent.KEYCODE_DPAD_RIGHT:
                direction = FOCUS_RIGHT;
                break;
            case KeyEvent.KEYCODE_DPAD_LEFT:
                direction = FOCUS_LEFT;
                break;
            case KeyEvent.KEYCODE_DPAD_UP:
                direction = FOCUS_UP;
                break;
        }

        if(direction == -1 || hasInBorder(direction)) {
            return false;
        } else {
            FocusFinder ff = FocusFinder.getInstance();
            View newFocusedView = ff.findNextFocus(this, getFocusedChild(), direction);
            if (null != newFocusedView) {
                newFocusedView.requestFocus();
            }
        }
        return true;
    }

    /**
     * 是否到了边界
     * @param direction
     * @return
     */
    private boolean hasInBorder(int direction) {
        boolean result = false;
        final View view = getFocusedChild();
        if(null != view) {
            Rect outRect = new Rect();
            LayoutParams lp = (LayoutParams) view.getLayoutParams();
            getLayoutManager().calculateItemDecorationsForChild(view, outRect);
            switch (direction) {
                case FOCUS_DOWN:
                    result = getHeight() - view.getBottom() <= getPaddingBottom() + lp.bottomMargin + outRect.bottom;
                    if(isVertical()) {
                        result = result && getLastVisiblePosition() == (getAdapter().getItemCount() - 1);
                    }
                    break;
                case FOCUS_UP:
                    result = view.getTop() <= getPaddingTop() + lp.topMargin + outRect.top;
                    if(isVertical()) {
                        result = result && getFirstVisiblePosition() == 0;
                    }
                    break;
                case FOCUS_LEFT:
                    result = view.getLeft() <= getPaddingLeft() + lp.leftMargin + outRect.left;
                    if(!isVertical()) {
                        result = result && getFirstVisiblePosition() == 0;
                    }
                    break;
                case FOCUS_RIGHT:
                    result = getWidth() - view.getRight() <= getPaddingRight() + lp.rightMargin + outRect.right;
                    if(!isVertical()) {
                        result = result && getLastVisiblePosition() == (getAdapter().getItemCount() - 1);
                    }
                    break;
            }
        }
        return result;
    }

    @Override
    public boolean isInTouchMode() {
        boolean result = super.isInTouchMode();
        // 解决4.4版本抢焦点的问题
        if (Build.VERSION.SDK_INT == 19) {
            return !(hasFocus() && !result);
        } else {
            return result;
        }
    }

    /**
     * itemView晃动动画
     * @param view
     * @return
     */
    @Override
    public void shockAnim(View view){
        this.focusView=view;
        ObjectAnimator animator= AnimTools.shock(focusView);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                postInvalidate();
            }
        });
        animator.start();
    }
}
