package com.gh.common.exposure

import io.reactivex.disposables.CompositeDisposable
import io.reactivex.functions.Consumer
import io.reactivex.schedulers.Schedulers
import io.reactivex.subjects.BehaviorSubject
import java.util.concurrent.TimeUnit

class ExposureThrottleBus(
    var onSuccess: Consumer<VisibleState>,
    var onError: Consumer<Throwable>,
    onPreSuccess: Consumer<VisibleState>
) {

    companion object {
        private const val THRESHOLD_TIME = 300L
    }

    private val mPublishSubject: BehaviorSubject<VisibleState> = BehaviorSubject.create()
    private val mCompositeDisposable: CompositeDisposable = CompositeDisposable()

    init {

        /**
         *  Since onScroll() callback will be triggered multiple times for every swipe, we use
         *  distinctUntilChanged() to prevent committing the same visibleState event and
         *  throttleWithTimeout() to pass a visibleState event with a delay and drop current event if another event arrives before the timeout.
         */
        val disposable = mPublishSubject
            .distinctUntilChanged()
            .doOnNext(onPreSuccess)
            .throttleWithTimeout(THRESHOLD_TIME, TimeUnit.MILLISECONDS)
            .subscribeOn(Schedulers.io())
            .subscribe(onSuccess, onError)
        mCompositeDisposable.add(disposable)
    }

    fun clear() {
        mCompositeDisposable.clear()
    }

    fun postVisibleState(visibleState: VisibleState) {
        mPublishSubject.onNext(visibleState)
    }

    class VisibleState(val firstVisiblePosition: Int, val lastVisiblePosition: Int) {
        override fun equals(other: Any?): Boolean {
            if (this === other) return true
            if (other == null || javaClass != other.javaClass) return false

            val that = other as VisibleState

            if (firstVisiblePosition != that.firstVisiblePosition) return false

            return lastVisiblePosition == that.lastVisiblePosition
        }

        override fun hashCode(): Int {
            var result = firstVisiblePosition
            result = 31 * result + lastVisiblePosition
            return result
        }
    }
}