package main

import (
	"fmt"
	"io"
	"os"
)

func main() {
	//
	dog := Dog{Name: "旺财"}
	fmt.Println(dog.Speak())
	//
	cat := Cat{Name: "招财猫"}
	fmt.Println(cat.Speak())
	//
	cow := Cow{Name: "小牛"}
	fmt.Println(cow.Speak())
	// 必须知道对象的对象, 才能调用, 面向具体类型来编写代码, 依赖具体类型, 耦合度高

	// 没有一层统一抽象，上层业务逻辑，需要知道对象，才能调用，非常麻烦，高耦合
	// speakers := []interface{}{dog, cat, cow}
	// speakers := []any{dog, cat, cow}
	// for _, speaker := range speakers {
	// 	switch speaker.(type) {
	// 	case Dog:
	// 		fmt.Println(speaker.(Dog).Speak())
	// 	case Cat:
	// 		fmt.Println(speaker.(Cat).Speak())
	// 	case Cow:
	// 		fmt.Println(speaker.(Cow).Speak())
	// 	case Person:
	// 		fmt.Println(speaker.(Person).Speak())
	// 	}
	// 	//...
	// }

	person := Person{Name: "小明"}

	// 管理员，组织这些演技者，进行演讲
	// interface 先制定规范， 底层系统按照规范进行实现，上层业务代码的编写将会是透明的，简单高效
	// 这里的Speaker 和 Dog Cat Cow 都是type 他们是一种东西吗？
	// speakers = []Dog{}{dog1,dog2, dog3}
	// Speaker 是一种约束, 约束放入这个slice里面的对象，必须实现这个接口的所有方法
	speakers := []Speaker{dog, cat, cow, person}
	for _, speaker := range speakers {
		fmt.Println(speaker.Speak())
		// 类型断言, 判断这个speaker是否是Dog类型, 如果是就调用Dog的GetName方法
		// 断言成一个具体的类型来使用，一般不建议，因为这样就失去了接口的意义，接口的意义就是不关心具体类型，只关心方法
		// if dog, ok := speaker.(Dog); ok {
		// 	fmt.Println(dog.GetName())
		// }
		// 不安全方式（会panic）
		// dog := a.(Dog) // 如果a不是Dog类型，会panic
		// speaker.(Dog).GetName() // 只能调用接口的方法, 不能调用具体类型的方法
	}
	// 只关注 方法(接口), 不关注 对象的编程的方式，就是面向接口

	// 1. 使用固定1k缓冲区循环读取文件并输出
	buf := NewBuffer()
	f, err := os.Open("main.go")
	if err != nil {
		panic(err)
	}
	defer f.Close()

	// 循环读取：文件 -> 缓冲区 -> 标准输出
	for {
		// 清空缓冲区准备接收新数据
		buf.Reset()
		// 从文件读取最多1k数据到缓冲区
		n, err := io.CopyN(buf, f, 1024)
		if n > 0 {
			// 将缓冲区的数据输出到标准输出
			fmt.Printf("\n--- 读取了 %d 字节 ---\n", n)
			io.Copy(os.Stdout, buf)
		}
		if err == io.EOF {
			break
		}
		if err != nil {
			panic(err)
		}
	}
}

// 任何一个结构体 实现了这个接口的所有方法, 就实现了这个接口
// 接口是一层抽象, 只要实现了这个接口的所有方法, 就可以被当做这个接口来使用，屏蔽了底层系统
type Speaker interface {
	Speak() string   // 说话方法
	GetName() string // 获取名字方法
}

// 2. 定义不同的类型
type Dog struct {
	Name string
}

// 3. 实现接口方法
func (d Dog) Speak() string {
	return "汪汪汪"
}

func (d Dog) GetName() string {
	return d.Name
}

type Cat struct {
	Name string
}

func (c Cat) Speak() string {
	return "喵喵喵"
}

func (c Cat) GetName() string {
	return c.Name
}

type Cow struct {
	Name string
}

func (c Cow) Speak() string {
	return "哞哞哞"
}
func (c Cow) GetName() string {
	return c.Name
}

// 标注自己实现了Speaker 不需要
type Person struct {
	Name string
}

func (p Person) Speak() string {
	return "hello"
}

func (p Person) GetName() string {
	return p.Name
}

func NewBuffer() *Buffer {
	return &Buffer{
		// 固定 1k 大小的缓冲区
		buf:     make([]byte, 1024),
		dataLen: 0,
	}
}

// 实现 Reader和 Writer方法
type Buffer struct {
	buf     []byte // 固定大小的缓冲区
	dataLen int    // 当前缓冲区中的数据量
}

func (b *Buffer) Read(p []byte) (n int, err error) {
	if b.dataLen == 0 {
		return 0, io.EOF
	}
	// 读取缓冲区中的数据
	n = copy(p, b.buf[:b.dataLen])
	fmt.Println("read: ", n)
	// 移除已读数据（将未读数据前移）
	copy(b.buf, b.buf[n:b.dataLen])
	b.dataLen -= n
	return n, nil
}

func (b *Buffer) Write(p []byte) (n int, err error) {
	// 计算剩余空间
	available := len(b.buf) - b.dataLen
	if available == 0 {
		// 缓冲区已满
		return 0, io.ErrShortBuffer
	}
	// 只写入缓冲区能容纳的数据（固定大小，不扩展）
	n = copy(b.buf[b.dataLen:], p)
	b.dataLen += n
	fmt.Printf("write: %d 字节, 缓冲区使用: %d/%d\n", n, b.dataLen, len(b.buf))

	// 如果没能写入全部数据，返回 ErrShortBuffer
	if n < len(p) {
		return n, io.ErrShortBuffer
	}
	return n, nil
}

func (b *Buffer) String() string {
	return string(b.buf[:b.dataLen])
}

// Reset 清空缓冲区
func (b *Buffer) Reset() {
	b.dataLen = 0
}

// end
