package main

import (
	"encoding/json"
	"fmt"
	"reflect"
	"unsafe"
)

type Counter struct {
	N       int
	Samples []int
}

func (c Counter) TryChange() { c.N++; c.Samples[0]++ }
func (c Counter) Value() int { return c.N }
func (c *Counter) Inc()      { c.N++ }

type Valuer interface{ Value() int }
type Incrementer interface{ Inc() }

var _ Valuer = Counter{}
var _ Valuer = (*Counter)(nil)
var _ Incrementer = (*Counter)(nil)

func receivers() {
	c := Counter{N: 1, Samples: []int{10}}
	c.TryChange()
	fmt.Println(c.N, c.Samples)
	c.Inc()
	fmt.Println(c.N)
	var v Valuer = c
	var p Incrementer = &c
	p.Inc()
	fmt.Println(v.Value(), c.Value())
}

func methodFunctions() {
	c := Counter{N: 1}
	saved := c.Value
	inc := c.Inc
	c.N = 10
	inc()
	fmt.Println(saved(), c.Value())
	read := Counter.Value
	add := (*Counter).Inc
	add(&c)
	fmt.Println(read(c), (*Counter).Value(&c))
	fmt.Printf("%T | %T\n", read, add)
}

type Base struct{}

func (Base) Name() string    { return "base" }
func (b Base) Label() string { return b.Name() }

type Service struct{ Base }

func (Service) Name() string { return "service" }

type ValueEmbedded struct{ Counter }
type PointerEmbedded struct{ *Counter }

var _ Valuer = ValueEmbedded{}
var _ Incrementer = (*ValueEmbedded)(nil)
var _ Valuer = PointerEmbedded{}
var _ Incrementer = PointerEmbedded{}

func embedding() {
	s := Service{}
	fmt.Println(s.Name(), s.Label(), s.Base.Name())
	v := ValueEmbedded{Counter: Counter{N: 3}}
	v.Inc()
	fmt.Println(v.Value(), v.Counter.N)
	p := PointerEmbedded{Counter: &Counter{N: 5}}
	var inc Incrementer = p
	inc.Inc()
	fmt.Println(p.Value())
}

type User struct {
	Name   string `json:"name" validate:"required"`
	Count  int    `json:"count,omitempty"`
	Secret string `json:"-"`
	note   string
}

func tags() {
	u := User{Name: "gopher", Secret: "hidden", note: "private"}
	b, err := json.Marshal(u)
	fmt.Println(string(b), err)
	f, _ := reflect.TypeOf(u).FieldByName("Name")
	fmt.Println(f.Tag.Get("json"), f.Tag.Get("validate"))
	t := reflect.StructTag(`label:""`)
	a, aok := t.Lookup("label")
	z, zok := t.Lookup("missing")
	fmt.Printf("%q %t | %q %t\n", a, aok, z, zok)
}

type Unpacked struct {
	A byte
	B int64
	C byte
}
type Packed struct {
	B int64
	A byte
	C byte
}

func layout() {
	var a Unpacked
	var b Packed
	fmt.Println("unpacked:", unsafe.Sizeof(a), unsafe.Alignof(a),
		unsafe.Offsetof(a.A), unsafe.Offsetof(a.B), unsafe.Offsetof(a.C))
	fmt.Println("packed:", unsafe.Sizeof(b), unsafe.Alignof(b),
		unsafe.Offsetof(b.A), unsafe.Offsetof(b.B), unsafe.Offsetof(b.C))
}

func main() {
	for _, demo := range []struct {
		name string
		run  func()
	}{
		{"receivers", receivers}, {"method functions", methodFunctions},
		{"embedding", embedding}, {"tags", tags}, {"layout", layout},
	} {
		fmt.Println("==", demo.name, "==")
		demo.run()
	}
}
