package com.adealink.weparty.commonui.widget.slide

import android.content.Context
import android.os.Bundle
import android.view.Gravity
import android.view.MotionEvent
import android.view.View
import android.view.View.OnTouchListener
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import androidx.annotation.IntDef
import androidx.core.view.children
import com.adealink.frame.ext.doOnGlobalLayout
import com.adealink.frame.util.isTouchPointInView
import com.adealink.weparty.commonui.widget.slide.touch.SlideTouchConsumer

class Slide(private val mBuilder: SlideBuilder) : OnTouchListener, SlideListener {

    enum class SlideDirection(val dir: Int) {
        UP(0x0001),
        DOWN(0x0010),
        VERTICAL(0x0011),
        LEFT(0x0100),
        RIGHT(0x1000),
        HORIZONTAL(0x1100);

        fun isSameDir(dir: SlideDirection): Boolean {
            when (this) {
                UP -> {
                    return dir == UP
                }

                DOWN -> {
                    return dir == DOWN
                }

                VERTICAL -> {
                    return dir == UP || dir == DOWN
                }

                LEFT -> {
                    return dir == LEFT
                }

                RIGHT -> {
                    return dir == RIGHT
                }

                HORIZONTAL -> {
                    return dir == LEFT || dir == RIGHT
                }
            }
        }

        companion object {
            @JvmStatic
            fun isUp(dir: Int): Boolean {
                return (UP.dir and dir) > 0
            }

            @JvmStatic
            fun isDown(dir: Int): Boolean {
                return (DOWN.dir and dir) > 0
            }

            @JvmStatic
            fun isLeft(dir: Int): Boolean {
                return (LEFT.dir and dir) > 0
            }

            @JvmStatic
            fun isRight(dir: Int): Boolean {
                return (RIGHT.dir and dir) > 0
            }
        }
    }

    enum class SlideTo {
        SELF,  //滑动自身的高度
        PARENT,  //滑动到父布局
        SPECIFY //指定滑动距离
    }


    @IntDef(value = [Gravity.START, Gravity.END, Gravity.TOP, Gravity.BOTTOM])
    @Retention(AnnotationRetention.SOURCE)
    internal annotation class StartVector

    private var mViewHeight = 0f
    private var mViewWidth = 0f

    private var touchConsumer: SlideTouchConsumer? = null

    /**
     *
     * Interface to listen to all handled events taking place in the slider
     */
    interface Listener {
        /**
         * @param percent percents of complete slide **(100 = HIDDEN, 0 = SHOWED)**
         */
        fun onSlide(percent: Float)

        fun onSlideFinish(slideToEnd: Boolean)
    }

    init {
        init()
    }

    private fun init() {
//        mBuilder.mSliderView.setOnTouchListener(this)
        mBuilder.mSliderView.doOnGlobalLayout {
            mViewHeight = mBuilder.mSliderView.height.toFloat()
            mViewWidth = mBuilder.mSliderView.width.toFloat()
            when (mBuilder.mSlideDirection) {
                SlideDirection.UP,
                SlideDirection.DOWN,
                SlideDirection.VERTICAL -> {
                    mBuilder.mSliderView.pivotY = 0f
                }

                SlideDirection.LEFT,
                SlideDirection.RIGHT,
                SlideDirection.HORIZONTAL -> {
                    mBuilder.mSliderView.pivotX = 0f
                }
            }
            createConsumers()
        }
    }

    private fun createConsumers() {
        touchConsumer = SlideTouchConsumer(
            mBuilder,
            this,
            slideUpLength = getSlideLength(SlideDirection.UP),
            slideDownLength = getSlideLength(SlideDirection.DOWN),
            slideLeftLength = getSlideLength(SlideDirection.LEFT),
            slideRightLength = getSlideLength(SlideDirection.RIGHT)
        )
    }

    private fun getSlideLength(dir: SlideDirection): Float {
        when (mBuilder.mSlideTo) {
            SlideTo.SELF -> return mViewHeight
            SlideTo.PARENT -> when {
                SlideDirection.isUp(dir.dir) -> {
                    val parent = mBuilder.mSliderView.parent
                    if (parent != null) {
                        return mBuilder.mSliderView.top.toFloat() + mViewHeight
                    }
                    return 0f
                }

                SlideDirection.isDown(dir.dir) -> {
                    val parent = mBuilder.mSliderView.parent
                    if (parent != null) {
                        return ((parent as ViewGroup).height - mBuilder.mSliderView.bottom).toFloat() + mViewHeight
                    }
                    return 0f
                }

                SlideDirection.isLeft(dir.dir) -> {
                    val parent = mBuilder.mSliderView.parent
                    if (parent != null) {
                        return mBuilder.mSliderView.left.toFloat() + mViewWidth
                    }
                }

                SlideDirection.isRight(dir.dir) -> {
                    val parent = mBuilder.mSliderView.parent
                    if (parent != null) {
                        return ((parent as ViewGroup).width - mBuilder.mSliderView.right).toFloat() + mViewWidth
                    }
                    return 0f
                }
            }

            SlideTo.SPECIFY -> return mBuilder.mSpecifySlideTo.toFloat()
        }
        return 0f
    }

    //region public interface
    /**
     *
     * Trying hide soft input from window
     *
     * @see InputMethodManager.hideSoftInputFromWindow
     */
    fun hideSoftInput() {
        (mBuilder.mSliderView.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager)
            .hideSoftInputFromWindow(mBuilder.mSliderView.windowToken, InputMethodManager.HIDE_NOT_ALWAYS)
    }

    /**
     *
     * Trying show soft input to window
     *
     * @see InputMethodManager.showSoftInput
     */
    fun showSoftInput() {
        (mBuilder.mSliderView.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager)
            .showSoftInput(mBuilder.mSliderView, 0)
    }

    val isVisible: Boolean
        /**
         *
         * Returns the visibility status for this view.
         *
         * @return true if view have status [View.VISIBLE]
         */
        get() = mBuilder.mSliderView.visibility == View.VISIBLE

    /**
     *
     * Add Listener which will be used in combination with this SlideUp
     */
    fun addSlideListener(listener: Listener) {
        mBuilder.mListeners.add(listener)
    }

    /**
     *
     * Remove Listener which was used in combination with this SlideUp
     */
    fun removeSlideListener(listener: Listener) {
        mBuilder.mListeners.remove(listener)
    }

    /**
     *
     * Returns typed view which was used as slider
     */
    fun <T : View?> getSliderView(): T? {
        return mBuilder.mSliderView as T
    }

    val autoSlideDuration: Float
        /**
         *
         * Returns duration of animation (whenever you use [.hide] or [.show] methods)
         */
        get() = mBuilder.mAutoSlideDuration.toFloat()

    /**
     *
     * Saving current parameters of SlideUp
     */
    fun onSaveInstanceState(savedState: Bundle) {
        savedState.putBoolean(KEY_STATE_SAVED, true)
        savedState.putSerializable(KEY_START_DIRECTION, mBuilder.mSlideDirection)
        savedState.putInt(KEY_AUTO_SLIDE_DURATION, mBuilder.mAutoSlideDuration)
        savedState.putBoolean(KEY_HIDE_SOFT_INPUT, mBuilder.mHideKeyboard)
    }

    override fun onTouch(v: View, event: MotionEvent): Boolean {
        if (touchConsumer?.isAnimationRunning() == true) return true
        val consumed = touchConsumer?.onTouch(v, event) ?: false
//        if (!consumed) {
//            performClick(event)
//        }
        return true
    }

    override fun performClick(event: MotionEvent) {
        findClickableChild(mBuilder.mSliderView, event)?.performClick()
    }

    private fun findClickableChild(view: View, event: MotionEvent): View? {
        if (view is ViewGroup) {
            for (child in view.children) {
                val clickView = findClickableChild(child, event)
                if (clickView != null) {
                    return clickView
                }
            }
        }
        if (view.isClickable) {
            if (isTouchPointInView(view, event.rawX.toInt(), event.rawY.toInt())) {
                return view
            }
        }
        return null
    }

    override fun notifySlideFinish(slideToEnd: Boolean) {
        if (mBuilder.mListeners.isNotEmpty()) {
            for (listener in mBuilder.mListeners) {
                listener?.onSlideFinish(slideToEnd)
            }
        }
    }

    override fun notifyPercentChanged(percent: Float, dir: SlideDirection) {
        if (mBuilder.mListeners.isNotEmpty()) {
            for (listener in mBuilder.mListeners) {
                listener?.onSlide(percent)
            }
        }
    }

    companion object {
        private val TAG: String = Slide::class.java.simpleName

        val KEY_START_DIRECTION: String = TAG + "_start_direction"
        val KEY_AUTO_SLIDE_DURATION: String = TAG + "_auto_slide_duration"
        val KEY_HIDE_SOFT_INPUT: String = TAG + "_hide_soft_input"
        val KEY_STATE_SAVED: String = TAG + "_state_saved"
    }
}
