// timetable.go
package controllers

import (
	"bytes"
	"fmt"
	"html/template"
	"log"
	"officeHours/models"
	"officeHours/templating"
)

// A function that takes a slice of office hours and
// constructs a timetable t such that t[date][slot] is an
// office hour that takes place at this time and no such cell has more than one
// office hour and each officehour is in exactly one slot.
// It also indicates how many slots exist per day.
func (b *BaseHandler) GetTimetable(officeHours []models.OfficeHour) (map[models.Date]map[int]models.OfficeHour, []int) {
	var timetable = make(map[models.Date]map[int]models.OfficeHour)
	for _, officeHour := range officeHours {
		var slot int = 0
		for minute := 0; minute < officeHour.Duration; minute += b.config.Date.MinuteGranularity { // find slot id
			_, exists := timetable[models.GetEndDate(officeHour.Date, minute, true)]
			if exists {
				_, exists := timetable[models.GetEndDate(officeHour.Date, minute, true)][slot]
				if exists {
					slot += 1
					minute = 0
					continue
				}
			} else {
				timetable[models.GetEndDate(officeHour.Date, minute, true)] = make(map[int]models.OfficeHour)
			}
		}
		for minute := 0; minute < officeHour.Duration; minute += b.config.Date.MinuteGranularity { // write officeHour id to timetable
			timetable[models.GetEndDate(officeHour.Date, minute, true)][slot] = officeHour
		}
	}
	slots := []int{1, 1, 1, 1, 1}
	for date := range timetable {
		if slots[date.Day] < len(timetable[date]) {
			slots[date.Day] = len(timetable[date])
		}
	}
	return timetable, slots
}

// A function to generate the HTML code for the table of office hours from a timetable.
func (b *BaseHandler) printTimetable(timetable map[models.Date]map[int]models.OfficeHour, slots []int, deleteIcons bool) template.HTML {
	if len(timetable) == 0 { // no office hours to display
		return template.HTML("<p class=\"text-center\">Aktuell sind keine passenden Sprechstunden eingetragen.</p>")
	}
	var tableBody string
	for hour := b.config.Date.EarliestStartTime.Hour; hour < b.config.Date.LatestStartTime.Hour+b.config.Date.MaxDuration/60; hour += 1 {
		for minute := 0; minute < 60; minute += b.config.Date.MinuteGranularity {
			tableBody += "<tr>"
			if minute == 0 {
				tableBody += fmt.Sprintf("<td class=\"timetableRightBorder\">%d Uhr</td>\n", hour)
			} else {
				tableBody += "<td class=\"timetableRightBorder\"></td>\n"
			}
			for day := 0; day < 5; day += 1 {
				for slot := 0; slot < slots[day]; slot += 1 {
					current, currentExists := timetable[models.Date{Week: 0, Day: day, Hour: hour, Minute: minute}][slot]

					if currentExists { // This slot is taken by some office hour
						var continued bool = false // is this slot occupied by the same office hour the previous minute?
						var predecessorExists bool
						var predecessor models.OfficeHour
						if hour > 0 && minute < b.config.Date.MinuteGranularity {
							predecessor, predecessorExists = timetable[models.Date{Week: 0, Day: day, Hour: hour - 1, Minute: 60 - b.config.Date.MinuteGranularity}][slot]
						} else {
							predecessor, predecessorExists = timetable[models.Date{Week: 0, Day: day, Hour: hour, Minute: minute - b.config.Date.MinuteGranularity}][slot]
						}
						if predecessorExists {
							continued = (predecessor == current)
						} else {
							continued = false
						}
						if continued {
							continue
						} else {
							var celldata bytes.Buffer
							data := struct {
								OfficeHour        models.OfficeHour
								MinuteGranularity int
								DeleteIcons       bool
							}{OfficeHour: current, MinuteGranularity: b.config.Date.MinuteGranularity, DeleteIcons: deleteIcons}
							err := templating.WriteTemplate(&celldata, "td", data)
							if err != nil {
								err = fmt.Errorf("writing table cell failed:\n%w", err)
								log.Println(err.Error())
								// TODO: better error wrapping up to top-level request handler
							}
							tableBody += celldata.String()
						}
					} else {
						if slot+1 == slots[day] {
							tableBody += "<td class=\"timetableRightBorder\"></td>\n"
						} else {
							tableBody += "<td></td>\n"
						}
					}
				}
			}
			tableBody += "</tr>\n"
		}
	}
	var table bytes.Buffer

	tableData := struct {
		ColspanMon int
		ColspanTue int
		ColspanWed int
		ColspanThu int
		ColspanFri int
		TableBody  template.HTML
	}{
		slots[0],
		slots[1],
		slots[2],
		slots[3],
		slots[4],
		template.HTML(tableBody),
	}
	err := templating.WriteTemplate(&table, "officeHourTable", tableData)
	if err != nil {
		err = fmt.Errorf("writing table failed:\n%w", err)
		log.Println(err.Error())
		// TODO: better error wrapping up to top-level request handler
	}
	return template.HTML(table.String())
}
