// the length is not part of the type for a slice

// slices use the same syntax as arrays for accessing/assigning

package main

import "fmt"

func UNUSED(x ...interface{}) {}

func main() {
	// []int makes a slice, [...]int makes an array
	x := []int{10, 20, 30}
	fmt.Println(x)

	x = []int{1, 5: 4, 6, 10: 20, 11}
	fmt.Println(x)
	fmt.Println()

	// access last element in a slice
	fmt.Println(x[len(x)-1])
	fmt.Println()

	// empty slice is nil value (no type)
	// can only compare slices to nil
	var y []int
	fmt.Println(y == nil)
	fmt.Println()

	// this notiation declares a nil slice
	var y0 []int
	fmt.Println("y0", y0 == nil)
	// this notation declares an empty slice literal
	var y1 = []int{}
	fmt.Println("y1", y1 == nil)
	// they are the same value ([]), but one is nil and one is not!
	fmt.Println(y0, y1)
	fmt.Println()

	// can append more than one at a time
	y = append(y, 1)
	y = append(y, 2, 3)
	fmt.Println(y)
	fmt.Println()

	// can use ... notation to unpack
	x = append(x, y...)
	fmt.Println(x)
	fmt.Println()

	// print the capacity of the slize (the memory reserved)
	// show how it increases as you add more
	var z []int
	fmt.Println(z, len(z), cap(z))
	z = append(z, 10)
	fmt.Println(z, len(z), cap(z))
	z = append(z, 20)
	fmt.Println(z, len(z), cap(z))
	z = append(z, 30)
	fmt.Println(z, len(z), cap(z))
	z = append(z, 40)
	fmt.Println(z, len(z), cap(z))
	z = append(z, 50)

	fmt.Println(z, len(z), cap(z))
	fmt.Println()

	//
	// make specifies type, length, capacity

	// slice of ints length 5 (5 0's)
	// don't use append with this as youll get [0,0,0,0,0,$append_val]
	a := make([]int, 5)
	fmt.Println(a)
	fmt.Println()

	// slice of ints length 0 capcity 10
	b := make([]int, 0, 10)
	fmt.Println(b)
	// can use append with this
	b = append(b, 1)
	fmt.Println(b, len(b), cap(b))
	fmt.Println()

	//
	// Declaring slices
	// the idea is to minimise the number of times it needs to grow

	// slice that is nil (use when it might stay null)
	var data_nil []int
	UNUSED(data_nil)

	// slice that is empty (not nill)
	var data_empty = []int{}
	UNUSED(data_empty)

	// slice literal
	// useful if you have starting values/values that won't change (hardcoded)
	data_literal := []int{1, 2, 3}
	UNUSED(data_literal)

	// slice make
	// if you have an idea how large the slice should be, but not the values, use `make`

	// option 1 - specify non-zero length - useful for using slices as buffers
	data_option_1 := make([]int, 5)
	UNUSED(data_option_1)

	// option 2 - specify exact length - if you know the exact length you nneed
	data_option_2 := make([]int, 10)
	UNUSED(data_option_2)

	// option 3 - zero length & capacity - for everything else
	data_option_3 := make([]int, 0, 10)
	UNUSED(data_option_3)

	//
	// Slicing slices
	sliced := []int{1, 2, 3, 4, 5}
	sliced_first_2 := sliced[:2]
	sliced_skip_first := sliced[1:]
	sliced_middle := sliced[2:3]
	fmt.Println("slicing slices")
	fmt.Println("slice:", sliced)
	fmt.Println("sliced_first_2:", sliced_first_2)
	fmt.Println("sliced_skip_first:", sliced_skip_first)
	fmt.Println("sliced_middle:", sliced_middle)
}
