package de.kif.frontend.views.calendar

import de.kif.frontend.launch
import de.kif.frontend.repository.ScheduleRepository
import de.westermann.kwebview.ViewCollection
import de.westermann.kwebview.async
import de.westermann.kwebview.interval
import de.westermann.kwebview.iterator
import org.w3c.dom.HTMLElement
import org.w3c.dom.INSTANT
import org.w3c.dom.SMOOTH
import org.w3c.dom.ScrollBehavior
import kotlin.browser.document
import kotlin.js.Date
import kotlin.math.max
import kotlin.math.min


class CalendarBody(val calendar: Calendar, view: HTMLElement) : ViewCollection<CalendarRow>(view) {

    val editable = calendar.editable
    val day = calendar.day

    var calendarEntries: List<CalendarEntry> = emptyList()

    val calendarCells: List<CalendarCell>
        get() = iterator().asSequence().flatten().toList()

    private suspend fun updateRows(startTime: Int? = null, length: Int = 0) {
        if (calendarEntries.isEmpty() && startTime == null && !editable) {
            for (row in iterator().asSequence().toList()) {
                remove(row)
            }
            return
        }

        var max: Int
        var min: Int

        if (startTime != null) {
            min = startTime
            max = startTime + length
        } else {
            min = calendarEntries.first().startTime
            max = calendarEntries.first().startTime + calendarEntries.first().length
        }

        for (entry in calendarEntries) {
            max = max(max, entry.startTime + entry.length)
            min = min(min, entry.startTime)
        }

        if (min > max) {
            val h1 = max
            max = min
            min = h1
        }

        if (editable) {
            min = min(min, 0)
            max = max(max, 24 * 60)
        }

        min = (min / 60 - 1) * 60
        max = (max / 60 + 2) * 60

        while (isNotEmpty() && min > first().time) {
            remove(first())
        }

        while (isNotEmpty() && max < last().time) {
            remove(last())
        }

        if (isEmpty()) {
            +CalendarRow.create(this, min)
        }

        while (min < first().time) {
            prepand(CalendarRow.create(this, first().time - 15))
        }

        while (max > last().time + 15) {
            append(CalendarRow.create(this, last().time + 15))
        }
    }

    fun update(scroll: ScrollBehavior) {
        val now = Date.now().toLong() - calendar.timeDifference
        val refDay = calendar.referenceDate / (1000 * 60 * 60 * 24)
        val nowDay = now / (1000 * 60 * 60 * 24)
        val d = nowDay - refDay
        val diff = (day - d).toInt()
        val date = Date(now)
        val currentTime = date.getHours() * 60 + date.getMinutes() + (diff * 60 * 24)

        val rowTime = (currentTime / 15) * 15

        var activeRow: CalendarRow? = null

        for (row in this) {
            if (row.time == rowTime) {
                row.classList.clear()
                for (str in row.classList) {
                    if ("now" in str) {
                        row.classList -= str
                    }
                }
                row.classList += "calendar-row"
                row.classList += "calendar-now"
                row.classList += "calendar-now-${currentTime - rowTime}"
                activeRow = row
            } else {
                for (str in row.classList) {
                    if ("now" in str) {
                        row.classList -= str
                    }
                }
            }
        }

        if (calendar.autoScroll && activeRow != null) {
            if (calendar.orientation == Calendar.Orientation.ROOM_TO_TIME) {
                calendar.scrollVerticalTo((activeRow.offsetTop - 150).toDouble(), scroll)
            } else {
                calendar.scrollHorizontalTo((activeRow.offsetLeft - 100).toDouble(), scroll)
            }
        }
    }

    init {
        calendarEntries = document.getElementsByClassName("calendar-entry")
            .iterator().asSequence().map { CalendarEntry(this, it) }.toList()

        wrapContent {
            CalendarRow(this, it)
        }

        ScheduleRepository.onCreate {
            launch {
                val schedule = ScheduleRepository.get(it) ?: throw  NoSuchElementException()

                updateRows(schedule.time, schedule.workGroup.length)

                calendarEntries += CalendarEntry.create(this, schedule)
            }
        }
        ScheduleRepository.onUpdate {
            launch {
                val schedule = ScheduleRepository.get(it) ?: throw  NoSuchElementException()

                updateRows(schedule.time, schedule.workGroup.length)

                var found = false
                for (entry in calendarEntries) {
                    if (entry.scheduleId == it) {
                        entry.load(schedule)
                        found = true
                    }
                }
                if (!found) {
                    calendarEntries += CalendarEntry.create(this, schedule)
                }

                updateRows()
            }
        }

        ScheduleRepository.onDelete {
            for (entry in calendarEntries) {
                if (entry.scheduleId == it) {
                    entry.html.remove()
                    calendarEntries -= entry

                    launch {
                        updateRows()
                    }
                }
            }
        }

        async {
            update(ScrollBehavior.INSTANT)
        }

        interval(1000) {
            update(ScrollBehavior.SMOOTH)
        }
    }
}
