package de.kif.frontend.views.calendar

import de.kif.frontend.launch
import de.kif.frontend.repository.RoomRepository
import de.kif.frontend.repository.ScheduleRepository
import de.westermann.kwebview.View
import de.westermann.kwebview.createHtmlView
import de.westermann.kwebview.iterator
import org.w3c.dom.*
import kotlin.browser.document
import kotlin.browser.window
import kotlin.js.Date
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min

class Calendar(calendar: HTMLElement) : View(calendar) {

    var autoScroll = true

    val calendarTable = calendar.getElementsByClassName("calendar-table")[0] as HTMLElement
    private val calendarTableHeader = calendar.getElementsByClassName("calendar-header")[0] as HTMLElement

    val htmlBody = document.body ?: createHtmlView()

    val day = calendarTable.dataset["day"]?.toIntOrNull() ?: -1
    val referenceDate = calendarTable.dataset["reference"]?.toLongOrNull() ?: -1L
    val nowDate = calendarTable.dataset["now"]?.toLongOrNull() ?: -1L
    val timeDifference = Date.now().toLong() - nowDate

    fun scrollVerticalBy(pixel: Double, scrollBehavior: ScrollBehavior = ScrollBehavior.SMOOTH) {
        scrollAllVerticalBy(pixel, scrollBehavior)
    }

    fun scrollHorizontalBy(pixel: Double, scrollBehavior: ScrollBehavior = ScrollBehavior.SMOOTH) {
        scrollAllHorizontalBy(pixel, scrollBehavior)
    }

    fun scrollVerticalTo(pixel: Double, scrollBehavior: ScrollBehavior = ScrollBehavior.SMOOTH) {
        scrollAllVerticalTo(pixel, scrollBehavior)
    }

    fun scrollHorizontalTo(pixel: Double, scrollBehavior: ScrollBehavior = ScrollBehavior.SMOOTH) {
        scrollAllHorizontalTo(pixel, scrollBehavior)
    }

    val editable = calendar.dataset["editable"]?.toBoolean() ?: false

    val body = CalendarBody(this, calendar.getElementsByClassName("calendar-body")[0] as HTMLElement)

    val orientation: Orientation = if (document.getElementsByClassName("time-to-room").length > 0) {
        Orientation.TIME_TO_ROOM
    } else {
        Orientation.ROOM_TO_TIME
    }

    private var autoCheck: Boolean = false

    fun autoCheck() {
        if (autoCheck) checkConstraints()
    }

    private fun checkConstraints() {
        launch {
            val errors = ScheduleRepository.checkConstraints()

            for ((s, l) in errors.map) {
                for (entry in body.calendarEntries) {
                    if (entry.scheduleId == s) {
                        entry.setError(l)
                    }
                }
            }
        }
    }

    init {
        scroll += calendarTable

        if (editable) {
            CalendarEdit(this, calendar.querySelector(".calendar-edit") as HTMLElement)
        }

        val checkConstraintsBtn =
            (document.getElementById("calendar-check-constraints") as? HTMLElement)?.let { wrap(it) }
        if (checkConstraintsBtn != null) {
            checkConstraintsBtn.onClick.addListener {
                checkConstraints()
            }
        }

        val autoCheckConstraintsBtn =
            (document.getElementById("calendar-auto-check-constraints") as? HTMLElement)?.let { wrap(it) }
        if (autoCheckConstraintsBtn != null) {
            autoCheckConstraintsBtn.onClick.addListener {
                if (autoCheck) {
                    for (entry in body.calendarEntries) {
                        entry.setError(emptyList())
                    }
                    autoCheck = false
                } else {
                    autoCheck = true
                    autoCheck()
                }

                autoCheckConstraintsBtn.classList["btn-primary"] = autoCheck
            }
        }

        onWheel {
            autoScroll = false

            val multiplier = when (it.deltaMode) {
                1 -> 16.0
                2 -> window.innerHeight.toDouble()
                else -> 1.0
            }

            var verticalScroll = it.deltaY * multiplier

            if (verticalScroll > 0) {
                val x = html.offsetTop - htmlBody.scrollTop
                if (x > 0) {
                    val bodyScroll = min(x, verticalScroll)
                    verticalScroll -= bodyScroll
                    htmlBody.scrollBy(ScrollToOptions(0.0, bodyScroll, ScrollBehavior.INSTANT))
                } else {
                    if (calendarTable.scrollTop + calendarTable.clientHeight + 5 >= calendarTable.scrollHeight) {
                        htmlBody.scrollBy(ScrollToOptions(0.0, verticalScroll, ScrollBehavior.INSTANT))
                    }
                }
            } else if (verticalScroll < 0) {
                val x = html.offsetTop - htmlBody.scrollTop
                if (x < 0) {
                    val bodyScroll = max(x, verticalScroll)
                    verticalScroll -= bodyScroll
                    htmlBody.scrollBy(ScrollToOptions(0.0, bodyScroll, ScrollBehavior.INSTANT))
                } else {
                    if (calendarTable.scrollTop == 0.0) {
                        htmlBody.scrollBy(ScrollToOptions(0.0, verticalScroll, ScrollBehavior.INSTANT))
                    }
                }
            }

            scrollHorizontalBy(it.deltaX * multiplier, ScrollBehavior.INSTANT)
            scrollVerticalBy(verticalScroll, ScrollBehavior.INSTANT)

            it.preventDefault()
        }

        RoomRepository.onCreate {
            val cell = createHtmlView<HTMLElement>()
            cell.dataset["room"] = it.toString()
            cell.classList.add("calendar-cell")
            calendarTableHeader.appendChild(cell)

            launch {
                val room = RoomRepository.get(it) ?: return@launch
                val span = createHtmlView<HTMLSpanElement>()
                span.textContent = room.name
                cell.appendChild(span)
            }
        }

        RoomRepository.onDelete {
            val str = it.toString()
            for (element in calendarTableHeader.children.iterator()) {
                if (element.dataset["room"] == str) {
                    element.remove()
                }
            }
        }
    }

    enum class Orientation {

        /**
         * Columns contains time
         * Rows contains rooms
         *
         * Like the old kif tool
         */
        TIME_TO_ROOM,

        /**
         * Columns contains rooms
         * Rows contains time
         *
         * Like the congress schedule
         */
        ROOM_TO_TIME
    }

    companion object {
        private var scroll = listOf<HTMLElement>()

        private fun scrollAllVerticalBy(pixel: Double, scrollBehavior: ScrollBehavior = ScrollBehavior.SMOOTH) {
            for (calendarTable in scroll) {
                calendarTable.scrollBy(ScrollToOptions(0.0, pixel, scrollBehavior))
            }
        }

        private fun scrollAllHorizontalBy(pixel: Double, scrollBehavior: ScrollBehavior = ScrollBehavior.SMOOTH) {
            for (calendarTable in scroll) {
                calendarTable.scrollBy(ScrollToOptions(pixel, 0.0, scrollBehavior))
            }
        }

        private fun scrollAllVerticalTo(pixel: Double, scrollBehavior: ScrollBehavior = ScrollBehavior.SMOOTH) {
            for (calendarTable in scroll) {
                calendarTable.scrollTo(ScrollToOptions(0.0, pixel, scrollBehavior))
            }
        }

        private fun scrollAllHorizontalTo(pixel: Double, scrollBehavior: ScrollBehavior = ScrollBehavior.SMOOTH) {
            for (calendarTable in scroll) {
                calendarTable.scrollTo(ScrollToOptions(pixel, 0.0, scrollBehavior))
            }
        }
    }
}

fun initCalendar() {
    document.getElementsByClassName("calendar").iterator().forEach { Calendar(it) }
}