package io.legado.app.service.help

import android.util.Log
import androidx.lifecycle.MutableLiveData
import com.hankcs.hanlp.HanLP
import io.legado.app.App
import io.legado.app.R
import io.legado.app.constant.BookType
import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter
import io.legado.app.data.entities.BookSource
import io.legado.app.data.entities.ReadRecord
import io.legado.app.help.AppConfig
import io.legado.app.help.BookHelp
import io.legado.app.help.IntentDataHelp
import io.legado.app.help.coroutine.Coroutine
import io.legado.app.model.webBook.WebBook
import io.legado.app.service.BaseReadAloudService
import io.legado.app.ui.book.read.page.entities.TextChapter
import io.legado.app.ui.book.read.page.entities.TextPage
import io.legado.app.ui.book.read.page.provider.ChapterProvider
import io.legado.app.ui.book.read.page.provider.ImageProvider
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.jetbrains.anko.getStackTraceString
import org.jetbrains.anko.toast


object ReadBook {
    var titleDate = MutableLiveData<String>()
    var book: Book? = null
    var inBookshelf = false
    var chapterSize = 0
    var durChapterIndex = 0
    var durPageIndex = 0
    var isLocalBook = true
    var callBack: CallBack? = null
    var prevTextChapter: TextChapter? = null
    var curTextChapter: TextChapter? = null
    var nextTextChapter: TextChapter? = null
    var bookSource: BookSource? = null
    var webBook: WebBook? = null
    var msg: String? = null
    private val loadingChapters = arrayListOf<Int>()
    private val readRecord = ReadRecord()
    var readStartTime: Long = System.currentTimeMillis()

    fun resetData(book: Book) {
        this.book = book
        readRecord.bookName = book.name
        readRecord.readTime = App.db.readRecordDao().getReadTime(book.name) ?: 0
        durChapterIndex = book.durChapterIndex
        durPageIndex = book.durChapterPos
        isLocalBook = book.origin == BookType.local
        chapterSize = 0
        prevTextChapter = null
        curTextChapter = null
        nextTextChapter = null
        titleDate.postValue(book.name)
        upWebBook(book)
        ImageProvider.clearAllCache()
    }

    fun upWebBook(book: Book) {
        if (book.origin == BookType.local) {
            bookSource = null
            webBook = null
        } else {
            App.db.bookSourceDao().getBookSource(book.origin)?.let {
                bookSource = it
                webBook = WebBook(it)
            } ?: let {
                bookSource = null
                webBook = null
            }
        }
    }

    fun upReadStartTime() {
        Coroutine.async {
            readRecord.readTime = readRecord.readTime + System.currentTimeMillis() - readStartTime
            readStartTime = System.currentTimeMillis()
            App.db.readRecordDao().insert(readRecord)
        }
    }

    fun upMsg(msg: String?) {
        if (this.msg != msg) {
            this.msg = msg
            callBack?.upContent()
        }
    }

    fun moveToNextPage() {
        durPageIndex++
        callBack?.upContent()
        saveRead()
    }

    fun moveToNextChapter(upContent: Boolean): Boolean {
        if (durChapterIndex < chapterSize - 1) {
            durPageIndex = 0
            durChapterIndex++
            prevTextChapter = curTextChapter
            curTextChapter = nextTextChapter
            nextTextChapter = null
            book?.let {
                if (curTextChapter == null) {
                    loadContent(durChapterIndex, upContent, false)
                } else if (upContent) {
                    callBack?.upContent()
                }
                loadContent(durChapterIndex.plus(1), upContent, false)
                GlobalScope.launch(Dispatchers.IO) {
                    for (i in 2..10) {
                        delay(100)
                        download(durChapterIndex + i)
                    }
                }
            }
            saveRead()
            callBack?.upView()
            curPageChanged()
            return true
        } else {
            return false
        }
    }

    fun moveToPrevChapter(upContent: Boolean, toLast: Boolean = true): Boolean {
        if (durChapterIndex > 0) {
            durPageIndex = if (toLast) prevTextChapter?.lastIndex ?: 0 else 0
            durChapterIndex--
            nextTextChapter = curTextChapter
            curTextChapter = prevTextChapter
            prevTextChapter = null
            book?.let {
                if (curTextChapter == null) {
                    loadContent(durChapterIndex, upContent, false)
                } else if (upContent) {
                    callBack?.upContent()
                }
                loadContent(durChapterIndex.minus(1), upContent, false)
                GlobalScope.launch(Dispatchers.IO) {
                    for (i in -5..-2) {
                        delay(100)
                        download(durChapterIndex + i)
                    }
                }
            }
            saveRead()
            callBack?.upView()
            curPageChanged()
            return true
        } else {
            return false
        }
    }

    fun skipToPage(page: Int) {
        durPageIndex = page
        callBack?.upContent()
        curPageChanged()
        saveRead()
    }

    fun setPageIndex(pageIndex: Int) {
        durPageIndex = pageIndex
        saveRead()
        curPageChanged()
    }

    private fun curPageChanged() {
        callBack?.pageChanged()
        if (BaseReadAloudService.isRun) {
            readAloud(!BaseReadAloudService.pause)
        }
        upReadStartTime()
    }

    /**
     * 朗读
     */
    fun readAloud(play: Boolean = true) {
        val book = book
        val textChapter = curTextChapter
        if (book != null && textChapter != null) {
            val key = IntentDataHelp.putData(textChapter)
            ReadAloud.play(
                App.INSTANCE,
                book.name,
                textChapter.title,
                durPageIndex,
                key,
                play
            )
        }
    }

    fun durChapterPos(): Int {
        curTextChapter?.let {
            if (durPageIndex < it.pageSize) {
                return durPageIndex
            }
            return it.pageSize - 1
        }
        return durPageIndex
    }

    /**
     * chapterOnDur: 0为当前页,1为下一页,-1为上一页
     */
    fun textChapter(chapterOnDur: Int = 0): TextChapter? {
        return when (chapterOnDur) {
            0 -> curTextChapter
            1 -> nextTextChapter
            -1 -> prevTextChapter
            else -> null
        }
    }

    /**
     * 加载章节内容
     */
    fun loadContent(resetPageOffset: Boolean) {
        loadContent(durChapterIndex, resetPageOffset = resetPageOffset)
        loadContent(durChapterIndex + 1, resetPageOffset = resetPageOffset)
        loadContent(durChapterIndex - 1, resetPageOffset = resetPageOffset)
    }

    fun loadContent(index: Int, upContent: Boolean = true, resetPageOffset: Boolean) {
        book?.let { book ->
            if (addLoading(index)) {
                Coroutine.async {
                    App.db.bookChapterDao().getChapter(book.bookUrl, index)?.let { chapter ->
                        BookHelp.getContent(book, chapter)?.let {
                            contentLoadFinish(book, chapter, it, upContent, resetPageOffset)
                            removeLoading(chapter.index)
                        } ?: download(chapter, resetPageOffset = resetPageOffset)
                    } ?: removeLoading(index)
                }.onError {
                    removeLoading(index)
                }
            }
        }
    }

    private fun download(index: Int) {
        book?.let { book ->
            if (book.isLocalBook()) return
            if (addLoading(index)) {
                Coroutine.async {
                    App.db.bookChapterDao().getChapter(book.bookUrl, index)?.let { chapter ->
                        if (BookHelp.hasContent(book, chapter)) {
                            removeLoading(chapter.index)
                        } else {
                            download(chapter, false)
                        }
                    } ?: removeLoading(index)
                }.onError {
                    removeLoading(index)
                }
            }
        }
    }

    private fun download(chapter: BookChapter, resetPageOffset: Boolean) {
        val book = book
        val webBook = webBook
        if (book != null && webBook != null) {
            webBook.getContent(book, chapter)
                .onSuccess(Dispatchers.IO) { content ->
                    if (content.isEmpty()) {
                        contentLoadFinish(
                            book,
                            chapter,
                            App.INSTANCE.getString(R.string.content_empty),
                            resetPageOffset = resetPageOffset
                        )
                        removeLoading(chapter.index)
                    } else {
                        BookHelp.saveContent(book, chapter, content)
                        contentLoadFinish(
                            book,
                            chapter,
                            content,
                            resetPageOffset = resetPageOffset
                        )
                        removeLoading(chapter.index)
                    }
                }.onError {
                    contentLoadFinish(
                        book,
                        chapter,
                        it.localizedMessage ?: "未知错误",
                        resetPageOffset = resetPageOffset
                    )
                    removeLoading(chapter.index)
                }
        } else if (book != null) {
            contentLoadFinish(
                book,
                chapter,
                "没有书源",
                resetPageOffset = resetPageOffset
            )
            removeLoading(chapter.index)
        } else {
            removeLoading(chapter.index)
        }
    }

    private fun addLoading(index: Int): Boolean {
        synchronized(this) {
            if (loadingChapters.contains(index)) return false
            loadingChapters.add(index)
            return true
        }
    }

    private fun removeLoading(index: Int) {
        synchronized(this) {
            loadingChapters.remove(index)
        }
    }

    fun searchResultPositions(pages: List<TextPage>, indexWithinChapter: Int, query: String): Array<Int>{
        //
        // calculate search result's pageIndex
        var content = ""
        pages.map{
            content+= it.text
        }
        var count = 1
        var index = content.indexOf(query)
        while(count != indexWithinChapter){
            index = content.indexOf(query, index + 1);
            count += 1
        }
        val contentPosition = index
        var pageIndex = 0
        var length = pages[pageIndex].text.length
        while (length < contentPosition){
            pageIndex += 1
            if (pageIndex >pages.size){
                pageIndex = pages.size
                break
            }
            length += pages[pageIndex].text.length
        }

        // calculate search result's lineIndex
        val currentPage = pages[pageIndex]
        var lineIndex = 0
        length = length - currentPage.text.length + currentPage.textLines[lineIndex].text.length
        while (length < contentPosition){
            lineIndex += 1
            if (lineIndex >currentPage.textLines.size){
                lineIndex = currentPage.textLines.size
                break
            }
            length += currentPage.textLines[lineIndex].text.length
        }

        // charIndex
        val currentLine = currentPage.textLines[lineIndex]
        length -= currentLine.text.length
        val charIndex = contentPosition - length
        var addLine = 0
        var charIndex2 = 0
        // change line
        if ((charIndex + query.length) > currentLine.text.length){
            addLine = 1
            charIndex2 = charIndex + query.length - currentLine.text.length - 1
        }
        // changePage
        if ((lineIndex + addLine + 1) > currentPage.textLines.size){
            addLine = -1
            charIndex2 = charIndex + query.length - currentLine.text.length - 1
        }
        return arrayOf(pageIndex, lineIndex, charIndex, addLine, charIndex2)
    }

    /**
     * 内容加载完成
     */
    private fun contentLoadFinish(
        book: Book,
        chapter: BookChapter,
        content: String,
        upContent: Boolean = true,
        resetPageOffset: Boolean
    ) {
        Coroutine.async {
            if (chapter.index in durChapterIndex - 1..durChapterIndex + 1) {
                chapter.title = when (AppConfig.chineseConverterType) {
                    1 -> HanLP.convertToSimplifiedChinese(chapter.title)
                    2 -> HanLP.convertToTraditionalChinese(chapter.title)
                    else -> chapter.title
                }
                val contents = BookHelp.disposeContent(
                    chapter.title,
                    book.name,
                    webBook?.bookSource?.bookSourceUrl,
                    content,
                    book.useReplaceRule
                )
                when (chapter.index) {
                    durChapterIndex -> {
                        curTextChapter =
                            ChapterProvider.getTextChapter(
                                book,
                                chapter,
                                contents,
                                chapterSize,
                                imageStyle
                            )
                        if (upContent) callBack?.upContent(resetPageOffset = resetPageOffset)
                        callBack?.upView()
                        curPageChanged()
                        callBack?.contentLoadFinish()
                        ImageProvider.clearOut(durChapterIndex)
                    }
                    durChapterIndex - 1 -> {
                        prevTextChapter =
                            ChapterProvider.getTextChapter(
                                book,
                                chapter,
                                contents,
                                chapterSize,
                                imageStyle
                            )
                        if (upContent) callBack?.upContent(-1, resetPageOffset)
                    }
                    durChapterIndex + 1 -> {
                        nextTextChapter =
                            ChapterProvider.getTextChapter(
                                book,
                                chapter,
                                contents,
                                chapterSize,
                                imageStyle
                            )
                        if (upContent) callBack?.upContent(1, resetPageOffset)
                    }
                }
            }
        }.onError {
            it.printStackTrace()
            App.INSTANCE.toast("ChapterProvider ERROR:\n${it.getStackTraceString()}")
        }
    }

    private val imageStyle get() = webBook?.bookSource?.ruleContent?.imageStyle

    fun setCharset(charset: String) {
        book?.let {
            it.charset = charset
            callBack?.loadChapterList(it)
        }
        saveRead()
    }

    fun saveRead() {
        Coroutine.async {
            book?.let { book ->
                book.lastCheckCount = 0
                book.durChapterTime = System.currentTimeMillis()
                book.durChapterIndex = durChapterIndex
                book.durChapterPos = durPageIndex
                App.db.bookChapterDao().getChapter(book.bookUrl, durChapterIndex)?.let {
                    book.durChapterTitle = it.title
                }
                App.db.bookDao().update(book)
            }
        }
    }

    interface CallBack {
        fun loadChapterList(book: Book)
        fun upContent(relativePosition: Int = 0, resetPageOffset: Boolean = true)
        fun upView()
        fun pageChanged()
        fun contentLoadFinish()
    }

}