package main import ( "fmt" "sort" "strings" ) func buildTasksForDay(plans []Plan, stepMap map[int64][]CropStep, month, day int) []Task { var tasks []Task for _, p := range plans { tasks = append(tasks, expandPlanDayTasks(p, stepMap[p.CropID], month, day)...) } sortTasks(tasks) return tasks } func buildCalendar(plans []Plan, stepMap map[int64][]CropStep, startMonth, startDay, daysPerMonth, spanMonths int) []CalendarMonth { tasksByKey := make(map[string][]Task) for _, p := range plans { for m := 1; m <= 12; m++ { for d := 1; d <= daysPerMonth; d++ { dayTasks := expandPlanDayTasks(p, stepMap[p.CropID], m, d) if len(dayTasks) > 0 { key := fmt.Sprintf("%d-%d", m, d) tasksByKey[key] = append(tasksByKey[key], dayTasks...) } } } } for k := range tasksByKey { sortTasks(tasksByKey[k]) } var out []CalendarMonth for offset := 0; offset < spanMonths; offset++ { month := wrapMonth(startMonth + offset) yearOffset := (startMonth - 1 + offset) / 12 label := monthNames[month-1] if yearOffset > 0 { label = fmt.Sprintf("%s (+%d Jahr)", label, yearOffset) } fromDay := 1 if offset == 0 { fromDay = startDay } var days []CalendarDay for d := fromDay; d <= daysPerMonth; d++ { key := fmt.Sprintf("%d-%d", month, d) items := append([]Task(nil), tasksByKey[key]...) days = append(days, CalendarDay{Day: d, Tasks: items}) } out = append(out, CalendarMonth{ Offset: offset, Month: month, Label: label, YearOffset: yearOffset, Days: days, }) } return out } func expandPlanDayTasks(p Plan, steps []CropStep, month, day int) []Task { field := p.TargetName if strings.TrimSpace(field) == "" { field = "Unbekanntes Feld" } var out []Task baseHarvest := p.HarvestMonth if p.StartMonth == month && p.StartDay == day { out = append(out, Task{ Type: "Aussaat", Field: field, Message: withOptionalNote(fmt.Sprintf("Aussaat %s", p.CropName), p.Notes), SortOrder: 10, }) } harvestMonths := []int{baseHarvest} if p.RegrowEnabled { maxExtra := p.RegrowCycles if maxExtra == 0 { maxExtra = 24 } for i := 1; i <= maxExtra; i++ { harvestMonths = append(harvestMonths, wrapMonth(baseHarvest+(i*p.HarvestDistance()))) } } harvestMonths = uniqueMonths(harvestMonths) for _, hm := range harvestMonths { if hm == month && p.HarvestDay == day { out = append(out, Task{ Type: "Ernte", Field: field, Message: withOptionalNote(fmt.Sprintf("Ernte %s", p.CropName), p.Notes), SortOrder: 20, }) } } for _, s := range steps { switch s.Phase { case "pre": taskMonth := wrapMonth(p.StartMonth - s.MonthOffset) if taskMonth == month && p.StartDay == day { out = append(out, Task{ Type: "Vorbereitung", Field: field, Message: s.Title, SortOrder: 5, }) } case "post": for _, hm := range harvestMonths { taskMonth := wrapMonth(hm + s.MonthOffset) if taskMonth == month && p.HarvestDay == day { out = append(out, Task{ Type: "Nachbereitung", Field: field, Message: s.Title, SortOrder: 30, }) } } } } return out } func (p Plan) HarvestDistance() int { if p.GrowMonths <= 0 { return 1 } return p.GrowMonths } func withOptionalNote(base, note string) string { n := strings.TrimSpace(note) if n == "" { return base } return fmt.Sprintf("%s (Notiz: %s)", base, n) } func sortTasks(tasks []Task) { sort.Slice(tasks, func(i, j int) bool { if tasks[i].SortOrder == tasks[j].SortOrder { if tasks[i].Field == tasks[j].Field { return tasks[i].Message < tasks[j].Message } return tasks[i].Field < tasks[j].Field } return tasks[i].SortOrder < tasks[j].SortOrder }) } func uniqueMonths(values []int) []int { seen := make(map[int]bool) var out []int for _, v := range values { if !seen[v] { seen[v] = true out = append(out, v) } } return out }