package main

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

// BreadthFirst 队列中的每节点都调用f()。
// f()返回的所有节点都被添加到队列中。
// 对每个节点最多调用一次f()。
func BreadthFirst(f func(item string) []string, worklist []string) {
	// seen
	seen := make(map[string]bool)
	for len(worklist) > 0 {
		// 复制当前队列
		items := worklist
		// 清空队列
		worklist = nil
		// 遍历复制的当前队列
		for _, item := range items {
			// 判断seen, 防止重复添加到队列
			if !seen[item] {
				seen[item] = true
				// 添加到队列
				worklist = append(worklist, f(item)...)
			}
		}
	}
}

// ExtractRestructure 从HTML文档中提取链接，并将相对路径转换为绝对路径
//
//		URL			string,		要提取链接的HTML文档
//		isSave		bool,		是否保存提取到的链接对应的页面
//		saveOriginOnly bool,	是否只保存与当前页面域名相同的页面
//
//	 return		[]string, error,	提取到的链接列表和错误信息
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
	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())
				// 判断是否只保存同一域名下的地址
				if saveOriginOnly && (link.Host != originURL) {
					continue
				}
				// 判断是否保存页面
				if isSave && link.String() != "" {
					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)
					}
					//及时关闭file句柄
					defer file.Close()
					//写入文件时，使用带缓存的 *Writer
					write := bufio.NewWriter(file)
					if _, err := write.WriteString(link.String() + "\r\n"); err != nil {
						panic(err)
					}
					// 将缓存写入文件
					if err := write.Flush(); err != nil {
						panic(err)
					}
				}
			}
		}
	}
	HtmlTools.ForEachNode(node, visitNode, nil)
	return links, nil
}

func main() {
	// crawl 此函数返回一个[]string, 内容为url
	crawl := func(url string) []string {
		fmt.Println("Crawl:", url)
		list, err := extractRestructure(url, true, true)
		if err != nil {
			log.Print(err)
		}
		return list
	}

	// Crawl the web breadth-first,
	// starting from the command-line arguments.
	BreadthFirst(crawl, []string{"https://go.dev/"})
}
