package main

//SECTION Package Import/Info
import ( // factored import statement, each line one package
	"fmt" // Implements basic I/O
	"reflect"

	"math/rand" // Implements pseudo-random number generation
	//UNSUITABLE for security-sensitive work bcs values are predictable

	"math" // Basic mathematical functions
	// Implements run-time reflection)
)

//!SECTION

func add(x int, y int) int              { return x + y }
func add3(x, y int, z float64) float64  { return float64(x) + float64(y) + z }
func sep(name string)                   { fmt.Printf("\n\n|-----%s-----|\n", name) }
func msep()                             { fmt.Printf("\n---\n") }
func swap(x, y string) (string, string) { return y, x }
func naked() (x, z, y int) { // operators couldn't be used here
	x = 10
	y = 15
	z = 20
	return // naked return calls named values
	// return z, y, x + z // Overrides to named return values BUT still needs 3 elements!!!
}

var python bool

// myvar := 10 // ERROR

func main() {

	//SECTION I/O
	fmt.Println("My favorite number is", rand.Intn(10))
	fmt.Printf("Now you have %g problems.\n", math.Sqrt(7)) // C style I/O, printf
	// fmt.Println("Now you have %g problems.\n", math.Sqrt(7)) // %g won't work
	fmt.Println("Println inserts blanks between operands", "and appends a newline")
	fmt.Printf("Type: %T, Value: %v", python, python)
	//!SECTION

	sep("Exported Names")

	//SECTION Exported Names
	fmt.Printf("if a name begins with a capital letter, it is exported and you can use it with importing package\n")
	// fmt.Println(math.pi) WRONG
	fmt.Println(math.Pi) // imports pi number
	//!SECTION

	sep("Values, Variables and Constants")

	//SECTION Values, Variables and Constants
	fmt.Printf("Data types in Go language:\n")
	fmt.Printf(`
	bool

	string
	
	int  int8  int16  int32  int64
	uint uint8 uint16 uint32 uint64 uintptr
	
	byte // alias for uint8
	
	rune // alias for int32
	// represents a Unicode code point
	
	float32 float64
	
	complex64 complex128`)
	//TODO add explanation for each data types

	msep()

	fmt.Printf("Data types of a variable is static. So you can't modify its type later in code (Like C, unlike python)\n")
	var age int // var VARIABLE_NAME DATA_TYPE
	age = 32    // = is assigment symbol
	// age = "hello" // ERROR
	fmt.Println(age)

	fmt.Println("We can also declare multiple variable with same data type at once like: `var VAR1, VAR2 DATA_TYPE`")
	var var1, var2, var3 int

	fmt.Println("var statement can be at package or function level")
	fmt.Println(var1, var2, var3, python)

	msep()

	fmt.Println("Variables with initializers")
	var var4, var5 int = 1, 2
	// var var4, var5 = 1, 2 // ALSO VALID even without datatype
	// var var4, var5 = 1 // ERROR
	// var var4, var5 = 1, 2, 3 // ERROR
	fmt.Println(var4, var5)

	fmt.Println("Different types also can be used together")
	var var6, var7, var8 = true, 24.123, "yep"
	fmt.Println(var6, var7, var8)

	fmt.Println("Multiple variables can be defined also like this:")
	var (
		var9  int = 14
		var10     = 1.34
	)
	fmt.Println(var9, var10)

	fmt.Println("Zero values are 0 for numeric types, false for boolean, and empty string(\"\") for the strings")
	msep()

	fmt.Printf("You must assign first values to constants while declaring\n")
	const msg1 = "Hello"
	fmt.Printf("%s - %s", msg1, reflect.TypeOf(msg1)) // Automatically declaring the type as string
	// msg1 = "Goodbye" //ERROR
	fmt.Println("Numeric constants are high-precision values.")

	msep()

	fmt.Print("You can also use `:=` for both declaring and assigning a variable\nvariable's type is inferred from the value on the right hand side\n")
	// var foo int = 10 // is the same thing with;
	foo, foo2 := 10, "hi"
	fmt.Println(foo, foo2)
	fmt.Println("Not avaliable outside a function (package level)")

	msep()

	fmt.Println("Type conversions: Type(value) can be applied both with:")

	var i int = 42
	var f float64 = float64(i)
	var u uint = uint(f)
	fmt.Println(reflect.TypeOf(i), reflect.TypeOf(f), reflect.TypeOf(u))

	// OR

	i2 := 42
	f2 := float64(i2)
	u2 := uint(f2)
	fmt.Println(reflect.TypeOf(i2), reflect.TypeOf(f2), reflect.TypeOf(u2))

	fmt.Println("!!!In Go assigments explicit conversions required in different type conversions")
	//!SECTION

	sep("Functions")

	//SECTION Functions
	fmt.Printf("In Go, type comes after the variable name\nLike `x int`\n")
	fmt.Println(add(10, 51)) // Tip: Ctrl+Click in VS Code for see function source quicly
	// fmt.Println(add(10)) ERROR
	// fmt.Println(add(10, 11, 12)) ERROR

	msep()

	fmt.Printf("Also return types comes after function name and variables\nJust leave a blank for use as void in C\n")
	// More info about Go's decleration syntax LINK https://go.dev/blog/declaration-syntax

	msep()

	fmt.Printf("You can also shorten arguments line with just using last arguments type if they are same type\nLike `x, y int`\n")
	fmt.Println(add3(10, 11, 12.0141))
	fmt.Println(add3(10, 11, 12.01412352345)) // not precise
	fmt.Println(add3(10, 11, 12))             // SUCCESS
	//fmt.Println(add3(10.34, 11.53, 12.23))    // ERROR

	msep()

	fmt.Printf("A function can return multiple results\n")
	a, b := swap("hello", "world")
	fmt.Printf("%s %s\n", a, b)

	msep()

	fmt.Printf("Return values in functions may be named. a `return` statement returns the named values if arguments didn't supplied. This is known as a naked return\n")
	fmt.Println("Naked return statements should be used only in short functions!!! Otherwise they can harm readability!!!")
	fmt.Println("Note: You can't use `:=` anymore bcs you declared your variables in top of your function")
	fmt.Println(naked())
	//!SECTION

	// sep()
}