/*
 * Lunar Launcher
 * Copyright (C) 2022 Md Rasel Hossain
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package rasel.lunar.launcher.feeds

import android.app.Activity.RESULT_CANCELED
import android.app.Activity.RESULT_OK
import android.appwidget.AppWidgetManager
import android.content.DialogInterface
import android.content.Intent
import android.content.SharedPreferences
import android.net.Uri
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.ResultReceiver
import android.text.method.ScrollingMovementMethod
import android.view.ContextMenu
import android.view.Gravity
import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.PopupMenu
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.google.android.material.button.MaterialButtonToggleGroup
import com.google.gson.Gson
import io.realm.kotlin.ext.query
import io.realm.kotlin.query.Sort
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import org.jsoup.Jsoup
import rasel.lunar.launcher.CommadCallabck
import rasel.lunar.launcher.LauncherActivity.Companion.appWidgetHost
import rasel.lunar.launcher.LauncherActivity.Companion.appWidgetManager
import rasel.lunar.launcher.LauncherActivity.Companion.lActivity
import rasel.lunar.launcher.LauncherActivity.Companion.refreshDeviceData
import rasel.lunar.launcher.LauncherActivity.Companion.refreshFeeds
import rasel.lunar.launcher.R
import rasel.lunar.launcher.databinding.FeedsBinding
import rasel.lunar.launcher.feeds.rss.RssAdapter
import rasel.lunar.launcher.helpers.Constants.Companion.KEY_WIDGET_HEIGHTS
import rasel.lunar.launcher.helpers.Constants.Companion.KEY_WIDGET_IDS
import rasel.lunar.launcher.helpers.Constants.Companion.PREFS_WIDGETS
import rasel.lunar.launcher.helpers.Constants.Companion.SEPARATOR
import rasel.lunar.launcher.helpers.Constants.Companion.requestCreateWidget
import rasel.lunar.launcher.helpers.Constants.Companion.requestPickWidget
import rasel.lunar.launcher.home.LauncherHome.Companion.home
import rasel.lunar.launcher.home.LauncherHome.Companion.lastedFinishedPageUrl
import rasel.lunar.launcher.home.LauncherHome.Companion.listTags
import rasel.lunar.launcher.model.CiliMagnet
import rasel.lunar.launcher.model.MostItem
import rasel.lunar.launcher.model.RssData
import rasel.lunar.launcher.model.RssDataInterface
import rasel.lunar.launcher.model.RssDataType
import rasel.lunar.launcher.model.jGuruTag
import rasel.lunar.launcher.model.dateFormat
import rasel.lunar.launcher.model.getRssData
import rasel.lunar.launcher.utils.BLog
import rasel.lunar.launcher.utils.JamoUtils
import rasel.lunar.launcher.utils.RssList.jGuruMain
import rasel.lunar.launcher.utils.USAGT
import rasel.lunar.launcher.utils.getJ
import rasel.lunar.launcher.workers.BaseGetter
import rasel.lunar.launcher.workers.RecentCallGetter
import rasel.lunar.launcher.workers.RecentSmsGetter
import rasel.lunar.launcher.workers.WorkersDb
import java.net.URLEncoder
import java.nio.charset.Charset
import java.text.SimpleDateFormat
import java.util.Date
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit


internal class Feeds : Fragment() , CommadCallabck {

    private lateinit var binding: FeedsBinding
    private val requestCodeString = "requestCode"
    var mRssAdapter : RssAdapter<RssDataInterface>? = null
    var mRssAdapter2 : RssAdapter<jGuruTag>? = null
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
        binding = FeedsBinding.inflate(inflater, container, false)
        mRssAdapter = RssAdapter(requireContext())
        mRssAdapter2 = RssAdapter(requireContext())
        binding.feedsRss.rss.adapter = mRssAdapter
        binding.feedsRss.rss2.adapter = mRssAdapter2
        binding.consoleLog.movementMethod = ScrollingMovementMethod();
        updateWidgets()
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        expandCollapse()
        systemInfo()
    }

    override fun onResume() {
        super.onResume()
        registerForContextMenu(binding.widgetContainer)
    }

    override fun onPause() {
        super.onPause()
        unregisterForContextMenu(binding.widgetContainer)
        if (binding.expandRss.isChecked)
            binding.expandRss.isChecked = false
    }

    override fun onCreateContextMenu(menu: ContextMenu, v: View, menuInfo: ContextMenu.ContextMenuInfo?) {
        super.onCreateContextMenu(menu, v, menuInfo)
        menu.clearHeader()
        lActivity!!.menuInflater.inflate(R.menu.add_widget, menu)
    }

    override fun onContextItemSelected(item: MenuItem): Boolean {
        if (item.itemId == R.id.add_widget) selectWidget()
        return super.onContextItemSelected(item)
    }

    /* control view's expand-collapse actions */
    private fun expandCollapse() {
        binding.expandableButtons.addOnButtonCheckedListener { _: MaterialButtonToggleGroup?, checkedId: Int, isChecked: Boolean ->
            if (isChecked) {
                when (checkedId) {
                    binding.expandRss.id -> {
                        binding.feedsRss.rss.visibility = View.GONE
                        binding.feedsRss.rss2.visibility = View.GONE
                        binding.feedsSysInfos.expandableSystemInfo.collapse()
                        binding.feedsRss.expandableRss.expand()
                        startService()
                    }
                    binding.expandSystemInfo.id -> {
                        binding.feedsRss.expandableRss.collapse()
                        binding.feedsSysInfos.expandableSystemInfo.expand()
                    }
                }
            } else {
                when (checkedId) {
                    binding.expandRss.id -> binding.feedsRss.expandableRss.collapse()
                    binding.expandSystemInfo.id -> binding.feedsSysInfos.expandableSystemInfo.collapse()
                }
            }
        }
    }

    fun consoleLog(str : String) {
        mMainHandler.removeCallbacks(hideConsole)
        binding.consoleLog.post {
            binding.consoleLog.visibility = View.VISIBLE
            binding.consoleLog.text = str
        }
        mMainHandler.postDelayed(hideConsole,10000L)
        BLog.LOGE("consoleLog >>>> ${str}")

    }

    fun openOpera(schemeString : String) {
        BLog.LOGE("openOpera ${schemeString}")
        val gmmIntentUri = Uri.parse(schemeString)
        val mapIntent = Intent(Intent.ACTION_VIEW, gmmIntentUri)
        mapIntent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
        mapIntent.setPackage("com.opera.browser")
        lActivity?.startActivity(mapIntent)
    }

    override fun onConsoleLog(log: String) {
        consoleLog(log)
    }


    val mMainHandler = Handler(Looper.getMainLooper())
    val hideConsole = {
        binding.consoleLog.visibility = View.GONE
        binding.consoleLog.text = ""
    }
    override fun collectComplete() {

    }


    /* start rss service if network is active and rss url is not empty */
    private fun startService() {
        try {
            System.gc()
        }catch (e : Exception){e.printStackTrace()}
        binding.feedsRss.rss.visibility = View.GONE
        binding.feedsRss.rss2.visibility = View.GONE
        binding.feedsRss.loading.visibility = View.VISIBLE
        binding.feedsRss.refresh.visibility = View.VISIBLE
        val builder: AlertDialog.Builder = AlertDialog.Builder(requireContext())
        builder.setTitle("Command Line")
        val viewInflated: View = LayoutInflater.from(context)
            .inflate(R.layout.text_inpu_password, view as ViewGroup?, false)
        val input = viewInflated.findViewById<View>(R.id.input) as EditText
        builder.setView(viewInflated)
        builder.setPositiveButton(android.R.string.ok,
            DialogInterface.OnClickListener { dialog, which ->
                dialog.dismiss()
                BLog.LOGE("input.text.toString() >>>> ${input.text.toString()}")
                if (input.text.toString().trim().contains(" ")) {
                    val cmd = input.text.toString().trim().split(" ")
                    when(cmd[0]) {
                        "s" -> {
                            home?.queryInfos(keyword = cmd[1])
                        }
                        "jf" -> {
                            consoleLog("on Cmd JF")
                            GlobalScope.launch { excuteGetterMostByUrl("https://javmost.to/search/movie/${cmd[1]}") }
                        }
                        "mgn"-> {
//                            lActivity?.doWebParseStart("https://cili.site/search?q=${URLEncoder.encode(cmd[1], Charset.defaultCharset().name())}") {}
                            GlobalScope.launch {
                                var temp = arrayListOf<CiliMagnet>()
                                consoleLog("this >>>> cili ${cmd[0]} -> ${cmd[1]}")
                                Jsoup.connect("https://cili.site/search?q=${URLEncoder.encode(cmd[1], Charset.defaultCharset().name())}").get().let { cili ->
                                    consoleLog("this >>>> cili ${cili.title()}")
                                    cili.getElementsByTag("tr").forEach { cili_tr ->
                                        CiliMagnet().let { ciliMgn ->
                                            ciliMgn.link = if(cili_tr.getElementsByTag("a").size > 0)cili_tr.getElementsByTag("a").get(0).attr("href") else ""
                                            ciliMgn.title = if(cili_tr.getElementsByTag("p").size > 0)cili_tr.getElementsByTag("p").text() else ""
                                            ciliMgn.size = if(cili_tr.getElementsByClass("td-size").size > 0)cili_tr.getElementsByClass("td-size").text() else ""
                                            if(ciliMgn.isValid() && temp.size < 8 ) {
                                                Jsoup.connect(ciliMgn.getMagnetPageLink()).get().let { mgn_Page ->
                                                    consoleLog("magnet_page >>> ${mgn_Page.title()}")
                                                    if (mgn_Page.getElementsByClass("input-group magnet-box").size > 0) {
                                                        mgn_Page.getElementsByClass("input-group magnet-box")
                                                            .get(0)?.let { magnet_box ->
//                                                            BLog.LOGE("magnet_box >>> ${magnet_box}")
//                                                        if (magnet_box.getElementById("input-magnet").size > 0) {
                                                                magnet_box.getElementById(
                                                                    "input-magnet"
                                                                )?.let { input_magnet ->
//                                                                    BLog.LOGE("input_magnet >>> ${input_magnet}")
                                                                    ciliMgn.magnetLink = input_magnet.attr("value").replace("&amp;","&")
                                                                }
                                                            }
                                                    }
                                                }.apply {
                                                    temp.add(ciliMgn)
                                                }
                                            }
                                        }

                                    }.apply {
                                        temp.forEach {
                                            consoleLog("ciliResult >>> ${Gson().toJson(it)}")
                                        }
                                    }
                                }
                            }.start()
                        }

                    }
                    binding.expandRss.isChecked = false
                } else {
                    when (input.text.toString()) {
                        "so" -> GlobalScope.launch {
                            "https://kr69.sogirl.so".getJ().let { doc ->
                                BLog.LOGE("ogirl >>> ${doc.title()}")
                                doc.getElementsByTag("article").forEach { article ->
                                    BLog.LOGE("ogirl article >>> ${article.text()}")
                                    val title = article.getElementsByTag("a").get(0).attr("title")
                                    val href = article.getElementsByTag("a").get(0).attr("href")
                                    val img = article.getElementsByTag("img").get(0).attr("data-src")
                                    WorkersDb.getRealm().writeBlocking {
                                        if (query<RssData>("originPage == $0", href).find().size == 0) {
                                            RssData().apply {
                                                this.originPage = href
                                                this.title = title
                                                this.description = "Sogirl"
                                                this.thumbnail = img
                                                this.pubDate = Date().time
                                                this.category = RssDataType.GURU.name
                                                this.chosung =
                                                    JamoUtils.split(title).joinToString("")
                                                copyToRealm(this)
                                            }
                                        }
                                    }
                                }
                            }
                            consoleLog("current j req() ${WorkersDb.getRealm()
                                .query<RssData>("category == $0", RssDataType.GURU.name).find().size}")
                            val urls = arrayOf("https://javmost.to/latest-updates",
                                "https://javmost.to/latest-updates/page-2",
                                "https://javmost.to/latest-updates/page-3",
                                "https://javmost.to/latest-updates/page-4",
                                "https://javmost.to/latest-updates/page-5")
                            urls.forEach { mostUrl ->
                                GlobalScope.launch {
                                    excuteGetterMostByUrl(mostUrl)
                                }.start()
                            }
                        }
                        "ojs" -> home?.queryInfos(arrayListOf<RssDataType>().apply {
                            addAll(RssDataType.values())
                            remove(RssDataType.GURU)
                            remove(RssDataType.Most)
                        })
                        "all" -> home?.queryInfos(arrayListOf<RssDataType>().apply {

                        })
                        "onews" -> home?.queryInfos(arrayListOf<RssDataType>().apply {
                            addAll(RssDataType.values())
                            remove(RssDataType.NewsFeed)
                        })
                        "ored" -> home?.queryInfos(arrayListOf<RssDataType>().apply {
                            addAll(RssDataType.values())
                            remove(RssDataType.REDDIT)
                        })


                        "req" -> {
                            refreshFeeds()
                            refreshDeviceData()
                            consoleLog("excute refreshFeeds()")
                        }
                        "reqmax" -> {
                            refreshFeeds()
                            RecentCallGetter.dayRange = 30
                            RecentSmsGetter.dayRange = 30
                            refreshDeviceData()
                            consoleLog("excute refreshFeeds()")
                        }

                        "taxi" -> {
                            consoleLog("before run State home?.binding?.alcholKatalkT?.isVisible >> ${home?.binding?.alcholKatalkT?.isVisible}")
                            home?.showAl()
                            consoleLog("after run State home?.binding?.alcholKatalkT?.isVisible >> ${home?.binding?.alcholKatalkT?.isVisible}")
                        }

                        "tax" -> {
                            consoleLog("before run State home?.binding?.alcholKatalkT?.isVisible >> ${home?.binding?.alcholKatalkT?.isVisible}")
                            home?.hideAl()
                            consoleLog("after run State home?.binding?.alcholKatalkT?.isVisible >> ${home?.binding?.alcholKatalkT?.isVisible}")
                        }

                        "jshow" -> {
                            binding.feedsRss.apply {
                                rss.adapter = mRssAdapter
                                loading.visibility = View.VISIBLE
                                mRssAdapter?.updateData(WorkersDb.getRealm()
                                    .query<RssData>("category == $0 || category == $1", RssDataType.GURU.name, RssDataType.Most.name)
                                    .sort("pubDate", Sort.DESCENDING).find())
                                rss.visibility = View.VISIBLE
                                loading.visibility = View.GONE
                                refresh.visibility = View.GONE
                            }
                        }

                        "jjp" -> {
//                            lActivity?.doWebParseStart("https://projectjav.com") {}
                        }
                        "jmnew" -> {
//                            lActivity?.doWebParseStart("https://missav.com/dm507/en/release") {}
                        }
                        "jmiss" -> {
//                            lActivity?.doWebParseStart("https://missav.com/dm16/en") {}
                        }
                        "jreq" -> {
                            consoleLog("current j req() ${WorkersDb.getRealm()
                                .query<RssData>("category == $0", RssDataType.GURU.name).find().size}")
                            lActivity?.doWebParseStart(jGuruMain,callBack = object : CommadCallabck {
                                override fun onConsoleLog(log: String) {
                                    this@Feeds.consoleLog(log)
                                }

                                override fun collectComplete() {
                                    consoleLog("excuted j req() ${WorkersDb.getRealm()
                                        .query<RssData>("category == $0", RssDataType.GURU.name).find().size}")
                                }
                            })
//                            {
//                                consoleLog("excuted j req() ${WorkersDb.getRealm()
//                                    .query<RssData>("category == $0", RssDataType.GURU.name).find().size}")
//                            }
                        }

                        "jtag" -> {
//                        lActivity?.doWebPare(TEST_PAG.plus("tags")) {
                            binding.feedsRss.apply {
                                rss2.adapter = mRssAdapter2

                                loading.visibility = View.VISIBLE
                                if (listTags.size > 0) {
                                    mRssAdapter2?.updateData(listTags)
                                    rss2.visibility = View.VISIBLE
                                    loading.visibility = View.GONE
                                    refresh.visibility = View.GONE
                                } else {
                                    refresh.visibility = View.VISIBLE
                                    rss2.visibility = View.GONE
                                    refresh.setOnClickListener {
                                        lActivity?.doWebParseStart(jGuruMain.plus("tags"), callBack = object : CommadCallabck {
                                            override fun onConsoleLog(log: String) {
                                                this@Feeds.consoleLog(log)
                                            }

                                            override fun collectComplete() {
                                                if (listTags.size > 0) {
                                                    rss2?.postDelayed({
                                                        mRssAdapter2?.updateData(listTags)
                                                        loading.visibility = View.GONE
                                                        refresh.visibility = View.GONE
                                                        rss2.visibility = View.VISIBLE
                                                    }, 500L)
                                                }
                                            }
                                        })
                                    }
                                }
                            }
//                        }
                        }

                        else -> {
                            binding.expandRss.isChecked = false
                        }
                    }
                }

            })
        builder.setNegativeButton(android.R.string.cancel,
            DialogInterface.OnClickListener { dialog, which -> dialog.cancel() })

        builder.show()
//        binding.feedsRss.apply {
//            if(rss.adapter != null) {
//                (rss.adapter as RssAdapter).items.clear()
//            }
//        }
//        val rssUrl = lActivity!!.getSharedPreferences(PREFS_SETTINGS, 0)
//            .getString(KEY_RSS_URL, "")
//        when {
//            isNetworkAvailable && !rssUrl.isNullOrEmpty() -> {
////                Intent(lActivity!!, RssService::class.java)
////                    .putExtra(RSS_RECEIVER, resultReceiver).let {
////                        enqueueWork(lActivity!!, RssService::class.java, rssJobId, it)
////                    }
//            }
//            else -> resumeService()
//        }
//        val rssUrl2 = lActivity!!.getSharedPreferences(PREFS_SETTINGS, 0)
//            .getString(KEY_RSS_URL2, "")
//        when {
//            isNetworkAvailable && !rssUrl2.isNullOrEmpty() -> {
//                Intent(lActivity!!, RssService::class.java)
//                    .putExtra(RSS_RECEIVER, resultReceiver).let {
//                        enqueueWork(lActivity!!, RssService::class.java, rssJobId, it)
//                    }
//            }
//            else -> resumeService()
//        }
    }

    /* retry to start rss service */
    private fun resumeService() {
        binding.feedsRss.apply {
            rss.visibility = View.GONE
            loading.visibility = View.GONE
            refresh.visibility = View.VISIBLE
            refresh.setOnClickListener {
                mRssAdapter?.notifyDataSetChanged()
            }
        }
    }

    var dmy = SimpleDateFormat("dd-MM-yyyy")
    fun excuteGetterMostByUrl(mostUrl : String) {
        Executors.newSingleThreadScheduledExecutor().schedule({
        Jsoup.connect(mostUrl).userAgent(USAGT).get().let { doc ->
            onConsoleLog("$lastedFinishedPageUrl >>> ${doc.title()}")
            doc.getElementsByClass("card").forEach { card ->
                onConsoleLog("${lastedFinishedPageUrl}_card >>> ${card}")
                var thumb = if(card.getElementsByTag("img").size > 0) card.getElementsByTag("img").get(0).attr("src") else ""
                if (thumb.contains("No+Poster")) thumb = if(card.getElementsByTag("img").size > 0) card.getElementsByTag("img").get(0).attr("data-src") else thumb
                var model = if(card.getElementsByTag("img").size > 0) card.getElementsByTag("img").get(0).attr("alt") else ""
                var title = ""
                var date = ""
                var link = ""
                if(card.getElementsByClass("card-block").size > 0) if(card.getElementsByClass("card-block").size > 0) {
                    link = card.getElementsByClass("card-block").get(0).getElementsByTag("a").get(0).attr("href")
                    title = card.getElementsByClass("card-block").get(0).getElementsByTag("a").get(0).attr("title")
                    date  = card.getElementsByTag("span").get(0).text()
                }


                MostItem().let { ms ->
                    ms.model = model
                    ms.image = thumb
                    ms.pageLink = link
                    ms.title = title
                    try {
                        ms.date = dmy.parse(date).time
                        consoleLog("dateFormat.format(Date(ms.date)) ${dateFormat.format(Date(ms.date))}")
                    }catch (e : Exception) {e.printStackTrace()}
                    if (ms.isValid()) {
                        WorkersDb.insertData(ms.getRssData())
                    }
                }

                onConsoleLog("" +
                        "model >>>>> ${model}\n" +
                        "thumb >>>>> ${thumb}\n" +
                        "title >>>>> ${title}\n" +
                        "date >>>>> ${date}" +
                        "")
            }
            consoleLog("excuted j req() ${WorkersDb.getRealm()
                .query<RssData>("category == $0", RssDataType.GURU.name).find().size}")
        }
        }, 1500, TimeUnit.MILLISECONDS)
    }

    /* rss service's result receiver */
    @Suppress("UNCHECKED_CAST")
    private val resultReceiver: ResultReceiver = object : ResultReceiver(Handler(Looper.getMainLooper())) {
//        override fun onReceiveResult(resultCode: Int, resultData: Bundle) {
//            when (val items = resultData.getSerializable(RSS_ITEMS) as List<Rss>?) {
//                null -> resumeService()
//                else -> {
//                    binding.feedsRss.apply {
//                        if(rss.adapter != null) {
//                            (rss.adapter as RssAdapter).items.addAll(items)
//                        }
//                        refresh.visibility = View.GONE
//                        loading.visibility = View.GONE
//                        rss.visibility = View.VISIBLE
//                    }
//                }
//            }
//        }
    }

    private fun systemInfo() {
        viewLifecycleOwner.lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.RESUMED) {
                SystemStats().apply {
                    intStorage(binding.feedsSysInfos.intParent)
                    extStorage(binding.feedsSysInfos.extParent)
                    while (isActive) {
                        ram(binding.feedsSysInfos.ramParent)
                        cpu(binding.feedsSysInfos.cpuParent)
                        misc(binding.feedsSysInfos.misc)
                        delay(1000)
                    }
                }
            }
        }
    }

    private fun selectWidget() {
        Intent(AppWidgetManager.ACTION_APPWIDGET_PICK).apply {
            putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetHost?.allocateAppWidgetId())
            putParcelableArrayListExtra(AppWidgetManager.EXTRA_CUSTOM_INFO, ArrayList())
            putParcelableArrayListExtra(AppWidgetManager.EXTRA_CUSTOM_EXTRAS, ArrayList())
            putExtra(requestCodeString, requestPickWidget)
        }.let { widgetPicker.launch(it) }
    }

    private val widgetPicker =
        registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
            val data = result.data
            val appWidgetId = data?.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1)
            if (result.resultCode == RESULT_OK) {
                when (data?.getIntExtra(requestCodeString, requestPickWidget)) {
                    requestPickWidget -> configureWidget(appWidgetId!!)
                    requestCreateWidget -> createWidget(appWidgetId!!, null)
                }
            } else if (result.resultCode == RESULT_CANCELED && data != null) {
                if (appWidgetId != -1) appWidgetHost?.deleteAppWidgetId(appWidgetId!!)
            }
        }

    private fun configureWidget(appWidgetId: Int) {
        when (val appWidgetConfig = appWidgetManager!!.getAppWidgetInfo(appWidgetId).configure) {
            null -> createWidget(appWidgetId, null)
            else -> {
                Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE).apply {
                    component = appWidgetConfig
                    putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
                    putExtra(requestCodeString, requestCreateWidget)
                }.let {
                    try { widgetPicker.launch(it) }
                    catch (e: Exception) { e.printStackTrace() }
                }
            }
        }
    }

    private fun createWidget(appWidgetId: Int, height: Int?) {
        if (appWidgetId == -1) return

        val appWidgetInfo = appWidgetManager!!.getAppWidgetInfo(appWidgetId)
        val params: ConstraintLayout.LayoutParams?

        when (height) {
            null -> {
                params = ConstraintLayout.LayoutParams(ConstraintLayout.LayoutParams.MATCH_PARENT, appWidgetInfo.minHeight)
                val updatedIds = splitWidgetIds.plus("$appWidgetId")
                val updatedHeights = splitWidgetHeights.plus("${appWidgetInfo.minHeight}")
                saveWidgetData(updatedIds, updatedHeights)
            }
            else -> params = ConstraintLayout.LayoutParams(ConstraintLayout.LayoutParams.MATCH_PARENT, height)
        }

        (appWidgetHost?.createView(lActivity!!.applicationContext, appWidgetId, appWidgetInfo) as WidgetHostView)
            .apply {
                setAppWidget(appWidgetId, appWidgetInfo)
            }.let {
                binding.widgetContainer.addView(it, params)
                widgetMenu(it)
            }
    }

    private fun updateWidgets() {
        if (splitWidgetIds.size > 0) {
            viewLifecycleOwner.lifecycleScope.launch {
                binding.widgetContainer.removeAllViews()
                splitWidgetIds.indices.forEach { i: Int ->
                    createWidget(splitWidgetIds[i]!!.int(), splitWidgetHeights[i]!!.int())
                }
            }
        }
    }

    private fun widgetMenu(hostView: WidgetHostView) {
        val appWidgetId = hostView.appWidgetId
        hostView.setOnLongClickListener {
            PopupMenu(requireContext(), it, Gravity.END).apply {
                menuInflater.inflate(R.menu.widget_menu, this.menu)
                show()
                setOnMenuItemClickListener { menuItem ->
                    when (menuItem.itemId) {
                        R.id.move_up -> moveWidget(appWidgetId, true)
                        R.id.move_down -> moveWidget(appWidgetId, false)
                        R.id.increase_height -> resizeWidget(appWidgetId, true)
                        R.id.decrease_height -> resizeWidget(appWidgetId, false)
                        R.id.delete_widget -> removeWidget(it as WidgetHostView)
                    }
                    false
                }
            }
            true
        }
    }

    private fun moveWidget(widgetId: Int, moveUp: Boolean) {
        val tempIds = splitWidgetIds
        val tempHeights = splitWidgetHeights

        splitWidgetIds.indexOf(widgetId.toString()).let { i ->
            when {
                moveUp && i > 0 -> {
                    tempIds.swap(i-1, i)
                    tempHeights.swap(i-1, i)
                }
                !moveUp && i < splitWidgetIds.size - 1 -> {
                    tempIds.swap(i, i+1)
                    tempHeights.swap(i, i+1)
                }
                else -> return
            }
        }

        saveWidgetData(tempIds, tempHeights)
        updateWidgets()
    }

    private fun resizeWidget(widgetId: Int, shouldAdd: Boolean) {
        val tempList = splitWidgetHeights

        splitWidgetIds.indexOf(widgetId.toString()).let { i ->
            tempList[i] = when (shouldAdd) {
                true -> (splitWidgetHeights[i]!!.int().plus(50)).toString()
                false -> (splitWidgetHeights[i]!!.int().minus(50)).toString()
            }
        }

        widgetPref.edit().putString(KEY_WIDGET_HEIGHTS, tempList.joinToString(separator = SEPARATOR)).apply()
        updateWidgets()
    }

    private fun removeWidget(hostView: WidgetHostView) {
        hostView.let { v ->
            appWidgetHost?.deleteAppWidgetId(v.appWidgetId)
            binding.widgetContainer.removeView(v)

            splitWidgetIds.indexOf(v.appWidgetId.toString()).let { i ->
                saveWidgetData(splitWidgetIds.minus(splitWidgetIds[i]), splitWidgetHeights.minus(splitWidgetHeights[i]))
            }
        }
    }

    private fun saveWidgetData(idList: List<String?>, heightList: List<String?>) {
        widgetPref.edit()
            .putString(KEY_WIDGET_IDS, idList.joinToString(separator = SEPARATOR))
            .putString(KEY_WIDGET_HEIGHTS, heightList.joinToString(separator = SEPARATOR))
            .apply()
    }

    private val widgetPref: SharedPreferences get() = lActivity!!.getSharedPreferences(PREFS_WIDGETS, 0)
    private val widgetIds: String? get() = widgetPref.getString(KEY_WIDGET_IDS, "")
    private val widgetHeights: String? get() = widgetPref.getString(KEY_WIDGET_HEIGHTS, "")
    private val splitWidgetIds: MutableList<String?> get() = widgetIds!!.split(SEPARATOR).toMutableList()
    private val splitWidgetHeights: MutableList<String?> get() = widgetHeights!!.split(SEPARATOR).toMutableList()

    private fun <T> MutableList<T>.swap(index1: Int, index2: Int){
        val temp = this[index1]
        this[index1] = this[index2]
        this[index2] = temp
    }

    private fun String.int() : Int {
        return try {
            this.toInt()
        } catch (e: Exception) {
            -1
        }
    }

}
