package main

import (
	"fmt"
	"math/rand"
	"sync"
	"time"
)

var (
	promet      float64
	stNarocil   int
	mutex       sync.Mutex
	printMutex  sync.Mutex
	wgProducers sync.WaitGroup
	wgConsumers sync.WaitGroup
)

type narocilo interface {
	obdelaj()
}

type izdelek struct {
	imeIzdelka string
	cena       float64
	teza       float64
}

type eknjiga struct {
	naslovKnjige string
	cena         float64
}

type spletniTecaj struct {
	imeTecaja   string
	trajanjeUre int
	cenaUre     float64
}

func (i izdelek) obdelaj() {
	printMutex.Lock()
	stNarocil++
	fmt.Printf("Številka naročila: %d\n", stNarocil)
	fmt.Println("Ime izdelka:", i.imeIzdelka)
	fmt.Printf("Cena: %.2f €\n", i.cena)
	fmt.Printf("Teža: %.2f kg\n\n", i.teza)
	printMutex.Unlock()

	mutex.Lock()
	promet += i.cena
	mutex.Unlock()
}

func (e eknjiga) obdelaj() {
	printMutex.Lock()
	stNarocil++
	fmt.Printf("Številka naročila: %d\n", stNarocil)
	fmt.Println("Naslov e-knjige:", e.naslovKnjige)
	fmt.Printf("Cena: %.2f €\n\n", e.cena)
	printMutex.Unlock()

	mutex.Lock()
	promet += e.cena
	mutex.Unlock()
}

func (s spletniTecaj) obdelaj() {
	cena := float64(s.trajanjeUre) * s.cenaUre

	printMutex.Lock()
	stNarocil++
	fmt.Printf("Številka naročila: %d\n", stNarocil)
	fmt.Println("Ime tečaja:", s.imeTecaja)
	fmt.Printf("Trajanje: %d ur\n", s.trajanjeUre)
	fmt.Printf("Cena na uro: %.2f €\n", s.cenaUre)
	fmt.Printf("Skupna cena: %.2f €\n\n", cena)
	printMutex.Unlock()

	mutex.Lock()
	promet += cena
	mutex.Unlock()
}

func generirajNarocilo() narocilo {
	t := rand.Intn(3)
	switch t {
	case 0:
		return izdelek{
			imeIzdelka: fmt.Sprintf("Izdelek-%d", rand.Intn(100)),
			cena:       float64(10 + rand.Intn(200)),
			teza:       rand.Float64()*5 + 0.5,
		}
	case 1:
		return eknjiga{
			naslovKnjige: fmt.Sprintf("Knjiga-%d", rand.Intn(50)),
			cena:         float64(5 + rand.Intn(50)),
		}
	default:
		return spletniTecaj{
			imeTecaja:   fmt.Sprintf("Tečaj-%d", rand.Intn(30)),
			trajanjeUre: rand.Intn(10) + 1,
			cenaUre:     float64(10 + rand.Intn(30)),
		}
	}
}

func producer(orders chan<- narocilo, stopChan <-chan struct{}) {
	defer wgProducers.Done()

	for {
		select {
		case <-stopChan:
			return
		default:
			orders <- generirajNarocilo()
			time.Sleep(500 * time.Millisecond)
		}
	}
}

func consumer(orders <-chan narocilo) {
	defer wgConsumers.Done()
	for order := range orders {
		order.obdelaj()
		time.Sleep(500 * time.Millisecond)
	}
}

func waitForEnter(stopChan chan<- struct{}) {
	fmt.Scanln()
	fmt.Println("Zaustavljam spletno trgovino...")
	close(stopChan)
}

func main() {

	orders := make(chan narocilo, 10)
	stopChan := make(chan struct{})

	go waitForEnter(stopChan)

	for i := 1; i <= 5; i++ {
		wgProducers.Add(1)
		go producer(orders, stopChan)
	}

	for i := 1; i <= 2; i++ {
		wgConsumers.Add(1)
		go consumer(orders)
	}

	wgProducers.Wait()
	close(orders)
	wgConsumers.Wait()

	fmt.Println("Spletna trgovina ustavljena.")
	fmt.Printf("Skupen promet: %.2f €\n", promet)
	fmt.Println("Skupno število obdelanih naročil:", stNarocil)
}
