/*
 * Copyright 2015 Hippo Seven
 *
 * 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 com.hippo.nimingban.widget;

import android.animation.ValueAnimator;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.support.annotation.IntDef;
import android.support.v4.view.MotionEventCompat;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.FrameLayout;

import com.hippo.nimingban.R;
import com.hippo.util.AnimationUtils2;
import com.hippo.yorozuya.LayoutUtils;
import com.hippo.yorozuya.MathUtils;
import com.hippo.yorozuya.ViewUtils;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

public final class PostLayout extends FrameLayout {

    @IntDef({STATE_NONE, STATE_SHOW, STATE_HIDE})
    @Retention(RetentionPolicy.SOURCE)
    private @interface State {}

    public static final int STATE_NONE = 0;
    public static final int STATE_SHOW = 1;
    public static final int STATE_HIDE = 2;

    private ViewDragHelper mDragHelper;

    private Drawable mShadowTop;

    private int mShadowHeight;

    private float mX;
    private float mY;

    private int mThreshold;

    private ValueAnimator mHideTypeSendAnimation;
    private ValueAnimator mShowTypeSendAnimation;

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

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

    public PostLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    private void init(Context context) {
        mDragHelper = ViewDragHelper.create(this, 1.0f, new DragHelperCallback());
        mShadowTop = context.getResources().getDrawable(R.drawable.shadow_top);
        mShadowHeight = LayoutUtils.dp2pix(context, 8);
        mThreshold = LayoutUtils.dp2pix(context, 48);

        mHideTypeSendAnimation = new ValueAnimator();
        mHideTypeSendAnimation.setDuration(300);
        mHideTypeSendAnimation.setInterpolator(AnimationUtils2.FAST_SLOW_INTERPOLATOR);
        mHideTypeSendAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                View view = getChildAt(1);
                if (view != null) {
                    int value = (Integer) animation.getAnimatedValue();
                    view.offsetTopAndBottom(value - view.getTop());
                    ((LayoutParams) view.getLayoutParams()).offsetY = value;
                    invalidate();
                }
            }
        });

        mShowTypeSendAnimation = new ValueAnimator();
        mShowTypeSendAnimation.setDuration(300);
        mShowTypeSendAnimation.setInterpolator(AnimationUtils2.FAST_SLOW_INTERPOLATOR);
        mShowTypeSendAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                View view = getChildAt(1);
                if (view != null) {
                    int value = (Integer) animation.getAnimatedValue();
                    view.offsetTopAndBottom(value - view.getTop());
                    ((LayoutParams) view.getLayoutParams()).offsetY = value;
                    invalidate();
                }
            }
        });
    }

    @State
    public int getTypeSendState() {
        View typeSendView = getChildAt(1);
        if (typeSendView == null) {
            return STATE_NONE;
        } else {
            LayoutParams lp = (LayoutParams) typeSendView.getLayoutParams();
            return lp.hide ? STATE_HIDE : STATE_SHOW;
        }
    }

    public void onRemoveTypeSend() {
        // Reset post view bottom margin
        View postView = getChildAt(0);
        if (postView != null) {
            LayoutParams lp = (LayoutParams) postView.getLayoutParams();
            lp.bottomMargin = 0;
            postView.setLayoutParams(lp);
        }
    }

    public void hideTypeSend() {
        mHideTypeSendAnimation.cancel();
        mShowTypeSendAnimation.cancel();

        View typeSendView = getChildAt(1);
        if (typeSendView == null) {
            return;
        }

        LayoutParams lp = (LayoutParams) typeSendView.getLayoutParams();
        lp.hide = true;

        int start = typeSendView.getTop();
        int end = getHeight() - typeSendView.findViewById(R.id.toolbar).getHeight();
        if (start != end) {
            mHideTypeSendAnimation.setIntValues(start, end);
            mHideTypeSendAnimation.start();
        }

        // Add post view bottom margin
        View postView = getChildAt(0);
        if (postView != null) {
            lp = (LayoutParams) postView.getLayoutParams();
            lp.bottomMargin = typeSendView.findViewById(R.id.toolbar).getHeight();
            postView.setLayoutParams(lp);
        }
    }

    public void showTypeSend() {
        mHideTypeSendAnimation.cancel();
        mShowTypeSendAnimation.cancel();

        View typeSendView = getChildAt(1);
        if (typeSendView == null) {
            return;
        }

        LayoutParams lp = (LayoutParams) typeSendView.getLayoutParams();
        lp.hide = false;

        int start = typeSendView.getTop();
        int end = 0;
        if (start != end) {
            mShowTypeSendAnimation.setIntValues(start, end);
            mShowTypeSendAnimation.start();
        }
    }

    @Override
    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
        boolean result = super.drawChild(canvas, child, drawingTime);
        int top = child.getTop();
        if (1 == indexOfChild(child) && top > 0) {
            mShadowTop.setBounds(0, top - mShadowHeight, child.getRight(), top);
            mShadowTop.draw(canvas);
        }
        return result;
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);

        for (int i = 0, count = getChildCount(); i < count; i++) {
            View child = getChildAt(i);
            if (GONE == child.getVisibility()) {
                continue;
            }

            LayoutParams lp = (LayoutParams) child.getLayoutParams();
            child.offsetLeftAndRight(lp.offsetX);
            child.offsetTopAndBottom(lp.offsetY);
        }
    }

    // ime keyboard may change window size
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        View typeSendView = getChildAt(1);
        if (typeSendView != null) {
            LayoutParams lp = (LayoutParams) typeSendView.getLayoutParams();
            if (lp.hide) {
                mHideTypeSendAnimation.cancel();
                mShowTypeSendAnimation.cancel();

                int top = getHeight() - typeSendView.findViewById(R.id.toolbar).getHeight();
                typeSendView.offsetTopAndBottom(top - typeSendView.getTop());
                lp.offsetY = top;
            }
        }
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        mX = ev.getX();
        mY = ev.getY();
        final int action = MotionEventCompat.getActionMasked(ev);
        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
            mDragHelper.cancel();
            return false;
        } else {
            return mDragHelper.shouldInterceptTouchEvent(ev);
        }
    }

    private void handleTypeSendState() {
        View view = getChildAt(1);
        if (view == null) {
            return;
        }

        int top = view.getTop();
        LayoutParams lp = (LayoutParams) view.getLayoutParams();
        if (lp.hide) {
            if (top < getHeight() - view.findViewById(R.id.toolbar).getHeight() - mThreshold) {
                showTypeSend();
            } else {
                hideTypeSend();
            }
        } else {
            if (top > mThreshold) {
                hideTypeSend();
            } else {
                showTypeSend();
            }
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        mX = ev.getX();
        mY = ev.getY();
        mDragHelper.processTouchEvent(ev);

        final int action = MotionEventCompat.getActionMasked(ev);
        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
            handleTypeSendState();
        }

        return true;
    }

    private class DragHelperCallback extends ViewDragHelper.Callback {

        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            if (child.getId() == R.id.type_send) {
                View view = child.findViewById(R.id.toolbar);
                if (view != null) {
                    return ViewUtils.isViewUnder(view, (int) mX, (int) mY - child.getTop(), 0);
                }
            }
            return false;
        }

        @Override
        public int clampViewPositionVertical(View child, int top, int dy) {
            if (child.getId() == R.id.type_send) {
                View view = child.findViewById(R.id.toolbar);
                if (view != null) {
                    return MathUtils.clamp(top, 0, getHeight() - view.getHeight());
                }
            }
            return top;
        }

        @Override
        public int getViewVerticalDragRange(View child) {
            if (child.getId() == R.id.type_send) {
                View view = child.findViewById(R.id.toolbar);
                if (view != null) {
                    return getHeight() - view.getHeight();
                }
            }
            return 0;
        }

        @Override
        public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
            if (changedView.getId() == R.id.type_send) {
                LayoutParams lp = (LayoutParams) changedView.getLayoutParams();
                lp.offsetY = top;
            }

            // Hide ime keyboard
            Context context = getContext();
            if (context instanceof Activity) {
                View focusView = ((Activity) context).getCurrentFocus();
                if (focusView != null) {
                    InputMethodManager imm = (InputMethodManager)
                            getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
                    imm.hideSoftInputFromWindow(focusView.getWindowToken(), 0);
                }
            }

            invalidate();
        }
    }

    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new LayoutParams(getContext(), attrs);
    }

    @Override
    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
        return p instanceof LayoutParams;
    }

    @Override
    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
        return new LayoutParams(p);
    }

    public static class LayoutParams extends FrameLayout.LayoutParams {

        public int offsetX = 0;
        public int offsetY = 0;
        public boolean hide = false;

        public LayoutParams(Context c, AttributeSet attrs) {
            super(c, attrs);
        }

        public LayoutParams(int width, int height) {
            super(width, height);
        }

        public LayoutParams(int width, int height, int gravity) {
            super(width, height, gravity);
        }

        public LayoutParams(ViewGroup.LayoutParams source) {
            super(source);
        }

        public LayoutParams(MarginLayoutParams source) {
            super(source);
        }
    }
}