package main

import (
	"fmt"
	"runtime"
	"time"
)

/*
1. Буферизированный канал.
c := make(chan int, 5)

Операция чтения такого канала является жадной, т.к. горутина чтения не будет
блокироваться до тех пор пока не будет вычетан весь буфер.
При записи в канал действует та же логика.

*/

func squares(c chan int) {
	for i := 0; i <= 9; i++ {
		c <- i * i
	}
	close(c)
}

func main() {
	// foo1()
	// capChannel()
	// runtimeChannel()
	readAfterClose()
	/*
		fmt.Println("main() started")
		c := make(chan int)

		go squares(c)

		// range будет считывать данные из канала пока он не будет закрыт
		// Если в squares не будет close, то будет deadlock
		for val := range c {
			fmt.Println(val)
		}

		fmt.Println("main() stopped")
	*/

	/*
		for {
			val, ok := <-c
			if ok == false {
				fmt.Println(val, ok, "<-- loop broke!")
				break // exit break loop
			} else {
				fmt.Println(val, ok)
			}
		}
	*/
}

func squaresRead(c chan int) {
	for i := 0; i <= 3; i++ {
		num := <-c
		fmt.Println(num * num)
	}
}

// Буферизированный канал не будет блоикироваться при записи
func foo1() {
	fmt.Println("main() started")
	c := make(chan int, 3)

	go squaresRead(c)

	c <- 1
	c <- 2
	c <- 3
	c <- 4
	// c <- 5
	// c <- 6
	// c <- 7
	// c <- 8
	// c <- 9
	// c <- 10

	// time.Sleep(1 * time.Second)
	fmt.Println("main() stopped")
}

// -----------------------------------------------------------------

func sender(c chan int) {
	c <- 1
	c <- 2
	c <- 3
	c <- 4 // goroutine blocks here
	close(c)
}

func capChannel() {
	c := make(chan int, 3)
	go sender(c)
	fmt.Printf("Length and capacity of channel c is %v, %v\n", len(c), cap(c))

	// read values from c (blocked here)
	for val := range c {
		fmt.Printf("Length of channel c after value '%v' len is %v\n", val, len(c))
	}

}

// -----------------------------------------------------------------

func squaresFour(c chan int) {
	for i := 0; i < 4; i++ {
		num := <-c
		fmt.Println(num * num)
	}
}

func runtimeChannel() {
	fmt.Println("main() started")
	c := make(chan int, 3)
	go squaresFour(c)

	fmt.Println("active goroutines", runtime.NumGoroutine())

	c <- 1
	c <- 2
	c <- 3
	c <- 4 // bloks here

	time.Sleep(1 * time.Second)

	fmt.Println("active goroutines", runtime.NumGoroutine())

	go squaresFour(c)

	fmt.Println("active goroutines", runtime.NumGoroutine())

	c <- 5
	c <- 6
	c <- 7
	c <- 8 // bloks here

	fmt.Println("active goroutines", runtime.NumGoroutine())
	fmt.Println("main() stopped")
}

// -----------------------------------------------------------------

func readAfterClose() {
	c := make(chan int, 3)
	c <- 1
	c <- 2
	c <- 3
	close(c)
	/*
		for elem := range c {
			fmt.Println(elem)
		}
	*/

	// Так тоже не будет deadlock
	for i := 0; i < 5; i++ {
		fmt.Println(i, <-c)
	}
}
