package com.kdanmobile.reader.screen.view

import android.content.Context
import android.graphics.RectF
import android.os.Handler
import android.os.Message
import android.support.v4.content.ContextCompat
import android.text.InputType
import android.util.AttributeSet
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager
import android.widget.*
import com.kdanmobile.kmpdfkit.pdfcommon.TextChar
import com.kdanmobile.kmpdfkit.pdfcommon.TextWord
import com.kdanmobile.reader.R
import com.kdanmobile.reader.screen.adapter.SearchAdapter
import com.kdanmobile.reader.screen.handler.PdfInfoHandler
import com.kdanmobile.reader.screen.handler.SearchHandler
import com.kdanmobile.reader.screen.model.SearchResultInfo
import com.kdanmobile.reader.screen.model.SimpleTextWatcher
import com.kdanmobile.reader.utils.DensityUtil
import io.reactivex.Completable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import kotlinx.android.synthetic.main.view_search.view.*
import java.lang.StringBuilder
import java.lang.ref.WeakReference
import kotlin.collections.ArrayList

class SearchView: RelativeLayout, View.OnClickListener, LeftToolbarView {
    private var pdfInfoHandler: PdfInfoHandler? = null
    private var searchHandler: SearchHandler? = null
    private lateinit var linearLayout: LinearLayout
    private lateinit var tv_page: TextView
    private lateinit var tv_text:TextView
    private lateinit var et_search: EditText
    private lateinit var lv: ListView
    private lateinit var tv_tishi: TextView
    private lateinit var adapter: SearchAdapter
    private val list = ArrayList<SearchResultInfo>()

    // 判断搜索的是否是页码 true 是页码
    private var isPage = false

    private var msgHandler: Handler? = null

    private val MAX_SEARCH_RESULT = 200

    private var isDone = false
    private var deviceWidth: Int = 0

    private var disposable: Disposable? = null

    constructor(context: Context) : super(context) {
        initView()
    }

    constructor(context: Context, attributeSet: AttributeSet) : super(context, attributeSet) {
        initView()
    }

    constructor(context: Context, attributeSet: AttributeSet, defStyleAttr: Int) : super(context, attributeSet, defStyleAttr) {
        initView()
    }

    private fun initView() {
        LayoutInflater.from(context).inflate(R.layout.view_search, this)

        initHandler()

        initLayout()

        setListener()

        postDelayed( { showSoftKeyboard() }, 200)
    }

    fun setHandlers(pdfInfoHandler: PdfInfoHandler, searchHandler: SearchHandler) {
        this.pdfInfoHandler = pdfInfoHandler
        this.searchHandler = searchHandler
    }

    private fun initHandler() {

        class WeakReferenceHandler(view: SearchView) : Handler() {
            private val mView: WeakReference<SearchView> = WeakReference(view)

            override fun handleMessage(msg: Message) {
                val view = mView.get()
                if (view != null) {
                    when (msg.what) {
                        10 -> {
                            if (msg.obj is List<*>) {
                                view.lv.visibility = View.VISIBLE
                                view.tv_tishi.visibility = View.GONE
                                val item = (msg.obj as List<*>).filterIsInstance<SearchResultInfo>()
                                view.list.addAll(item)
                                view.adapter.add(item)
                                view.adapter.notifyDataSetChanged()
                            }
                        }
                        20 -> search()
                        30 -> {
                            val position = msg.arg1
                            val info = view.list[position]
                            view.searchHandler?.setSearchResult(info.page, info.keyword, info.rectList)
                            view.pdfInfoHandler?.setCurrentPage(view.list[position].page)
                        }
                    }
                }
            }
        }
        msgHandler = WeakReferenceHandler(this)
    }

    private fun initLayout() {

        linearLayout = ll_fragmentPdfReaderSearch
        tv_page = tv_fragmentPdfReaderSearch_page
        tv_text = tv_fragmentPdfReaderSearch_text
        et_search = et_fragmentPdfReaderSearch_

        et_search.setHint(R.string.fragment_search_enter_text)
        et_search.inputType = InputType.TYPE_CLASS_TEXT
        et_search.isLongClickable = false
        lv = lv_fragmentPdfReaderSearch_
        tv_tishi = tv_fragmentPdfReaderSearch_tishi

        var navigationHeight = 0
        val resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android")
        if (resourceId > 0) {
            val rid = resources.getIdentifier("config_showNavigationBar", "bool", "android")
            if (resources.getBoolean(rid)) {
                navigationHeight = resources.getDimensionPixelSize(resourceId)
            }
        }
        val screenWidth = DensityUtil.getScreenWidthPx(context)
        val screenHeight = DensityUtil.getScreenHeightPx(context)
        val density = DensityUtil.getDensity(context)
        val xDp = (screenWidth / density).toInt()
        val yDp = ((screenHeight + navigationHeight) / density).toInt()
        val isPhone = when (xDp >= 550 && yDp >= 550) {
            true -> false
            false -> true
        }
        deviceWidth = Math.min(screenWidth, screenHeight)

        linearLayout.layoutParams = LinearLayout.LayoutParams(318 * deviceWidth / 800, LinearLayout.LayoutParams.MATCH_PARENT)

        val params = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)
        params.leftMargin = 21 * deviceWidth / 800
        params.rightMargin = 21 * deviceWidth / 800
        params.topMargin = 8 * deviceWidth / 800
        params.bottomMargin = 8 * deviceWidth / 800
        et_search.layoutParams = params
        val pad = (density * 4).toInt()
        et_search.setPadding(pad, pad, pad, pad)

        if (isPhone) {
            tv_tishi.textSize = 18f
        } else {
            val textSize = deviceWidth.toFloat() / 800f / density
            tv_tishi.textSize = 24 * textSize
        }

        adapter = SearchAdapter(deviceWidth)
        lv.adapter = adapter
    }

    private fun setListener() {

        tv_page.setOnClickListener(this)
        tv_text.setOnClickListener(this)

        lv.onItemClickListener = AdapterView.OnItemClickListener { parent, view, position, id ->
            adapter.setSelected(position)

            msgHandler?.sendMessageDelayed(msgHandler?.obtainMessage(30, position, 0), 50)
        }

        et_search.setOnEditorActionListener { _, actionId, _ ->
            if (actionId == EditorInfo.IME_ACTION_SEARCH) {
                if (!isPage && et_search.text.toString().length < 3) {
                    val la = IntArray(2)
                    et_search.getLocationOnScreen(la)
                    val y = la[1]
                    val toast = Toast.makeText(context, R.string.fileManager_thishi_search, Toast.LENGTH_SHORT)
                    toast.setGravity(Gravity.TOP or Gravity.LEFT, 11 * deviceWidth / 800, y + 30 * deviceWidth / 800)
                    toast.show()
                } else {
                    isDone = true
                    msgHandler?.sendEmptyMessageDelayed(20, 100)
                    hideSoftKeyboard()
                }
            }
            return@setOnEditorActionListener false
        }

        et_search.addTextChangedListener(object : SimpleTextWatcher() {
            override fun onTextChanged(sequence: CharSequence, start: Int, before: Int, count: Int) {
                super.onTextChanged(sequence, start, before, count)
                try {
                    if (isPage && sequence.isNotEmpty()) {
                        val c = sequence[sequence.length - 1]
                        val index = sequence[0]
                        if (c < '0' || c > '9' || index == '0') {
                            val text = sequence.subSequence(0, sequence.length - 1).toString()
                            et_search.setText(text)
                            et_search.setSelection(text.length)
                        }
                        val searchText = et_search.text.toString()
                        if (searchText.isNotEmpty()) {
                            val page = Integer.parseInt(searchText)
                            val pageCount = pdfInfoHandler?.getPdfPageCount() ?: 0
                            if (page > pageCount) {
                                val text = searchText.subSequence(0, searchText.length - 1).toString()
                                et_search.setText(text)
                                et_search.setSelection(text.length)
                            }
                        }
                    }
                } catch (e: Exception) {
                    e.printStackTrace()
                    et_search.setText("")
                }

            }
        })
    }

    override fun onClick(v: View) {

        when (v.id) {
            R.id.tv_fragmentPdfReaderSearch_page -> {
                if (!isPage) {
                    et_search.isLongClickable = false
                    tv_page.setTextColor(ContextCompat.getColor(context, R.color.reader_search_title_selected))
                    tv_text.setTextColor(ContextCompat.getColor(context, R.color.reader_search_title_normal))
                    et_search.setText("")
                    et_search.setHint(R.string.fragment_search_enter_page)
                    et_search.inputType = InputType.TYPE_CLASS_NUMBER
                    et_search.setSingleLine(true)

                    lv.visibility = View.GONE
                    tv_tishi.visibility = View.GONE

                    showSoftKeyboard()
                }

                isPage = true
            }
            R.id.tv_fragmentPdfReaderSearch_text -> {
                if (isPage) {
                    et_search.isLongClickable = true
                    tv_page.setTextColor(ContextCompat.getColor(context, R.color.reader_search_title_normal))
                    tv_text.setTextColor(ContextCompat.getColor(context, R.color.reader_search_title_selected))
                    et_search.setText("")
                    et_search.setHint(R.string.fragment_search_enter_text)
                    et_search.inputType = InputType.TYPE_CLASS_TEXT
                    et_search.setSingleLine(true)

                    lv.visibility = View.VISIBLE
                    tv_tishi.visibility = View.GONE

                    showSoftKeyboard()
                }

                isPage = false
            }
        }
    }

    private fun search() {
        val searchText = et_search.text.toString()
        if (searchText.isNotEmpty()) {
            if (isPage) {
                val page = Integer.parseInt(searchText)
                if (page > 0) {
                    pdfInfoHandler?.setCurrentPage(page - 1)
                }
            } else if (searchText.length >= 3) {
                if (list.size > 0) {
                    adapter.setSelected(-1)
                    adapter.clear()
                    list.clear()
                    adapter.notifyDataSetChanged()
                }

                isDone = false
                disposable = Completable.create {
                    if (null != pdfInfoHandler && null != searchHandler) {
                        val pageCount = pdfInfoHandler!!.getPdfPageCount()
                        for (i in 0 until pageCount) {
                            if (!isDone) {
                                val results = searchHandler!!.searchPage(i, searchText)
                                if (results.isNotEmpty()) {
                                    val chars = pdfInfoHandler!!.getPageChars(i)
                                    val list = dealData(chars, results, searchText, i)
                                    msgHandler?.sendMessage(msgHandler?.obtainMessage(10, list))
                                    if (this.list.size + list.size >= MAX_SEARCH_RESULT) {
                                        isDone = true
                                    }
                                }
                            }
                        }
                    }
                    it.onComplete()
                }
                        .subscribeOn(Schedulers.io())
                        .observeOn(AndroidSchedulers.mainThread())
                        .subscribe({
                            if (list.size < 1) {
                                lv.visibility = View.GONE
                                tv_tishi.visibility = View.VISIBLE
                            }
                        }, {
                            it.printStackTrace()
                        })
            }
        }
    }

    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        if (null != disposable && !disposable!!.isDisposed) {
            disposable?.dispose()
            disposable = null
        }
    }

    private fun dealData(chars: Array<Array<Array<Array<TextChar>>>>?, results: Array<RectF>?, search: String, page: Int): List<SearchResultInfo> {
        val list = ArrayList<SearchResultInfo>()
        if (chars == null || results == null) {
            return list
        }
        var lastResult = results[0]
        var currentWord = TextWord()
        var rectList = ArrayList<RectF>()
        var resultBlockStartIndex = 0
        var resultLineStartIndex = 0

        for (blockIndex in 0 until chars.size) {
            val block = chars[blockIndex]
            if (block.isEmpty()) continue
            for (lineIndex in 0 until block.size) {
                val line = block[lineIndex]
                if (line.isEmpty()) continue
                for (word in line) {
                    if (word.isEmpty()) continue
                    for (char in word) {

                        fun addSearchResult() {
                            val builder = StringBuilder("")
                            for (bIndex in resultBlockStartIndex .. blockIndex) {
                                for (lIndex in 0 until chars[bIndex].size) {
                                    if (bIndex == resultBlockStartIndex && lIndex < resultLineStartIndex) continue
                                    if (bIndex == blockIndex && lIndex > lineIndex) break
                                    if (chars[bIndex][lIndex].isEmpty()) continue
                                    for (w in chars[bIndex][lIndex]) {
                                        if (w.isEmpty()) continue
                                        for (c in w) {
                                            builder.append(c.c)
                                        }
                                    }
                                }
                            }
                            list.add(SearchResultInfo(page, search, rectList.toTypedArray(), builder.toString()))
                            currentWord = TextWord()
                            rectList = ArrayList()
                        }

                        var targetResult: RectF? = null
                        for (result in results) {
                            if (result.contains(char)) {
                                targetResult = result
                                break
                            }
                        }
                        if (null != targetResult) {
                            if (currentWord.w.contains(search, true) && targetResult != lastResult) {

                                lastResult = targetResult
                                addSearchResult()
                                if (this.list.size + list.size >= MAX_SEARCH_RESULT) {
                                    return list
                                }
                            }
                            if (currentWord.isEmpty) {
                                resultBlockStartIndex = blockIndex
                                resultLineStartIndex = lineIndex
                            }
                            currentWord.Add(char)
                            rectList.add(char)
                        } else if (currentWord.w.contains(search, true)) {
                            addSearchResult()
                            if (this.list.size + list.size >= MAX_SEARCH_RESULT) {
                                return list
                            }
                        }
                    }
                }
            }
        }
        return list
    }

    private fun showSoftKeyboard() {
        val inputMethodManager = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
        et_search.requestFocus()
        inputMethodManager.showSoftInput(et_search, InputMethodManager.RESULT_UNCHANGED_SHOWN)
    }

    fun hideSoftKeyboard() {
        val inputMethodManager = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
        inputMethodManager.hideSoftInputFromWindow(et_search.windowToken, InputMethodManager.HIDE_NOT_ALWAYS)
    }

    override fun updateContent() {
        search()
    }

    override fun onScrollToContent(pageIndex: Int, notify: Boolean) {

    }
}