package main

import (
	"Study/src/study/day7Function/HtmlTools"
	"fmt"
	"golang.org/x/net/html"
	"strings"
)

func main() {
	// 函数字面量允许我们在使用函数时，再定义它。
	// 通过这种技巧，我们可以改写之前对strings.Map的调用：
	str1 := strings.Map(func(r rune) rune { return r + 1 }, "HAL-9000")
	fmt.Println(str1)

	// 定义变量保存square的返回值
	sqar := square()
	// 此时sqar里保存的是返回值，也就是相当于一个函数类型的变量
	// sqar 相当于

	//sqar = func(x int) int {
	//	x++
	//	return x * x
	//}

	// 只不过在sqar := square()里，x的值不来自于形参的x，而是来自于上一级快语句square中。

	sqar = square()
	fmt.Println(sqar()) // 1
	fmt.Println(sqar()) // 4
	fmt.Println(sqar()) // 9
	fmt.Println(sqar()) // 16
	// 我们看到变量的生命周期不由它的作用域决定：squares返回后，变量x仍然隐式的存在于sqar中。
	// 直到我们销毁sqar变量，资源被gc回收，或者程序运行完成被系统回收
	// 这就是 闭包(closures)
	// 闭包 一定是在返回的函数里面使用了外部函数的资源
	// 闭包 通常用于扩展函数的功能
	links, err := Extract()
	if err != nil {
		fmt.Println(err)
	}
	for _, link := range links {
		fmt.Println(link)
	}
}

// 函数square返回另一个类型为 func() int 的函数，
// 对square的第一次调用会生成第一个局部变量x并返回一个匿名函数，
// 每次调用此匿名函数时, 改函数都会使x的值+1, 再返回x的平方,
// 第二次调用square时, 会生成新的匿名函数。
// 新的匿名函数操作的也是一个新的x变量
// ⚠️需要注意x的作用域和生存周期
// ⚠️x的作用域在匿名函数里是属于上一级块语句提供的，
// ⚠️所以只要square的返回值被保存了
// ⚠️匿名函数就可以访问到x变量
func square() func() int {
	var x int
	return func() int {
		x++
		return x * x
	}
}

// Extract
//
//	现在links中存储的不是href属性的原始值，
//	而是通过resp.Request.URL解析后的值。
//	解析后，这些连接以绝对路径的形式存在，可以直接被http.Get访问。
func Extract() ([]string, error) {
	node, resp := HtmlTools.GetHtmlResponse("")
	var links []string
	visitNode := func(n *html.Node) {
		if n.Type == html.ElementNode && n.Data == "a" {
			for _, a := range n.Attr {
				if a.Key != "href" {
					continue
				}
				// 将相对路径和url的内容组合在一起形成完整的绝对路径
				link, err := resp.Request.URL.Parse(a.Val)
				if err != nil {
					// 忽略掉报error的URL
					fmt.Println(err)
					continue
				}
				// 将link添加到切片
				links = append(links, link.String())
			}
		}
	}
	HtmlTools.ForEachNode(node, visitNode, nil)
	return links, nil
}
