package com.kdanmobile.reader.thumb

import android.arch.lifecycle.ViewModel
import android.graphics.Bitmap
import com.kdanmobile.kmpdfkit.manager.KMPDFFactory
import com.kdanmobile.kmpdfkit.manager.controller.KMPDFDocumentController
import com.kdanmobile.kmpdfkit.pdfcommon.Bookmark
import com.kdanmobile.kmpdfkit.pdfcommon.TextChar
import com.kdanmobile.kmpdfkit.pdfcommon.TextWord
import com.kdanmobile.reader.ReaderModel
import com.kdanmobile.reader.screen.handler.*
import com.kdanmobile.reader.thumb.mode.Mode
import com.kdanmobile.reader.thumb.mode.NormalMode
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import java.io.File
import java.util.*

class PdfThumbViewModel: ViewModel() {
    companion object {
        var tempReaderModel: ReaderModel? = null
    }
    private var pageCount = 0
    private var normalMode = NormalMode()
    private var disposable: Disposable? = null
    private var readerModel: ReaderModel? = null
    private val kmpdfFactory: KMPDFFactory?
        get() = readerModel?.kmpdfFactory
    private val kmpdfDocumentController: KMPDFDocumentController?
        get() = readerModel?.kmpdfDocumentController
    var thumbData: ThumbData = ThumbData()
    val mapSelect: MutableMap<Int, Boolean> = HashMap()
    var mode: Mode
    var isEdit = false
    var isPdf = false

    init {
        readerModel = tempReaderModel
        readerModel?.register()
        tempReaderModel = null
        isPdf = kmpdfFactory?.fileName?.toLowerCase(Locale.US)?.endsWith(".pdf") ?: false
        //  TODO this workaround should be removed in the future
        //  workaround: password-protected pdf is not editable
        isPdf = isPdf && (kmpdfFactory?.needPassWord() ?: false)
        pageCount = kmpdfDocumentController?.getDocumentPageCount(false) ?: 0
        setAllUnSelect()
        mode = normalMode
    }

    val pdfInfoHandler = object : PdfInfoHandler {
        override fun getOpenPdfFilename(): String {
            return kmpdfFactory?.fileName ?: ""
        }

        override fun getPdfPageCount(isNativeRefresh: Boolean): Int {
            return kmpdfDocumentController?.getDocumentPageCount(isNativeRefresh) ?: 0
        }

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

        override fun setCurrentPage(page: Int): Boolean {
            return kmpdfDocumentController?.gotoPage(page) ?: false
        }

        override fun textLines(page: Int): Array<Array<TextWord>> {
            return kmpdfDocumentController?.textLines(page) ?: arrayOf()
        }

        override fun getPageChars(page: Int): Array<Array<Array<Array<TextChar>>>> {
            return kmpdfDocumentController?.getPageChars(page) ?: arrayOf()
        }
    }

    val thumbnailHandler = object : ThumbnailHandler {
        override fun getPdfBitmap(position: Int, picWidth: Int, mode: Int, isDrawAnnot: Boolean): Bitmap {
            return kmpdfDocumentController?.covertPDFToBitmap(position, picWidth, mode, isDrawAnnot) ?: Bitmap.createBitmap(0, 0, Bitmap.Config.ARGB_4444)
        }
    }

    private val bookmarkHandler = object : BookmarkHandler {
        override fun getBookmarks(): Array<Bookmark> {
            return kmpdfDocumentController?.bookmarks ?: arrayOf()
        }
    }

    private val operateHandler = object : OperateHandler {
        override fun rotatePages(pages: IntArray, rotateDirection: KMPDFDocumentController.RotationalDirection): Boolean {
            return kmpdfDocumentController?.rotatePages(pages, rotateDirection) ?: false
        }

        override fun deletePages(pages: IntArray): Boolean {
            return kmpdfDocumentController?.deletePages(pages) ?: false
        }
        override fun save(): Boolean {
            return kmpdfDocumentController?.save() ?: false
        }

        override fun reorderPage(fromPage: Int, toPage: Int): Boolean {
            return kmpdfDocumentController?.reorderPages(fromPage,toPage) ?: false
        }

        override fun splitPDFWithPages(path: String, selectPage: IntArray): Boolean {
            return kmpdfDocumentController?.splitPDFWithPages(path, selectPage) ?: false
        }
    }

    val passwordHandler = object : PasswordHandler {
        override fun isNeedPassword(): Boolean {
            return kmpdfFactory?.needPassWord() ?: false
        }
    }

    fun initThumbData(height: Int, width:Int, padding: Int): Observable<ThumbData> {
        return Observable.create { emitter ->
            thumbData = ThumbData().apply {
                viewHeight = height
                viewWidth = width
                viewPadding = padding
                isEditMode = isEdit
                currentPage = pdfInfoHandler.getCurrentPage()
                pageSelect = mapSelect
                pageCount = this@PdfThumbViewModel.pageCount
                hasBookMarks = getBookmarks()
            }
            emitter.onNext(thumbData)
            emitter.onComplete()
        }
    }

    fun isAllItemSelect(): Boolean {
        for (i in 0 until pageCount) {
            mapSelect[i]?.let {
                if (!it) return false
            }
        }
        return true
    }

    fun setAllSelect() {
        for (i in 0 until pageCount) {
            mapSelect.let {
                it[i] = true
            }
        }
    }

    fun setAllUnSelect() {
        for (i in 0 until pageCount) {
            mapSelect.let {
                it[i] = false
            }
        }
    }

    fun getSelectPage(): ArrayList<Int> {
        val list = ArrayList<Int>()
        for (i in 0 until pageCount) {
            mapSelect[i]?.let {
                if (it) list.add(i)
            }
        }
        return list
    }

    fun hasSelectItem(): Boolean {
        for (i in 0 until pageCount) {
            mapSelect[i]?.also {
                if (it) return true
            }
        }
        return false
    }

    private fun getBookmarks() : Array<Boolean> {
        println("BOOKMARK")
        val hasBookMark = Array(pageCount) { false }
        for (bookmark in bookmarkHandler.getBookmarks()) {
            println("BOOKMARK::${bookmark.pageNum}")
            hasBookMark[bookmark.pageNum] = true
        }
        return hasBookMark
    }

    fun pageRotate(runnable: Runnable) {
        disposable = Observable.create<Boolean> { emitter ->
            val selection = getSelectPage()
            val isRotate = operateHandler.rotatePages(selection.toIntArray(), KMPDFDocumentController.RotationalDirection.CLOCK_WISE)
            for (pageIndex in selection) {
                thumbData.pages.remove(pageIndex)
            }
            emitter.onNext(isRotate)
            emitter.onComplete()
        }
                .subscribeOn(Schedulers.computation())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe( {
                    when(it) {
                        true -> {
                            runnable.run()
                        }
                    }
                }, {
                    it.printStackTrace()
                })
    }

    fun pageDelete(runnable: Runnable) {
        val selection = getSelectPage()
        disposable = Observable.create<Boolean> { emitter ->
            val flag = operateHandler.deletePages(selection.toIntArray())
            emitter.onNext(flag)
            emitter.onComplete()
        }
                .subscribeOn(Schedulers.computation())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe({
                    when(it){
                        true -> {
                            readerModel?.onPdfChangedListener?.onPageDeleted(selection.toList())
                            pageCount -= selection.size
                            thumbData.pageCount = pageCount
                            thumbData.hasBookMarks = getBookmarks()
                            thumbData.pages.trimToSize(0)
                            updateCurrentPage()
                            runnable.run()
                        }
                    }
                }, {
                    it.printStackTrace()
                })
    }

    private fun updateCurrentPage() {
        // fix: incorrect additional empty pdf page
        val pdfPageCount = pdfInfoHandler.getPdfPageCount(true)
        // update selected page
        val currentPage = pdfInfoHandler.getCurrentPage()
        if (currentPage < 0) {
            pdfInfoHandler.setCurrentPage(0)
            thumbData.currentPage = 0
        }
        else if (currentPage >= pdfPageCount) {
            pdfInfoHandler.setCurrentPage(pdfPageCount - 1)
            thumbData.currentPage = pdfPageCount - 1
        }
    }

    fun pageExtract(dst: File, runnableForSuc: Runnable, runnableForFail: Runnable) {
        disposable = Observable.create<Boolean> { emitter ->
            val b = operateHandler.splitPDFWithPages(dst.absolutePath, getSelectPage().toIntArray())
            emitter.onNext(b)
            emitter.onComplete()
        }
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe({
                    when (it) {
                        true -> {
                            thumbData.pages.trimToSize(0)
                            runnableForSuc.run()
                        }
                        false -> {
                            runnableForFail.run()
                        }
                    }
                }, {
                    it.printStackTrace()
                })
    }

    private fun reorderCache(fromPage: Int, toPage: Int) {
        if (fromPage > toPage) {
            val bitmap = thumbData.pages.get(fromPage)
            for (pageIndex in fromPage - 1 downTo toPage) {
                println("reorderCache pageIndex=$pageIndex")
                thumbData.pages.remove(pageIndex + 1)
                val currentBitmap = thumbData.pages.get(pageIndex)
                if (null != currentBitmap) {
                    thumbData.pages.put(pageIndex + 1, currentBitmap)
                }
            }
            if (null != bitmap) {
                thumbData.pages.put(toPage, bitmap)
            }
        } else {
            val bitmap = thumbData.pages.get(fromPage)
            for (pageIndex in fromPage + 1 .. toPage) {
                thumbData.pages.remove(pageIndex - 1)
                val currentBitmap = thumbData.pages.get(pageIndex)
                if (null != currentBitmap) {
                    thumbData.pages.put(pageIndex - 1, currentBitmap)
                }
            }
            if (null != bitmap) {
                thumbData.pages.put(toPage, bitmap)
            }
        }
    }

    fun reorderPage(fromPage: Int, toPage: Int) {
        disposable = Observable.create<Boolean> { emitter ->
            val flag = operateHandler.reorderPage(fromPage, toPage)
            emitter.onNext(flag)
            emitter.onComplete()
        }
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe({
                    when(it){
                        true -> {
                            readerModel?.onPdfChangedListener?.onPageUpdated(if (fromPage > toPage) (toPage .. fromPage).toList() else (fromPage .. toPage).toList())
                            thumbData.hasBookMarks = getBookmarks()
                            reorderCache(fromPage, toPage)
                        }
                    }
                }, {
                    it.printStackTrace()
                })
    }

    override fun onCleared() {
        if (null != disposable && !disposable!!.isDisposed) {
            disposable?.dispose()
            disposable = null
        }
        thumbData.pages.evictAll()
        readerModel?.unregister()
        super.onCleared()
    }

    fun updateBookmarkDisplay() {
        kmpdfFactory?.documentInfo?.bookmarks = kmpdfDocumentController?.bookmarks
    }

    fun temporarySave() {
        //  TODO this workaround should be removed in the future
        //  workaround: password-protected pdf is not editable
        if (kmpdfFactory?.needPassWord() == true) return
        kmpdfDocumentController?.temporarySave()
    }
}