// Single Line Comment.
/*
    Multi Line Comment
*/

struct User { // Cannot be defined in main, explained later.
	age  int
	name string
	pos int = -1 // custom default value
}
// struct method
fn (u User) can_register() bool {
	return u.age > 16
}

struct Parser {
	token Token
}

// c like enums
enum Token {
	plus
	minus
	div
	mult
}

// 1. functions
// language does not use semi colons
fn add(x int, y int) int {
	return x + y 
}
// can return multiple values
fn foo() (int, int) {
	return 2, 3
}

// function visibility 
pub fn public_function() { // pub can only be used from a named module.
}

fn private_function() {
}



// Main function
fn main() {
	// Anonymous functions can be declared inside other functions:
	double_fn := fn (n int) int {
		return n + n
	}
	// 2. Variables: they are immutable by default
	// implicitly typed
	x := 1
	// x = 2 // error
	mut y := 2
	y = 4
	name := "John"
	large_number := i64(9999999999999)
    println("$x, $y, $name, $large_number") // 1, 4, John, 9999999999999

	// unpacking values from functions.
	a, b := foo()
	println("$a, $b") // 2, 3
	c, _ := foo() // ignore values using `_`
	println("$c") // 2

	// Numbers
	u := u16(12)
	v := 13 + u    // v is of type `u16`
	r := f32(45.6)
	q := r + 3.14  // x is of type `f32`
	s := 75        // a is of type `int` 
	l := 14.7      // b is of type `f64` 
	e := u + s     // c is of type `int`
	d := l + r     // d is of type `f64`

	// Strings
	mut bob := 'Bob'
	assert bob[0] == u8(66) // indexing gives a byte, u8(66) == `B`
	assert bob[1..3] == 'ob'  // slicing gives a string 'ob'
	bobby := bob + 'by' // + is used to concatenate strings
	println(bobby) // "Bobby"
	bob += "by2" // += is used to append to strings
	println(bob) // "Bobby2"

	//String values are immutable. You cannot mutate elements:
	//mut s := 'hello 🌎'
	//s[0] = `H` // not allowed

	//For raw strings, prepend r. Escape handling is not done for raw strings:
	rstring := r'hello\nworld' // the `\n` will be preserved as two characters
	println(rstring) // "hello\nworld"

	// string interpolation
	println('Hello, $bob!') // Hello, Bob!
	println('Bob length + 10: ${bob.len + 10}!') // Bob length + 10: 13!

	// 3. Arrays
	mut numbers := [1, 2, 3]
	println(numbers) // `[1, 2, 3]`
	numbers << 4 // append elements with <<
	println(numbers[3]) // `4`
	numbers[1] = 5
	println(numbers) // `[1, 5, 3]`
	// numbers << "John" // error: `numbers` is an array of numbers
	numbers = [] // array is now empty
	arr := []int{len: 5, init: -1}
	// `arr == [-1, -1, -1, -1, -1]`, arr.cap == 5

	number_slices := [0, 10, 20, 30, 40]
	println(number_slices[1..4]) // [10, 20, 30]
	println(number_slices[..4]) // [0, 10, 20, 30]
	println(number_slices[1..]) // [10, 20, 30, 40]

	// 4. structs and enums
	// struct User {
	// 	age  int
	// 	name string
	//  pos int = -1 // custom default value
	// }
	mut users := User{21, 'Bob', 0}
	println(users.age) // 21
	
	// enum Token {
	// 	plus
	// 	minus
	// 	div
	// 	mult
	// }

	// struct Parser {
	// 	token Token
	// }
	parser := Parser{}
	if parser.token == .plus || parser.token == .minus 
	|| parser.token == .div || parser.token == .mult {
		// ...
	}


	// 5. Maps
	number_map := {
		'one': 1
		'two': 2
	}
	println(number_map) // {'one': 1, 'two': 2}
	println(number_map["one"]) // 1
	mut m := map[string]int{} // a map with `string` keys and `int` values
	m['one'] = 1
	m['two'] = 2
	println(m['one']) // "1"
	println(m['bad_key']) // "0"
	m.delete('two')

	// 6. Conditionals
	a_number := 10
	b_number := 20
	if a_number < b {
		println('$a_number < $b_number')
	} else if a_number > b {
		println('$a_number > $b_number')
	} else {
		println('$a_number == $b_number')
	}
	num := 777
	even_odd := if num % 2 == 0 { 'even' } else { 'odd' }
	println(even_odd)

	match even_odd {
		'even' { println('even') }
		'odd' { println('odd') }
		else { println('unknown') }
	} 

	// 7. Loops
	loops := [1, 2, 3, 4, 5]
	for lp in loops {
		println(lp)
	}
	loop_names := ['Sam', 'Peter']
	for i, lname in loop_names {
		println('$i) $lname')
		// Output: 0) Sam
		//         1) Peter
	}
	// You can also use break and continue followed by a 
	// label name to refer to an outer for loop:
	outer: for i := 4; true; i++ {
		println(i)
		for {
			if i < 7 {
				continue outer
			} else {
				break outer
			}
		}
	}
}
