package app.furmeet.events.system.helper

import android.annotation.SuppressLint
import android.app.Activity
import android.graphics.Point
import android.graphics.Rect
import android.util.DisplayMetrics
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import androidx.annotation.IdRes
import androidx.annotation.LayoutRes
import androidx.appcompat.app.AlertDialog
import kotlin.math.roundToInt

class PeekAndPop(builder: Builder) {
    class Builder private constructor(val activity: Activity, val builderData: BuilderData) {
        constructor(activity: Activity) : this(activity, BuilderData(
                null,
                null,
                null,
                false,
                NullObjects.nullGeneralActionListener,
                NullObjects.nullHoldAndReleaseListener
        ))

        class BuilderData(
                val peekLayout: Int?,
                val longClickViews: View?,
                val parentViewGroupToDisallowTouchEvents: ViewGroup?,
                val blurs: Boolean,
                val onGeneralActionListener: OnGeneralActionListener,
                val onHoldAndReleaseListener: OnHoldAndReleaseListener
        )

        fun peekLayout(@LayoutRes layoutId: Int) = Builder(activity, BuilderData(
                layoutId,
                builderData.longClickViews,
                builderData.parentViewGroupToDisallowTouchEvents,
                builderData.blurs,
                builderData.onGeneralActionListener,
                builderData.onHoldAndReleaseListener
        ))

        fun longClickViews(view: View): Builder = Builder(activity, BuilderData(
                builderData.peekLayout,
                view,
                builderData.parentViewGroupToDisallowTouchEvents,
                builderData.blurs,
                builderData.onGeneralActionListener,
                builderData.onHoldAndReleaseListener
        ))

        fun parentViewGroupToDisallowTouchEvents(viewGroup: ViewGroup) = Builder(activity, BuilderData(
                builderData.peekLayout,
                builderData.longClickViews,
                viewGroup,
                builderData.blurs,
                builderData.onGeneralActionListener,
                builderData.onHoldAndReleaseListener
        ))

        fun onGeneralActionListener(onGeneralActionListener: OnGeneralActionListener) = Builder(activity, BuilderData(
                builderData.peekLayout,
                builderData.longClickViews,
                builderData.parentViewGroupToDisallowTouchEvents,
                builderData.blurs,
                onGeneralActionListener,
                builderData.onHoldAndReleaseListener
        ))

        fun onHoldAndReleaseListener(onHoldAndReleaseListener: OnHoldAndReleaseListener) = Builder(activity, BuilderData(
                builderData.peekLayout,
                builderData.longClickViews,
                builderData.parentViewGroupToDisallowTouchEvents,
                builderData.blurs,
                builderData.onGeneralActionListener,
                onHoldAndReleaseListener
        ))

        fun blurBackground(blur: Boolean) = Builder(activity, BuilderData(
                builderData.peekLayout,
                builderData.longClickViews,
                builderData.parentViewGroupToDisallowTouchEvents,
                blur,
                builderData.onGeneralActionListener,
                builderData.onHoldAndReleaseListener
        ))

        fun build(): PeekAndPop = PeekAndPop(this)
    }

    private val activity = builder.activity
    private val peekLayout = builder.builderData.peekLayout!!
    private val longClickViews = builder.builderData.longClickViews!!
    private val parentViewGroupToDisallowTouchEvents = (builder.builderData.parentViewGroupToDisallowTouchEvents
            ?: longClickViews) as ViewGroup
    private val onGeneralActionListener = builder.builderData.onGeneralActionListener
    private val onHoldAndReleaseListener = builder.builderData.onHoldAndReleaseListener

    val peekView = activity.layoutInflater.inflate(peekLayout)

    init {
        longClickViews.isLongClickable = true
        longClickViews.setOnLongClickListener {
            onGeneralActionListener.onPeek(longClickViews, -1)
            parentViewGroupToDisallowTouchEvents.requestDisallowInterceptTouchEvent(true)
            AlertDialog.Builder(activity)
                    .setView(peekView)
                    .setOnDismissListener {
                        (peekView.parent as ViewGroup).removeView(peekView)
                        longClickViews.setOnTouchListener(null)
                        cursorIsOver = null
                        onGeneralActionListener.onPop(longClickViews, -1)
                    }
                    .show()
                    .apply {
                        val dialog = this
                        window?.setBackgroundDrawableResource(android.R.color.transparent)
                        //@SuppressLint("ClickableViewAccessibility") //
                        longClickViews.setOnTouchListener { _, event ->
                            when {
                                event.action == MotionEvent.ACTION_CANCEL -> dialog.dismiss()
                                event.action == MotionEvent.ACTION_MOVE -> {
                                    val metrics = DisplayMetrics()
                                    activity.windowManager.defaultDisplay.getMetrics(metrics)
                                    val winSize = Point(metrics.widthPixels, metrics.heightPixels + (40 * metrics.density).roundToInt())
                                    val peekSize = peekView.let { Point(it.width, it.height) }
                                    val offset = Point(
                                            ((winSize.x / 2) - (peekSize.x / 2)),
                                            ((winSize.y / 2) - (peekSize.y / 2))
                                    )
                                    val newCursorIsOver = holdAndReleaseViews.firstOrNull { view ->
                                        val relativePosition = Rect()
                                        view.getGlobalVisibleRect(relativePosition)
                                        val actualPosition = Rect(
                                                relativePosition.left + offset.x,
                                                relativePosition.top + offset.y,
                                                relativePosition.right + offset.x,
                                                relativePosition.bottom + offset.y
                                        )
                                        Point(event.rawX.roundToInt(), event.rawY.roundToInt()) in actualPosition
                                    }
                                    updateCursorIsOver(newCursorIsOver)
                                }
                                event.action == MotionEvent.ACTION_UP -> {
                                    cursorIsOver?.let { releasedView ->
                                        onHoldAndReleaseListener.onRelease(releasedView, -1)

                                    }
                                    dialog.dismiss()
                                }
                                else -> dialog.dismiss()
                            }
                            false
                        }
                    }
            true
        }
    }

    private var cursorIsOver: View? = null
    private val holdAndReleaseViews = mutableListOf<View>()
    private fun updateCursorIsOver(view: View?) {
        if (view != cursorIsOver) {
            cursorIsOver?.let {
                onHoldAndReleaseListener.onLeave(it, -1)
            }
            view?.let {
                onHoldAndReleaseListener.onHold(it, -1)
            }
            cursorIsOver = view
        }
    }

    fun addHoldAndReleaseView(@IdRes idRes: Int) {
        peekView.findViewById<View?>(idRes)?.let {
            holdAndReleaseViews.add(it)
        }
    }

    interface OnGeneralActionListener {
        fun onPeek(longClickView: View, position: Int)
        fun onPop(longClickView: View, position: Int)
    }

    interface OnHoldAndReleaseListener {
        fun onHold(view: View, position: Int)
        fun onLeave(view: View, position: Int)
        fun onRelease(view: View, position: Int)
    }

    private object NullObjects {
        val nullGeneralActionListener = object : OnGeneralActionListener {
            override fun onPeek(longClickView: View, position: Int) {}
            override fun onPop(longClickView: View, position: Int) {}
        }
        val nullHoldAndReleaseListener = object : OnHoldAndReleaseListener {
            override fun onHold(view: View, position: Int) {}
            override fun onLeave(view: View, position: Int) {}
            override fun onRelease(view: View, position: Int) {}
        }
    }
}

private operator fun Rect.contains(point: Point): Boolean = contains(point.x, point.y)
