package dev.fyloz.plannervio.ui.view

import dev.fyloz.plannervio.core.model.Board
import dev.fyloz.plannervio.core.service.IBoardService
import dev.fyloz.plannervio.ui.style.Style
import javafx.application.Platform
import javafx.beans.binding.Bindings
import javafx.geometry.Pos
import javafx.scene.control.Label
import javafx.scene.layout.HBox
import javafx.scene.layout.Priority
import javafx.scene.paint.Color
import kfoenix.jfxtextfield
import org.kodein.di.instance
import org.kodein.di.tornadofx.kodeinDI
import tornadofx.*
import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle

/** The app's main view */
class MainView : View() {
    private val presenter: MainViewPresenter by inject()
    private var activeTabView: View = presenter.activeView
        set(value) {
            activeTabView.replaceWith(value)
            field = value
        }

    init {
        messages = messages("lang.views.main")
    }

    override val root = borderpane {
        left = vbox {
            addClass(Style.navBar)
            alignment = Pos.TOP_LEFT

            this += label {
                addClass(Style.appNameLabel)
                alignment = Pos.CENTER
                text = "Plannervio"
            }
            this += jfxtextfield {
                vgrow = Priority.NEVER
                isLabelFloat = true
                promptText = messages[MainViewKeywords.NAV_SEARCH_LABEL]
                vboxMargin(horizontal = 16, vertical = 20)
            }
            for (t in Tab.values()) {
                this += tabButton {
                    tab = t
                    alignment = Pos.BOTTOM_LEFT
                    textFill = Color.RED
                    graphic = hbox {
                        paddingTop = 2
                        this += pane {
                            hboxMargin(top = 8)
                            addClass(Style.jfxSvgGlyphWrapper)
                            this += svgGlyph(t.icon, size = 16.0, color = Color.WHITE)
                        }
                        this += Label(messages[t.messageKey])
                    }
                    setOnMouseClicked { presenter.onTabButtonClick(this) }
                }
            }
        }
        center = vbox {
            isFillWidth = true
            padding = insets(10.0)
            this += activeTabView
        }
    }

    fun resetCurrentTab() {
        val view = presenter.activeView
        activeTabView = view
    }
}

class BoardsView : View() {
    private val presenter by inject<BoardsTabViewPresenter>()
    private val headerRowHeightRatio = 1.15
    private val rowHeight = 52.0

    init {
        messages = messages("lang.views.main")
    }

    private val defaultBoardLocation = messages[Keywords.BOARD_LOCATION_LOCAL]

    override val root = tableview(presenter.boards.toObservable()) {
        fixedCellSize = rowHeight
        prefHeightProperty().bind(fixedCellSizeProperty() * (Bindings.size(items) + headerRowHeightRatio))

        columns.setAll(
                column(messages[Keywords.BOARD_NAME], String::class) {
                    minWidth = 150.0
                    value { it.value.name }
                },
                column(messages[Keywords.BOARD_LOCATION], String::class) {
                    value { it.value.location ?: defaultBoardLocation }
                    minWidth = 150.0
                    setComparator { a, b ->
                        when {
                            a != defaultBoardLocation && b == defaultBoardLocation -> 1
                            a == defaultBoardLocation && b != defaultBoardLocation -> -1
                            a == defaultBoardLocation && b == defaultBoardLocation -> 0
                            else -> a.compareTo(b) * -1
                        }
                    }
                },
                column(messages[Keywords.BOARD_PROTECTION], HBox::class) {
                    fixedWidth(120.0)
                    value {
                        if (it.value.isRemote) {
                            val private = it.value.isPrivate
                            LabeledIcon(
                                    if (private) SvgIconName.LOCK else SvgIconName.EARTH,
                                    if (private) messages[Keywords.BOARD_PROTECTION_PRIVATE] else messages[Keywords.BOARD_PROTECTION_PUBLIC]
                            )
                        } else null
                    }
                },
                column(messages[Keywords.BOARD_FAVORITE], ToggleableIconButton::class) {
                    fixedWidth(100.0)
                    value {
                        toggleableIconButton(SvgIconName.STAR, Color.GOLD, Color.GREY) {
                            enabled = it.value.favorite
                        }
                    }
                },
                column(messages[Keywords.BOARD_LAST_VISITED], String::class) {
                    value { it.value.lastVisited?.format(DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT)) }
                    fixedWidth(150.0)
                }
        )

        // Prevent selection
        selectionModel.selectedIndexProperty().addListener { _, _, _ -> Platform.runLater { selectionModel.clearSelection() } }
    }
}

class FavoritesView : View() {
    override val root = pane {
        this += Label("FAVORITES")
    }
}

class RecentsView : View() {
    override val root = pane {
        this += Label("RECENTS")
    }
}

class SettingsView : View() {
    override val root = pane {
        this += Label("SETTINGS")
    }
}

class MainViewPresenter : Controller() {
    private val view: MainView by inject()

    private val boardSelectionView = find<BoardsView>()
    private val favoritesView = find<FavoritesView>()
    private val recentView = find<RecentsView>()
    private val settingsView = find<SettingsView>()

    private val defaultActiveView = boardSelectionView

    /** The currently active [TabButton]. **/
    var activeTabButton: TabButton? = null
        private set

    /** The currently active [Tab]. */
    private val activeTab: Tab?
        get() = activeTabButton?.tab

    /** The currently active view. */
    val activeView: View
        get() {
            val tab = activeTab
            return if (tab == null) defaultActiveView else when (tab) {
                Tab.BOARDS -> boardSelectionView
                Tab.SETTINGS -> settingsView
                Tab.FAVORITES -> favoritesView
                Tab.HISTORY -> recentView
            }
        }

    /** Called when a [TabButton] has been clicked. */
    fun onTabButtonClick(button: TabButton) {
        switchTab(button)
    }

    /** Called when the search bar detects changes. Its content is contained in [searchQuery]. */
    fun onSearchChange(searchQuery: String) {
        println(searchQuery)
    }

    /** Switch the active tab to [button]'s tab. */
    private fun switchTab(button: TabButton) {
        activeTabButton?.active = false
        button.active = true
        activeTabButton = button
        view.resetCurrentTab()
    }
}

abstract class TabViewPresenter : Controller() {
    protected abstract val view: View
    protected val boardService: IBoardService by kodeinDI().instance()
}

class BoardsTabViewPresenter : TabViewPresenter() {
    override val view: BoardsView by inject()

    val boards: List<Board>
        get() = boardService.getBoards()
}

object MainViewKeywords {
    const val NAV_SEARCH_LABEL = "nav.search.label"
    const val NAV_TAB_BOARDS_LABEL = "nav.tab.boards.label"
    const val NAV_TAB_FAVORITES_LABEL = "nav.tab.favorites.label"
    const val NAV_TAB_HISTORY_LABEL = "nav.tab.history.label"
    const val NAV_TAB_SETTINGS_LABEL = "nav.tab.settings.label"
    const val BOARDS_VIEW_OPEN_LABEL = "view.boards.open.label"
}

enum class Tab(val icon: SvgIconName, val messageKey: String) {
    BOARDS(SvgIconName.FORMAT_LIST_BULLETED, MainViewKeywords.NAV_TAB_BOARDS_LABEL),
    FAVORITES(SvgIconName.STAR, MainViewKeywords.NAV_TAB_FAVORITES_LABEL),
    HISTORY(SvgIconName.HISTORY, MainViewKeywords.NAV_TAB_HISTORY_LABEL),
    SETTINGS(SvgIconName.COG, MainViewKeywords.NAV_TAB_SETTINGS_LABEL)
}
