package main

import (
	"encoding/json"
	"fmt"
)

func main() {
	person := &Person{
		Name: "张三",
		age:  18,
	}
	// 通过一个一个的函数 去处理数据的 编程方式，就是面向过程编程
	ChangePersonAge(person, 28)
	// 修改体重 ChangeWeight 的函数
	fmt.Println(person.age)

	// 面向对象编程，有更好封装，也贴近与现实世界
	person.ChangePersonAge(20)
	fmt.Println("修改后的年龄:", person.age)
	fmt.Println(person.age)
	person.我的名字()

	// 修改了wei lght吗？
	person.SetWeight(80)
	fmt.Println("修改后的体重:", person.Weight)

	fmt.Println("结构体与面向对象")
	// 创建交通工具
	// 能做的动作 -> 海豚跳(Jump)
	bike := &Bike{TranficTool{
		Name:  "自行车",
		Speed: 20,
		Color: "黑色",
	}, PlayTool{}}

	// bike.TranficTool.Drive()
	bike.Drive()
	// bike.PlayTool.Play()
	bike.Play()

	// bike.PlayTool.Jump()
	// bike.Jump()
	// Rewrite 方法重写
	bike.Jump()
	bike.PlayTool.Jump()

	// 炸街
	motor := &Moto{TranficTool{
		Name:  "摩托车",
		Speed: 80,
		Color: "蓝色",
	}}
	motor.Drive()
	motor.Music()

	// 打开空调
	car := &Car{TranficTool{
		Name:  "汽车",
		Speed: 120,
		Color: "白色",
	}}
	car.Drive()
	car.AirConditioning()

	// Json
	bike.move = true
	bikeJson, err := json.Marshal(bike)
	if err != nil {
		panic(err)
	}
	// 类型转换： []byte -> string
	fmt.Println(bikeJson)
	fmt.Println("bike json string: ", string(bikeJson))
	// 123 34 78 97 109 101 34 58 34 33258 34892 36710 34 44 34 83 112 101 101 100 34 58 50 48 44 34 67 111 108 111 114 34 58 34 40657 33394 34 125
	for _, b := range string(bikeJson) {
		fmt.Printf("%v ", b)
	}

	// json.Unmarshal()
	bike_cloned := &Bike{}
	if err := json.Unmarshal(bikeJson, bike_cloned); err != nil {
		panic(err)
	}
	fmt.Println("bike source:", bike)
	fmt.Println("bike clone:", bike_cloned)
}

// Person 是一个表示人的结构体，包含姓名、年龄、性别、体重和喜欢的颜色等字段。
// 也是一个复合数据类型。
type Person struct {
	Name string
	// 不对外暴露的字段
	age           int
	Gender        Gender
	Weight        uint
	FavoriteColor []string
}

// func ChangePersonAge(p *Person, newAge int)
func (p *Person) ChangePersonAge(newAge int) {
	p.age = newAge
}

// 修改的
// SetWeight(p *Person)
func (p *Person) SetWeight(newWeight uint) {
	p.Weight = newWeight
}

// 不涉及到对象修改的，把这个方法绑定给值也是可以
// GetWeight(p Person)
func (p Person) GetWeight() uint {
	return p.Weight
}

func (p *Person) 我的名字() string {
	return p.Name
}

// GenderXXX 表示枚举值
// Gender 是一种自定义类型，底层类型是 int
// Gender != int
type Gender int

// Sex == Gender
type Sex = Gender

const (
	GenderMale Gender = iota
	GenderFemale
)

// ChangePersonAge 修改Person的年龄
func ChangePersonAge(p *Person, newAge int) {
	// 自动解开引用(*p)
	// * -> 解引用(Unbox)
	// (*p).age = newAge
	// p 指针类型， 自动解引用
	// . 访问字段
	// 自动解引用
	// p.age == (*p).age
	// 所以可以直接写成下面这样
	p.age = newAge
}

type TranficTool struct {
	// `` 表示字符串 都是 Struct Tag, 结构体标签，lib 反射使用
	Name  string `json:"name"`
	Speed int    `json:"speed"`
	Color string `json:"color"`
	move  bool
}

// Drive 是交通工具的行驶方法
func (t *TranficTool) Drive() {
	fmt.Printf("%s 正在以 %d 的速度前进\n", t.Name, t.Speed)
}

// PlayTool 是一个表演工具
type PlayTool struct{}

func (p *PlayTool) Play() {
	fmt.Println("开始我的表演: 后空翻")
}

func (b *PlayTool) Jump() {
	fmt.Println("海豚跳： 高级版")
}

// Bike 是自行车
type Bike struct {
	// 嵌入了交通工具, 继承了交通工具的属性和方法 (可以上班)
	TranficTool
	// 嵌套了PlayTool, 继承了PlayTool的方法(可以表演)
	PlayTool
}

// review的核心逻辑: 优先找方法接收者所属的结构体的方法(优先级最高)
// 如果找不到，再去找嵌入的结构体的方法
// 如果嵌入的结构体也找不到，再去找嵌入的结构体的嵌入结构体的方法
// 直到找到为止
// 等同于重写(Rewrite)，覆盖了之前的继承而来的方法
func (b *Bike) Jump() {
	fmt.Println("海豚跳 基础版")
}

// Bike 重写了 Drive 方法，定制了自己的 Drive 方法
func (b *Bike) Drive() {
	fmt.Printf("%s 正在以 %d 的速度 脚踏前进\n", b.Name, b.Speed)
}

// Moto 是摩托车
type Moto struct {
	// 嵌入了交通工具, 继承了交通工具的属性和方法
	TranficTool
}

func (m *Moto) Music() {
	fmt.Println("炸街")
}

// Car 是汽车
type Car struct {
	// 嵌入了交通工具, 继承了交通工具的属性和方法
	TranficTool
}

// AirConditioning 是汽车的空调方法
func (c *Car) AirConditioning() {
	fmt.Println("打开空调")
}
