package eu.kanade.tachiyomi.ui.library

import android.content.Context
import android.util.AttributeSet
import android.view.View
import com.bluelinelabs.conductor.Router
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.preference.PreferenceValues.DisplayMode
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.widget.ExtendedNavigationView
import eu.kanade.tachiyomi.widget.ExtendedNavigationView.Item.TriStateGroup.Companion.STATE_EXCLUDE
import eu.kanade.tachiyomi.widget.ExtendedNavigationView.Item.TriStateGroup.Companion.STATE_IGNORE
import eu.kanade.tachiyomi.widget.ExtendedNavigationView.Item.TriStateGroup.Companion.STATE_INCLUDE
import eu.kanade.tachiyomi.widget.TabbedBottomSheetDialog
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy

class LibrarySettingsSheet(
    router: Router,
    onGroupClickListener: (ExtendedNavigationView.Group) -> Unit
) : TabbedBottomSheetDialog(router) {

    val filters: Filter
    private val sort: Sort
    private val display: Display
    private val grouping: Grouping

    init {
        filters = Filter(router.activity!!)
        filters.onGroupClicked = onGroupClickListener

        sort = Sort(router.activity!!)
        sort.onGroupClicked = onGroupClickListener

        display = Display(router.activity!!)
        display.onGroupClicked = onGroupClickListener

        grouping = Grouping(router.activity!!)
        grouping.onGroupClicked = onGroupClickListener
    }

    fun refreshSort() {
        sort.refreshMode()
    }

    override fun getTabViews(): List<View> = listOf(
        filters,
        sort,
        display,
        grouping
    )

    override fun getTabTitles(): List<Int> = listOf(
        R.string.action_filter,
        R.string.action_sort,
        R.string.action_display,
        R.string.group
    )

    /**
     * Filters group (unread, downloaded, ...).
     */
    inner class Filter @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
        Settings(context, attrs) {

        private val filterGroup = FilterGroup()

        init {
            setGroups(listOf(filterGroup))
        }

        /**
         * Returns true if there's at least one filter from [FilterGroup] active.
         */
        fun hasActiveFilters(): Boolean {
            return filterGroup.items.any { it.state != STATE_IGNORE }
        }

        inner class FilterGroup : Group {

            private val downloaded = Item.TriStateGroup(R.string.action_filter_downloaded, this)
            private val unread = Item.TriStateGroup(R.string.action_filter_unread, this)
            private val completed = Item.TriStateGroup(R.string.completed, this)
            private val started = Item.TriStateGroup(R.string.started, this)
            private val tracked = Item.TriStateGroup(R.string.tracked, this)
            private val lewd = Item.TriStateGroup(R.string.lewd, this)

            override val header = null
            // SY -->
            override val items = (
                if (Injekt.get<TrackManager>().hasLoggedServices()) {
                    listOf(downloaded, unread, completed, started, tracked, lewd)
                } else {
                    listOf(downloaded, unread, completed, started, lewd)
                }
                )
            // SY <--
            override val footer = null

            override fun initModels() {
                if (preferences.downloadedOnly().get()) {
                    downloaded.state = STATE_INCLUDE
                    downloaded.enabled = false
                } else {
                    downloaded.state = preferences.filterDownloaded().get()
                }
                unread.state = preferences.filterUnread().get()
                completed.state = preferences.filterCompleted().get()
            }

            override fun onItemClicked(item: Item) {
                item as Item.TriStateGroup
                val newState = when (item.state) {
                    STATE_IGNORE -> STATE_INCLUDE
                    STATE_INCLUDE -> STATE_EXCLUDE
                    STATE_EXCLUDE -> STATE_IGNORE
                    else -> throw Exception("Unknown State")
                }
                item.state = newState
                when (item) {
                    downloaded -> preferences.filterDownloaded().set(newState)
                    unread -> preferences.filterUnread().set(newState)
                    completed -> preferences.filterCompleted().set(newState)
                    started -> preferences.filterStarted().set(newState)
                    tracked -> preferences.filterTracked().set(newState)
                    lewd -> preferences.filterLewd().set(newState)
                }

                adapter.notifyItemChanged(item)
            }
        }
    }

    /**
     * Sorting group (alphabetically, by last read, ...) and ascending or descending.
     */
    inner class Sort @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
        Settings(context, attrs) {

        init {
            setGroups(listOf(SortGroup()))
        }

        // SY -->
        fun refreshMode() {
            recycler.adapter = null
            removeView(recycler)
            setGroups(listOf(SortGroup()))
        }
        // SY <--

        inner class SortGroup : Group {

            private val alphabetically = Item.MultiSort(R.string.action_sort_alpha, this)
            private val total = Item.MultiSort(R.string.action_sort_total, this)
            private val lastRead = Item.MultiSort(R.string.action_sort_last_read, this)
            private val lastChecked = Item.MultiSort(R.string.action_sort_last_checked, this)
            private val unread = Item.MultiSort(R.string.action_filter_unread, this)
            private val latestChapter = Item.MultiSort(R.string.action_sort_latest_chapter, this)
            private val dateAdded = Item.MultiSort(R.string.action_sort_date_added, this)
            // SY -->
            private val dragAndDrop = Item.MultiSort(R.string.action_sort_drag_and_drop, this)
            private val tagList = Item.MultiSort(R.string.tag_sorting, this)
            // SY <--

            override val header = null
            override val items =
                listOf(alphabetically, lastRead, lastChecked, unread, total, latestChapter, dateAdded /* SY --> */, dragAndDrop) + if (preferences.sortTagsForLibrary().get().isNotEmpty()) listOf(tagList) else emptyList() /* SY <-- */
            override val footer = null

            override fun initModels() {
                val sorting = preferences.librarySortingMode().get()
                val order = if (preferences.librarySortingAscending().get()) {
                    Item.MultiSort.SORT_ASC
                } else {
                    Item.MultiSort.SORT_DESC
                }

                alphabetically.state =
                    if (sorting == LibrarySort.ALPHA) order else Item.MultiSort.SORT_NONE
                lastRead.state =
                    if (sorting == LibrarySort.LAST_READ) order else Item.MultiSort.SORT_NONE
                lastChecked.state =
                    if (sorting == LibrarySort.LAST_CHECKED) order else Item.MultiSort.SORT_NONE
                unread.state =
                    if (sorting == LibrarySort.UNREAD) order else Item.MultiSort.SORT_NONE
                total.state =
                    if (sorting == LibrarySort.TOTAL) order else Item.MultiSort.SORT_NONE
                latestChapter.state =
                    if (sorting == LibrarySort.LATEST_CHAPTER) order else Item.MultiSort.SORT_NONE
                dateAdded.state =
                    if (sorting == LibrarySort.DATE_ADDED) order else Item.MultiSort.SORT_NONE
                // SY -->
                dragAndDrop.state = if (sorting == LibrarySort.DRAG_AND_DROP) order else Item.MultiSort.SORT_NONE
                tagList.state =
                    if (sorting == LibrarySort.TAG_LIST) order else Item.MultiSort.SORT_NONE
                // SY <--
            }

            override fun onItemClicked(item: Item) {
                item as Item.MultiStateGroup
                // SY -->
                if (item == dragAndDrop && preferences.groupLibraryBy().get() != LibraryGroup.BY_DEFAULT) return
                // SY <--
                val prevState = item.state

                item.group.items.forEach {
                    (it as Item.MultiStateGroup).state =
                        Item.MultiSort.SORT_NONE
                }
                // SY -->
                if (item == dragAndDrop) {
                    item.state = Item.MultiSort.SORT_ASC
                } else {
                    item.state = when (prevState) {
                        Item.MultiSort.SORT_NONE -> Item.MultiSort.SORT_ASC
                        Item.MultiSort.SORT_ASC -> Item.MultiSort.SORT_DESC
                        Item.MultiSort.SORT_DESC -> Item.MultiSort.SORT_ASC
                        else -> throw Exception("Unknown state")
                    }
                }
                // SY <--

                preferences.librarySortingMode().set(
                    when (item) {
                        alphabetically -> LibrarySort.ALPHA
                        lastRead -> LibrarySort.LAST_READ
                        lastChecked -> LibrarySort.LAST_CHECKED
                        unread -> LibrarySort.UNREAD
                        total -> LibrarySort.TOTAL
                        latestChapter -> LibrarySort.LATEST_CHAPTER
                        dateAdded -> LibrarySort.DATE_ADDED
                        // SY -->
                        dragAndDrop -> LibrarySort.DRAG_AND_DROP
                        tagList -> LibrarySort.TAG_LIST
                        // SY <--
                        else -> throw Exception("Unknown sorting")
                    }
                )
                preferences.librarySortingAscending().set(item.state == Item.MultiSort.SORT_ASC)

                item.group.items.forEach { adapter.notifyItemChanged(it) }
            }
        }
    }

    /**
     * Display group, to show the library as a list or a grid.
     */
    inner class Display @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
        Settings(context, attrs) {

        init {
            setGroups(listOf(DisplayGroup(), BadgeGroup(), /* SY --> */ ButtonsGroup(), /* SY <-- */ TabsGroup()))
        }

        inner class DisplayGroup : Group {

            private val compactGrid = Item.Radio(R.string.action_display_grid, this)
            private val comfortableGrid = Item.Radio(R.string.action_display_comfortable_grid, this)
            // SY -->
            private val noTitleGrid = Item.Radio(R.string.action_display_no_title_grid, this)
            // SY <--
            private val list = Item.Radio(R.string.action_display_list, this)

            override val header = Item.Header(R.string.action_display_mode)
            override val items = listOf(compactGrid, comfortableGrid, /* SY --> */ noTitleGrid /* SY <-- */, list)
            override val footer = null

            override fun initModels() {
                val mode = preferences.libraryDisplayMode().get()
                compactGrid.checked = mode == DisplayMode.COMPACT_GRID
                comfortableGrid.checked = mode == DisplayMode.COMFORTABLE_GRID
                // SY -->
                noTitleGrid.checked = mode == DisplayMode.NO_TITLE_GRID
                // SY <--
                list.checked = mode == DisplayMode.LIST
            }

            override fun onItemClicked(item: Item) {
                item as Item.Radio
                if (item.checked) return

                item.group.items.forEach { (it as Item.Radio).checked = false }
                item.checked = true

                preferences.libraryDisplayMode().set(
                    when (item) {
                        compactGrid -> DisplayMode.COMPACT_GRID
                        comfortableGrid -> DisplayMode.COMFORTABLE_GRID
                        // SY -->
                        noTitleGrid -> DisplayMode.NO_TITLE_GRID
                        // SY <--
                        list -> DisplayMode.LIST
                        else -> throw NotImplementedError("Unknown display mode")
                    }
                )

                item.group.items.forEach { adapter.notifyItemChanged(it) }
            }
        }

        inner class BadgeGroup : Group {
            private val downloadBadge = Item.CheckboxGroup(R.string.action_display_download_badge, this)
            private val unreadBadge = Item.CheckboxGroup(R.string.action_display_unread_badge, this)

            override val header = Item.Header(R.string.badges_header)
            override val items = listOf(downloadBadge, unreadBadge)
            override val footer = null

            override fun initModels() {
                downloadBadge.checked = preferences.downloadBadge().get()
                unreadBadge.checked = preferences.unreadBadge().get()
            }

            override fun onItemClicked(item: Item) {
                item as Item.CheckboxGroup
                item.checked = !item.checked
                when (item) {
                    downloadBadge -> preferences.downloadBadge().set((item.checked))
                    unreadBadge -> preferences.unreadBadge().set((item.checked))
                }
                adapter.notifyItemChanged(item)
            }
        }

        // SY -->
        inner class ButtonsGroup : Group {
            private val startReadingButton = Item.CheckboxGroup(R.string.action_start_reading_button, this)

            override val header = Item.Header(R.string.buttons_header)
            override val items = listOf(startReadingButton)
            override val footer = null

            override fun initModels() {
                startReadingButton.checked = preferences.startReadingButton().get()
            }

            override fun onItemClicked(item: Item) {
                item as Item.CheckboxGroup
                item.checked = !item.checked
                when (item) {
                    startReadingButton -> preferences.startReadingButton().set((item.checked))
                }
                adapter.notifyItemChanged(item)
            }
        }
        // SY <--

        inner class TabsGroup : Group {
            private val showTabs = Item.CheckboxGroup(R.string.action_display_show_tabs, this)

            override val header = Item.Header(R.string.tabs_header)
            override val items = listOf(showTabs)
            override val footer = null

            override fun initModels() {
                showTabs.checked = preferences.categoryTabs().get()
            }

            override fun onItemClicked(item: Item) {
                item as Item.CheckboxGroup
                item.checked = !item.checked
                when (item) {
                    showTabs -> preferences.categoryTabs().set((item.checked))
                }
                adapter.notifyItemChanged(item)
            }
        }
    }

    // SY -->
    inner class Grouping @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
        Settings(context, attrs) {

        init {
            setGroups(listOf(InternalGroup()))
        }

        inner class InternalGroup : Group {
            private val groupItems = mutableListOf<Item.DrawableSelection>()
            private val db: DatabaseHelper = Injekt.get()
            private val trackManager: TrackManager = Injekt.get()
            private val hasCategories = db.getCategories().executeAsBlocking().size != 0

            init {
                val groupingItems = mutableListOf(
                    LibraryGroup.BY_DEFAULT,
                    LibraryGroup.BY_SOURCE,
                    LibraryGroup.BY_STATUS
                )
                if (trackManager.hasLoggedServices()) {
                    groupingItems.add(LibraryGroup.BY_TRACK_STATUS)
                }
                if (hasCategories) {
                    groupingItems.add(LibraryGroup.UNGROUPED)
                }
                groupItems += groupingItems.map { id ->
                    Item.DrawableSelection(
                        id,
                        this,
                        LibraryGroup.groupTypeStringRes(id, hasCategories),
                        LibraryGroup.groupTypeDrawableRes(id)
                    )
                }
            }

            override val header = null
            override val items = groupItems
            override val footer = null

            override fun initModels() {
                val groupType = preferences.groupLibraryBy().get()

                items.forEach {
                    it.state = if (it.id == groupType) {
                        Item.DrawableSelection.SELECTED
                    } else {
                        Item.DrawableSelection.NOT_SELECTED
                    }
                }
            }

            override fun onItemClicked(item: Item) {
                item as Item.DrawableSelection
                if (item.id != LibraryGroup.BY_DEFAULT && preferences.librarySortingMode().get() == LibrarySort.DRAG_AND_DROP) {
                    preferences.librarySortingMode().set(LibrarySort.ALPHA)
                    preferences.librarySortingAscending().set(true)
                    refreshSort()
                }

                item.group.items.forEach {
                    (it as Item.DrawableSelection).state =
                        Item.DrawableSelection.NOT_SELECTED
                }
                item.state = Item.DrawableSelection.SELECTED

                preferences.groupLibraryBy().set(item.id)

                item.group.items.forEach { adapter.notifyItemChanged(it) }
            }
        }
    }
    // SY <--

    open inner class Settings(context: Context, attrs: AttributeSet?) :
        ExtendedNavigationView(context, attrs) {

        val preferences: PreferencesHelper by injectLazy()
        lateinit var adapter: Adapter

        /**
         * Click listener to notify the parent fragment when an item from a group is clicked.
         */
        var onGroupClicked: (Group) -> Unit = {}

        fun setGroups(groups: List<Group>) {
            adapter = Adapter(groups.map { it.createItems() }.flatten())
            recycler.adapter = adapter

            groups.forEach { it.initModels() }
            addView(recycler)
        }

        /**
         * Adapter of the recycler view.
         */
        inner class Adapter(items: List<Item>) : ExtendedNavigationView.Adapter(items) {

            override fun onItemClicked(item: Item) {
                if (item is GroupedItem) {
                    item.group.onItemClicked(item)
                    onGroupClicked(item.group)
                }
            }
        }
    }
}
