// 8. hét – A gyakorlati feladatok megoldásai
// Futtatás: go run ./h08/exercises
package main

import (
	"fmt"
	"strings"
)

// Egyszerű generikus sor szelettel a feladatokhoz
type Queue[T any] struct{ items []T }

func (q *Queue[T]) Enqueue(x T) { q.items = append(q.items, x) }
func (q *Queue[T]) Dequeue() T {
	x := q.items[0]
	q.items = q.items[1:]
	return x
}
func (q *Queue[T]) Peek() T     { return q.items[0] }
func (q *Queue[T]) Len() int    { return len(q.items) }
func (q *Queue[T]) Empty() bool { return len(q.items) == 0 }

// F1. Kódkövetés a régi dolgozatból
func fibonacciQueue(n int) {
	var q Queue[int]
	q.Enqueue(0)
	q.Enqueue(1)
	for i := 0; i < n; i++ {
		a := q.Dequeue()
		fmt.Print(a, " ")
		b := q.Peek()
		q.Enqueue(a + b)
	}
	fmt.Println()
}

// F2. Josephus-probléma
func josephus(n, k int) int {
	var q Queue[int]
	for i := 1; i <= n; i++ {
		q.Enqueue(i)
	}
	fmt.Print("kiesési sorrend: ")
	for q.Len() > 1 {
		for i := 1; i < k; i++ {
			q.Enqueue(q.Dequeue())
		}
		fmt.Print(q.Dequeue(), " ")
	}
	return q.Dequeue()
}

// F3. Round-robin ütemezés
func roundRobin(names []string, burst []int, quantum int) {
	var q Queue[int]
	left := append([]int(nil), burst...)
	for i := range names {
		q.Enqueue(i)
	}
	time := 0
	for !q.Empty() {
		p := q.Dequeue()
		run := min(quantum, left[p])
		time += run
		left[p] -= run
		if left[p] > 0 {
			q.Enqueue(p)
		} else {
			fmt.Printf("%s kész (t=%d)  ", names[p], time)
		}
	}
	fmt.Println()
}

// F4. Sor megfordítása veremmel
func reverse(q *Queue[int]) {
	var st []int
	for !q.Empty() {
		st = append(st, q.Dequeue())
	}
	for len(st) > 0 {
		q.Enqueue(st[len(st)-1])
		st = st[:len(st)-1]
	}
}

// F5. Kódkövetés: betű = enqueue, '*' = dequeue és kiírás
func trace(ops string) string {
	var q Queue[rune]
	var out strings.Builder
	for _, c := range ops {
		switch c {
		case ' ':
		case '*':
			out.WriteRune(q.Dequeue())
		default:
			q.Enqueue(c)
		}
	}
	return out.String()
}

// F6. Mozgó átlag
func movingAverage(data []int, k int) {
	var w Queue[int]
	sum := 0
	for _, x := range data {
		w.Enqueue(x)
		sum += x
		if w.Len() > k {
			sum -= w.Dequeue()
		}
		fmt.Printf("%.1f ", float64(sum)/float64(w.Len()))
	}
	fmt.Println()
}

func main() {
	fmt.Print("F1. ")
	fibonacciQueue(10)
	fmt.Print("F2. ")
	fmt.Println("-> túlélő:", josephus(7, 3))
	fmt.Print("F3. ")
	roundRobin([]string{"P1", "P2", "P3"}, []int{5, 2, 4}, 2)
	q := &Queue[int]{items: []int{1, 2, 3, 4}}
	reverse(q)
	fmt.Println("F4. reverse([1 2 3 4]) =", q.items)
	fmt.Println(`F5. trace("D A T * A * S * T R * U C * T") =`, trace("D A T * A * S * T R * U C * T"))
	fmt.Print("F6. mozgó átlag (k=3): ")
	movingAverage([]int{10, 20, 30, 40, 50, 60}, 3)
}
