package main

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

func main() {
	// 同样的，函数可以作为参数传入另一个函数
	// forEachNode针对每个结点x，都会调用pre(x)和post(x)。
	// pre和post都是可选的。
	// 遍历孩子结点之前，pre被调用
	// 遍历孩子结点之后，post被调用
	var forEachNode func(n *html.Node, pre, post func(n *html.Node))
	forEachNode = func(n *html.Node, pre, post func(n *html.Node)) {
		if pre != nil {
			pre(n)
		}
		for c := n.FirstChild; c != nil; c = c.NextSibling {
			forEachNode(c, pre, post)
		}
		if post != nil {
			post(n)
		}
	}
	var depth int
	var ExcludeNodes []string = []string{
		"link",
		"img",
		"meta",
	}
	// 断言
	m, _ := ToMapSetStrictE(ExcludeNodes)
	ExcludeNodesMap := m.(map[string]struct{})
	startElemen := func(n *html.Node) {
		if n.Type == html.ElementNode {
			fmt.Printf("%*s<%s", depth*2, "", n.Data)
			for _, attr := range n.Attr {
				fmt.Printf(" %s=\"%s\"", attr.Key, attr.Val)
			}
			// 检测单节点
			if _, ok := ExcludeNodesMap[n.Data]; ok {
				fmt.Println(" />")
			} else {
				fmt.Println(">")
			}
			depth++
		}
	}
	endElement := func(n *html.Node) {
		if n.Type == html.ElementNode {
			depth--
			// 检测单节点
			if _, ok := ExcludeNodesMap[n.Data]; !ok {
				fmt.Printf("%*s</%s>\n", depth*2, "", n.Data)
			}
		}
	}
	forEachNode(HtmlTools.GetHtml(), startElemen, endElement)
}

// 借助一个空接口，使切片转为map
func ToMapSetStrictE(i interface{}) (interface{}, error) {
	// check param
	if i == nil {
		return nil, fmt.Errorf("unable to converts %#v of type %T to map[interface{}]struct{}", i, i)
	}
	t := reflect.TypeOf(i)
	kind := t.Kind()
	if kind != reflect.Slice && kind != reflect.Array {
		return nil, fmt.Errorf("the input %#v of type %T isn't a slice or array", i, i)
	}
	// execute the convert
	v := reflect.ValueOf(i)
	mT := reflect.MapOf(t.Elem(), reflect.TypeOf(struct{}{}))
	mV := reflect.MakeMapWithSize(mT, v.Len())
	for j := 0; j < v.Len(); j++ {
		mV.SetMapIndex(v.Index(j), reflect.ValueOf(struct{}{}))
	}
	return mV.Interface(), nil
}
