package main

import (
	"Study/src/study/day7Function/HtmlTools"
	"bufio"
	"fmt"
	"golang.org/x/net/html"
	"log"
	"net/http"
	"os"
	"strings"
	"time"
)

// defer语句经常被用于处理成对的操作，
// 如打开、关闭、连接、断开连接、加锁、释放锁。
// 通过defer机制，不论函数逻辑多复杂，都能保证在任何执行路径下，资源被释放。
// 释放资源的defer应该直接跟在请求资源的语句后。

func title(url string) error {
	resp, err := http.Get(url)
	if err != nil {
		return err
	}
	// 直到包含defer的函数执行完毕之后，defer的这条语句才会执行
	defer resp.Body.Close()
	// Check Content-Type is Html (e.g., "text/html;charset=utf-8").
	ct := resp.Header.Get("Content-Type")
	if ct != "text/html" && !strings.HasPrefix(ct, "text/html;") {
		return fmt.Errorf("%s has type %s, not text/html", url, ct)
	}
	doc, err := html.Parse(resp.Body)

	if err != nil {
		return fmt.Errorf("parsing %s as Html: %v", url, err)
	}
	visitNode := func(n *html.Node) {
		if n.Type == html.ElementNode && n.Data == "title" && n.FirstChild != nil {
			fmt.Println(n.FirstChild.Data)
		}
	}
	HtmlTools.ForEachNode(doc, visitNode, nil)
	return nil
}

// 每一次bigSlowOperation被调用，程序都会记录函数的进入，退出，持续时间。
// (我们用time.Sleep模拟一个耗时的操作）
func bigSlowOperation() {
	defer trace("aaa")()
	time.Sleep(3 * time.Second)
}
func trace(msg string) func() {
	start := time.Now()
	log.Printf("enter %s", msg)
	return func() {
		log.Printf("leave %s, (%s)", msg, time.Since(start))
	}
}

// defer 可在函数调用执行完毕时，打印一些调试信息
func double(x int) (result int) {
	defer func() { fmt.Println(x) }()
	return x + x
}
func triple(x int) (result int) {
	defer func() { result += x }()
	return double(x)
}
func main() {
	//if err := title(os.Args[1]); err != nil {
	//	log.Fatalln(err)
	//}
	bigSlowOperation()

	fmt.Println(triple(3)) // 9

	//⚠️注意，在循环体里调用defer时，由于defer所在的语句要在整个函数执行完毕时在执行，
	//  这样的代码会导致系统的文件描述符耗尽，因为在所有文件都被处理之前，没有文件会被关闭。

}

// 我们可以稍微对以前练习1.13的代码做改进
func extractRestructure(URL string, isSave bool, saveOriginOnly bool) ([]string, error) {
	// 检查参数
	if URL == "" {
		URL = "https://go.dev/"
	}

	node, resp := HtmlTools.GetHtmlResponse(URL)
	originURL := resp.Request.URL.Host
	var links []string

	// 这样就不会有重复打开文件的浪费资源的bug了，
	// 也没有在循环体里调用defer
	// 定义变量
	var filePath string
	var file *os.File
	// 定义保存路径
	if isSave {
		var err error
		filePath = HtmlTools.GetCurrentAbPath() + "/www/urls.txt"
		// 打开文件
		file, err = os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
		if err != nil {
			fmt.Println("文件打开失败", err)
		}
	}
	// 关闭文件
	defer file.Close().Error()
	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
				}
				// 判断是否只保存同一域名下的地址
				if saveOriginOnly && (link.Host != originURL) {
					continue
				}
				// 判断是否保存页面
				if isSave && link.String() != "" {
					write := bufio.NewWriter(file)
					if _, err := write.WriteString(link.String() + "\r\n"); err != nil {
						panic(err)
					}
					// 将缓存写入文件
					if err := write.Flush(); err != nil {
						panic(err)
					}
				}
				// 将link添加到切片
				links = append(links, link.String())

			}
		}
	}
	HtmlTools.ForEachNode(node, visitNode, nil)
	return links, nil
}
