package com.gh.common.exposure

import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.gh.gamecenter.feature.exposure.ExposureEvent
import io.reactivex.functions.Consumer

/**
 * Exposure Event Listener for RecyclerView
 */
class ExposureListener(var fragment: Fragment, var exposable: IExposable) : RecyclerView.OnScrollListener() {

    val throttleBus: ExposureThrottleBus by lazy {
        ExposureThrottleBus(
            Consumer { commitExposure(it) },
            Consumer(Throwable::printStackTrace)
        )
    }
    var layoutManager: LinearLayoutManager? = null
    var visibleState: ExposureThrottleBus.VisibleState? = null

    init {
        fragment.fragmentManager?.registerFragmentLifecycleCallbacks(
            object : FragmentManager.FragmentLifecycleCallbacks() {
                override fun onFragmentPaused(fm: FragmentManager, f: Fragment) {
                    if (fragment == f) {
                        visibleState?.let { commitExposure(it) }
                        throttleBus.clear()
                    }
                }

                override fun onFragmentViewDestroyed(fm: FragmentManager, f: Fragment) {
                    if (fragment == f) {
                        fragment.fragmentManager?.unregisterFragmentLifecycleCallbacks(this)
                    }
                }
            }, false
        )
    }

    /**
     * Monitor items in display when scrolling, record those newly displayed as well as
     * those newly disappeared. And finally trigger commitExposure().
     */
    override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
        super.onScrolled(recyclerView, dx, dy)

        if (layoutManager == null) layoutManager = recyclerView.layoutManager as LinearLayoutManager

        layoutManager?.run {
            visibleState =
                ExposureThrottleBus.VisibleState(findFirstVisibleItemPosition(), findLastVisibleItemPosition())
            throttleBus.postVisibleState(visibleState!!)
        }
    }

    /**
     * Just commit the exposureEvent that is stored in listItem.
     */
    private fun commitExposure(visibleState: ExposureThrottleBus.VisibleState) {

        val eventList = arrayListOf<ExposureEvent>()

        for (pos in visibleState.firstVisiblePosition..visibleState.lastVisiblePosition) {
            try {
                exposable.getEventByPosition(pos)?.let { eventList.add(it) }
                exposable.getEventListByPosition(pos)?.let { eventList.addAll(it) }
            } catch (ignore: Exception) {
                // Just ignore the error.
            }
        }

        ExposureManager.log(eventList)
    }

}