package radix

import (
	"strings"
)

//@Title		radix
//@Description
//		前缀基数树的节点
//		可通过节点的分叉对string进行查找
//		增添string时候需要增删结点,同时将结点内置的map中增删对应的string即可
//		当string到终点时存储元素

//node树节点结构体
//该节点是radix的树节点
//结点存储到此时的string的前缀数量
//son存储其下属分叉的子结点指针
//该节点同时存储其元素
type node struct {
	pattern string           //到终点时不为"",其他都为""
	part    string           //以当前结点的string内容
	num     int              //以当前结点为前缀的数量
	sons    map[string]*node //该结点下属结点的指针
	fuzzy   bool             //模糊匹配?该结点首字符为':'或'*'为模糊匹配
}

//@title    newNode
//@description
//		新建一个前缀基数树节点并返回
//		将传入的元素e作为该节点的承载元素
//@receiver		nil
//@param    	name		string					该节点的名字,即其对应的string
//@return    	n        	*node					新建的单词查找树节点的指针
func newNode(part string) (n *node) {
	fuzzy := false
	if len(part) > 0 {
		fuzzy = part[0] == ':' || part[0] == '*'
	}
	return &node{
		pattern: "",
		part:    part,
		num:     0,
		sons:    make(map[string]*node),
		fuzzy:   fuzzy,
	}
}

//@title    analysis
//@description
//		将string按'/'进行分段解析
//		为""部分直接舍弃,返回解析结果
//		同时按规则重组用以解析是string并返回
//@receiver		nil
//@param    	s			string					待解析的string
//@return    	ss			[]string				按'/'进行分层解析后的结果
//@return    	newS		string					按符合规则解析结果重组后的s
func analysis(s string) (ss []string, newS string) {
	vs := strings.Split(s, "/")
	ss = make([]string, 0)
	newS = "/"
	for _, item := range vs {
		if item != "" {
			ss = append(ss, item)
			newS = newS + "/" + item
			if item[0] == '*' {
				break
			}
		}
	}
	return ss, newS
}

//@title    inOrder
//@description
//		以node前缀基数树节点做接收者
//		遍历其分叉以找到其存储的所有string
//@receiver		n			*node					接受者node的指针
//@param    	s			string					到该结点时的前缀string
//@return    	es        	[]interface{}			以该前缀s为前缀的所有string的集合
func (n *node) inOrder(s string) (es []interface{}) {
	if n == nil {
		return es
	}
	if n.pattern != "" {
		es = append(es, s+n.part)
	}
	for _, son := range n.sons {
		es = append(es, son.inOrder(s+n.part+"/")...)
	}
	return es
}

//@title    insert
//@description
//		以node前缀基数树节点做接收者
//		从n节点中继续插入以s为索引的元素e,且当前抵达的string位置为p
//		当到达s终点时进行插入,如果此时node承载了string则插入失败,否则成功
//		当未到达终点时,根据当前抵达的位置去寻找其子结点继续遍历即可
//		当插入失败且对应子结点为新建节点时则需要删除该子结点
//@receiver		n			*node					接受者node的指针
//@param    	pattern		string					待插入的string整体
//@param    	ss			[]string				待删除元素的索引s的按'/'进行分层的索引集合
//@param    	p			int						索引当前抵达的位置
//@return    	b        	bool					是否插入成功?
func (n *node) insert(pattern string, ss []string, p int) (b bool) {
	if p == len(ss) {
		if n.pattern != "" {
			//该节点承载了string
			return false
		}
		//成功插入
		n.pattern = pattern
		n.num++
		return true
	}
	//找到该层的string
	s := ss[p]
	//从其子结点的map中找到对应的方向
	son, ok := n.sons[s]
	if !ok {
		//不存在,新建并放入map中
		son = newNode(s)
		n.sons[s] = son
	}
	//从子结点对应方向继续插入
	b = son.insert(pattern, ss, p+1)
	if b {
		n.num++
	} else {
		if !ok {
			//插入失败且该子节点为新建结点则需要删除该子结点
			delete(n.sons, s)
		}
	}
	return b
}

//@title    erase
//@description
//		以node前缀基数树节点做接收者
//		从n节点中继续删除以s为索引的元素e,且当前抵达的string位置为p
//		当到达s终点时进行删除,如果此时node未承载元素则删除失败,否则成功
//		当未到达终点时,根据当前抵达的位置去寻找其子结点继续遍历即可,若其分叉为nil则直接失败
//@receiver		n			*node					接受者node的指针
//@param    	ss			[]string				待删除元素的索引s的按'/'进行分层的索引集合
//@param    	p			int						索引当前抵达的位置
//@return    	b        	bool					是否删除成功?
func (n *node) erase(ss []string, p int) (b bool) {
	if p == len(ss) {
		if n.pattern != "" {
			//该结点承载是string是,删除成功
			n.pattern = ""
			n.num--
			return true
		}
		return false
	}
	//从map中找到对应下子结点位置并递归进行删除
	s := ss[p]
	son, ok := n.sons[s]
	if !ok || son == nil {
		//未找到或son不存在,删除失败
		return false
	}
	b = son.erase(ss, p+1)
	if b {
		n.num--
		if son.num <= 0 {
			//删除后子结点的num<=0即该节点无后续存储元素,可以销毁
			delete(n.sons, s)
		}
	}
	return b
}

//@title    delete
//@description
//		以node前缀基数树节点做接收者
//		从n节点中继续删除以s为索引的元素e,且当前抵达的string位置为p
//		当到达s终点时进行删除,删除所有后续元素,并返回其后续元素的数量
//		当未到达终点时,根据当前抵达的位置去寻找其子结点继续遍历即可,若其分叉为nil则直接返回0
//@receiver		n			*node					接受者node的指针
//@param    	ss			[]string				待删除元素的索引s的按'/'进行分层的索引集合
//@param    	p			int						索引当前抵达的位置
//@return    	num        	int						被删除元素的数量
func (n *node) delete(ss []string, p int) (num int) {
	if p == len(ss) {
		return n.num
	}
	//从map中找到对应下子结点位置并递归进行删除
	s := ss[p]
	son, ok := n.sons[s]
	if !ok || son == nil {
		return 0
	}
	num = son.delete(ss, p+1)
	if num > 0 {
		son.num -= num
		if son.num <= 0 {
			//删除后子结点的num<=0即该节点无后续存储元素,可以销毁
			delete(n.sons, s)
		}
	}
	return num
}

//@title    count
//@description
//		以node前缀基数树节点做接收者
//		从n节点中继续查找以s为前缀索引的元素e,且当前抵达的string位置为p
//		当到达s终点时返回其值即可
//		当未到达终点时,根据当前抵达的位置去寻找其子结点继续遍历即可,当其分叉为nil则直接返回0
//@receiver		n			*node					接受者node的指针
//@param    	ss			[]string				待删除元素的索引s的按'/'进行分层的索引集合
//@param    	p			int						索引当前抵达的位置
//@return    	num        	int						以该s为前缀的string的数量
func (n *node) count(ss []string, p int) (num int) {
	if p == len(ss) {
		return n.num
	}
	//从map中找到对应下子结点位置并递归进行查找
	s := ss[p]
	son, ok := n.sons[s]
	if !ok || son == nil {
		return 0
	}
	return son.count(ss, p+1)
}

//@title    mate
//@description
//		以node前缀基数树节点做接收者
//		先从radix树的根节点开始找到第一个可以满足该模糊匹配方案的string结点
//		随后将s和结点的pattern进行模糊映射,将模糊查找的值和匹配值进行映射并返回即可
//		若该结点未找到则直接返回nil和false即可
//@receiver		n			*node					接受者node的指针
//@param    	ss			[]string				待删除元素的索引s的按'/'进行分层的索引集合
//@param    	p			int						索引当前抵达的位置
//@return    	m			map[string]string		s从结点中利用模糊匹配到的所有key和value的映射
//@return    	ok			bool					匹配成功?
func (n *node) mate(s string, p int) (m map[string]string, ok bool) {
	//解析url
	searchParts, _ := analysis(s)
	//从该请求类型中寻找对应的路由结点
	q := n.find(searchParts, 0)
	if q != nil {
		//解析该结点的pattern
		parts, _ := analysis(q.pattern)
		//动态参数映射表
		params := make(map[string]string)
		for index, part := range parts {
			if part[0] == ':' {
				//动态匹配,将参数名和参数内容的映射放入映射表内
				params[part[1:]] = searchParts[index]
			}
			if part[0] == '*' && len(part) > 1 {
				//通配符,将后续所有内容全部添加到映射表内同时结束遍历
				params[part[1:]] = strings.Join(searchParts[index:], "/")
				break
			}
		}
		return params, true
	}
	return nil, false
}

//@title    find
//@description
//		以node前缀基数树节点做接收者
//		从radix树的根节点开始找到第一个可以满足该模糊匹配方案的string结点
//		若该结点未找到则直接返回nil
//@receiver		n			*node					接受者node的指针
//@param    	ss			[]string				待删除元素的索引s的按'/'进行分层的索引集合
//@param    	p			int						索引当前抵达的位置
//@return    	m			map[string]string		s从结点中利用模糊匹配到的所有key和value的映射
//@return    	ok			bool					匹配成功?
func (n *node) find(parts []string, height int) (q *node) {
	//根据长度和局部string的首字符进行判断
	if len(parts) == height || strings.HasPrefix(n.part, "*") {
		if n.pattern == "" {
			//匹配失败,该结点处无匹配的信息
			return nil
		}
		//匹配成功,返回该结点
		return n
	}
	//从该结点的所有子结点中查找可用于递归查找的结点
	//当局部string信息和当前层string相同时可用于递归查找
	//当该子结点是动态匹配时也可以用于递归查找
	part := parts[height]
	//从所有子结点中找到可用于递归查找的结点
	children := make([]*node, 0, 0)
	for _, child := range n.sons {
		if child.part == part || child.fuzzy {
			//局部string相同或动态匹配
			children = append(children, child)
		}
	}
	for _, child := range children {
		//递归查询,并根据结果进行判断
		result := child.find(parts, height+1)
		if result != nil {
			//存在一个满足时就可以返回
			return result
		}
	}
	return nil
}