package com.kdanmobile.reader.test

import android.util.SparseBooleanArray
import com.kdanmobile.kmpdfkit.globaldata.Config
import com.kdanmobile.kmpdfkit.manager.controller.KMPDFDocumentController

class AdPageHelper {

    companion object {
        private const val DEFAULT_AD_INTERVAL = 1
        private const val DEFAULT_INIT_AD_INDEX = 1
        private const val DEFAULT_MINIMUM_INTERVAL = 4
        private const val DEFAULT_NEXT_AD_INTERVAL = 3
    }

    enum class DisplayStrategy {
        HIDE,
        PAGE_INTERVAL,
        TIME_INTERVAL,
        PAGE_OR_TIME_INTERVAL,
        PAGE_AND_TIME_INTERVAL
    }
    var displayStrategy = DisplayStrategy.PAGE_INTERVAL

    var adInterval = DEFAULT_AD_INTERVAL
        set(value) {
            if (field != value) {
                field = if (value <= 0) 1 else value
                adVisibleSet.clear()
            }
        }
    var initAdIndex = DEFAULT_INIT_AD_INDEX
        set(value) {
            if (field != value) {
                field = if (value <= 0) 1 else value
                adVisibleSet.clear()
            }
        }
    var minInterval = DEFAULT_MINIMUM_INTERVAL
        set(value) {
            if (field != value) {
                field = if (value <= 0) 1 else value
                adVisibleSet.clear()
            }
        }
    var nextInterval = DEFAULT_NEXT_AD_INTERVAL
        set(value) {
            field = Math.max(2, value)
        }
    var timeInterval = 5

    var kmpdfDocumentController: KMPDFDocumentController? = null

    private var lastDisplayTime = System.currentTimeMillis()

    private val adVisibleSet = SparseBooleanArray()

    var requestAd: (position: Int) -> Unit = {}

    var shouldShowAd: () -> Boolean = {
        defaultShouldShowAd()
    }

    var isAdLoaded: (position: Int) -> Boolean = {
        false
    }

    fun isAdVisible(pageIndex: Int): Boolean {
        return isAdPage(pageIndex) && adVisibleSet[pageIndex]
    }

    private fun checkCanAdShow(pageIndex: Int): Boolean {
        return when {
            pageIndex < initAdIndex -> false
            !isAdPage(pageIndex) -> false
            !shouldShowAd.invoke() -> false
            Math.abs(pageIndex - getCurrentPage()) < 2 -> false
            pageIndex < 0 || pageIndex >= getPageCount() -> false
            else -> true
        }
    }

    fun onPageChanged(pageIndex: Int) {
        if (!shouldShowAd.invoke()) return

        for (dir in -1 .. 1 step 2) {
            val targetIndex = pageIndex + nextInterval * dir
            val showAd = when (displayStrategy) {
                DisplayStrategy.HIDE -> false
                DisplayStrategy.PAGE_INTERVAL -> !haveAnyVisibleAdNear(targetIndex)
                DisplayStrategy.TIME_INTERVAL -> !haveDisplayAdRecent()
                DisplayStrategy.PAGE_OR_TIME_INTERVAL -> !haveAnyVisibleAdNear(targetIndex) || !haveDisplayAdRecent()
                DisplayStrategy.PAGE_AND_TIME_INTERVAL -> !haveAnyVisibleAdNear(targetIndex) && !haveDisplayAdRecent()
            }
            if (showAd) {
                if (shouldShowAd.invoke()) {
                    if (isAdLoaded.invoke(targetIndex)) {
                        if (checkCanAdShow(targetIndex)) {
                            adVisibleSet.put(targetIndex, true)
                            lastDisplayTime = System.currentTimeMillis()
                        }
                    } else {
                        requestAd.invoke(targetIndex)
                    }
                }
            }
        }
    }

    private fun haveAnyVisibleAdNear(pageIndex: Int): Boolean {
        for (index in 0 until adVisibleSet.size()) {
            if (Math.abs(pageIndex - adVisibleSet.keyAt(index)) < minInterval) {
                return true
            }
        }
        return false
    }

    private fun haveDisplayAdRecent(): Boolean {
        return System.currentTimeMillis() - lastDisplayTime < timeInterval * 1000L
    }

    fun isAdPage(pageIndex: Int): Boolean {
        if (!shouldShowAd.invoke()) return false
        if (pageIndex < initAdIndex) return false
        return (pageIndex - initAdIndex) % (adInterval + 1) == 0
    }

    fun convertToPageIndex(rawPageIndex: Int): Int {
        if (!shouldShowAd.invoke()) return rawPageIndex
        if (rawPageIndex < initAdIndex) return rawPageIndex
        return rawPageIndex + 1 + (rawPageIndex - initAdIndex) / adInterval
    }

    fun convertToRawPageIndex(pageIndex: Int): Int {
        if (!shouldShowAd.invoke()) return pageIndex
//        if (pageIndex < initAdIndex) return pageIndex / (adInterval + 1)
        if (pageIndex < initAdIndex) return pageIndex
        return (pageIndex * adInterval + initAdIndex) / (adInterval + 1)
    }

    fun getPageCount(isNativeRefresh: Boolean = false): Int {
        val rawPageCount = getRawPageCount(isNativeRefresh)
        if (!shouldShowAd.invoke()) return rawPageCount
        val pageCount = convertToPageIndex(rawPageCount)
        val lastPage = pageCount - 1
        return when (isAdPage(lastPage)) {
            true -> pageCount - 1
            false -> pageCount
        }
    }

    fun getRawPageCount(isNativeRefresh: Boolean = false): Int {
        return kmpdfDocumentController?.getDocumentPageCount(isNativeRefresh) ?: 0
    }

    fun getCurrentPage(): Int {
        return kmpdfDocumentController?.currentPageNum ?: 0
    }

    private fun isVerticalContinuesViewMode(): Boolean {
        return kmpdfDocumentController?.pdfViewMode == Config.PDFViewMode.VERTICAL_SINGLE_PAGE_CONTINUES
    }

    fun defaultShouldShowAd(): Boolean  {
        if (displayStrategy == DisplayStrategy.HIDE) return false
        return isVerticalContinuesViewMode()
    }
}