dnsConfigure() {
	if [ "$ipv6Opt" = 'deny' ]; then
		qs='UseIPv4'
	else
		qs='UseIP'
	fi
	if [ "$deny_ad" = '1' ]; then
		hostsObj='
		"hosts": {
			"geosite:category-ads-all": "127.0.0.2"
		}, '
	fi
	if [ -n "$cnNodes" ]; then
		result="{$hostsObj"'
		"servers": [{
			"address": "'$globalDNS'",
			"port": 53,
			"domains": ["geosite:google-cn"]
		}, {
			"address": "'$cnDNS'",
			"port": '53',
			"domains": ["geosite:cn" , "domain:ikandy.fun", "full:cn.ipcelou.com"]
		}],
		"tag": "resolve",
		"queryStrategy": "'$qs'"
	}'
	else
		result="{$hostsObj"'
		"servers": [{
			"address": "'$globalDNS'",
			"port": 53
		}],
		"tag": "resolve",
		"queryStrategy": "'$qs'"
	}'
	fi
}

inConfigure() {
	[ "$deny_ad" = '1' -o -n "$cnNodes" ] && sniffingOpt='true' || sniffingOpt='false'
	result='{
		"tag": "socks-in",
		"port": 10800,
		"protocol": "socks",
		"settings": {
			"network": "tcp,udp",
			"udp": true
		},
		"sniffing": {
			"enabled": '$sniffingOpt',
			"destOverride": ["http","tls"]
		}
	}'
}

vnextConfigure() {
	unset result
	for ip in ${add}; do
		if [ -n "$result" ]; then
			result="${result}, "
		fi
		result="$result"'{
				"address": "'$ip'",
				"port": '$port',
				"users": [{
					"id": "'$id'",
					"alterId": '${aid:-0}',
					"security": "'$scy'",
					"flow": "'$flow'",
					"encryption": "'${encryption:-none}'"
				}]
			}'
	done
}

trojanConfigure() {
	unset result
	for ip in ${add}; do
		if [ -n "$result" ]; then
			result="${result}, "
		fi
		result="$result"'{
				"address": "'$ip'",
				"port": '$port',
				"password": "'$id'",
				"flow": "'$flow'"
			}'
	done
}

streamSettingsConfigure() {
	if [ -n "$alpn" ]; then
		alpn="\"${alpn//,/\", \"}\""
	else
		alpn='"h2", "http/1.1"'
	fi
	if [ "$security" = 'reality' ]; then
		securitySettings='"realitySettings": {
				"show": false,
				"fingerprint": "'${fp:-chrome}'",
				"serverName": "'${sni:-$host}'",
				"publickey": "'${pbk}'",
				"shortId": "'${sid}'",
				"spiderX": "'${spx:-/`date +%s`}'"
			}'
		security='reality'
		scy="${scy:-zero}"  #开启reality或tls后, 默认不加密
	elif [ "$tls" = 'tls' -o "$security" = 'tls' ]; then
		securitySettings='"tlsSettings": {
				"allowInsecure": '${allowInsecure:-true}',
				"fingerprint": "'${fp:-chrome}'",
				"serverName": "'${sni:-$host}'",
				"alpn": ['$alpn']
			}'
		security='tls'
		scy="${scy:-zero}"  #开启reality或者tls后, 默认不加密
	else
		securitySettings='"tlsSettings": {}'
		security='none'
		scy="${scy:-auto}"
	fi
	if [ "$protocol" = 'vmess' ]; then
		headerType="$type"
		unset type
	fi
	if echo "${net}${type}" | grep -qi 'ws'; then
		#设置多行请求头
		unset h
		for l in ${host:-$add}; do
			l=`echo "$l" | grep -Eo '[[:print:]]+'`  ##只允许可打印字符
			if [ -z "$h" ]; then
				h="\"Host\": \"$l\""
			else
				if echo "$l" | grep -q ':'; then
					h="$h,
					\"${l%%:*}\": \"${l#*:}\""
				else
					h="$h,
					\"${l}\": \"\""
				fi
			fi
		done
		result='{
			"sockopt": {
				"tcpFastOpen": '${tfoOpt:-false}',
				"tcpKeepAliveInterval": 15
			},
			"network": "ws",
			"wsSettings": {
				"path": "'${path:-/}'",
				"headers": {
					'$h'
				}
			},
			"security": "'$security'",
			'$securitySettings'
		}'
	elif echo "${net}${type}" | grep -qi 'kcp'; then
		[ -n "$path$seed" ] && seed='"seed": "'$path$seed'",'
		result='{
			"network": "mkcp",
			"kcpSettings": {
				'$seed'
				"header": {
					"type": "'${headerType:-none}'"
				}
			},
			"security": "'$security'",
			'$securitySettings'
		}'
	elif echo "${net}${type}" | grep -qi 'quic'; then
		quicSecurity="$host$quicSecurity"
		[ -n "$path$key" ] && key='"key": "'$path$key'",'
		result='{
			"network": "quic",
			"quicSettings": {
				"security": "'${quicSecurity:-none}'",
				'$key'
				"header": {
					"type": "'${headerType:-none}'"
				}
			},
			"security": "'$security'",
			'$securitySettings'
		}'
	else
		#设置多行请求头
		unset h
		for l in ${host:-$add}; do
			l=`echo "$l" | grep -Eo '[[:print:]]+'`  ##只允许可打印字符
			if [ -z "$h" ]; then
				h="\"Host\": [\"$l\"]"
			else
				if echo "$l" | grep -q ':'; then
					h="$h,
						\"${l%%:*}\": [\"${l#*:}\"]"
				else
					h="$h,
						\"${l}\": [\"\"]"
				fi
			fi
		done
		result='{
			"sockopt": {
				"tcpFastOpen": '${tfoOpt:-false}',
				"tcpKeepAliveInterval": 15
			},
			"network": "tcp",
			"security": "'$security'",
			'$securitySettings
			if [ "$headerType" = 'http' ]; then
				result="$result"',
			"tcpSettings": {
				"header": {
					"type": "http",
					"request": {
						"version": "1.1",
						"method": "GET",
						"path": ["'${path:-/}'"],
						"headers": {
							"Connection": ["keep-alive"],
							"Accept-Encoding": ["gzip, deflate"],
							"User-Agent": ["QQBrowser"],
							'$h'
						}
					}
				}
			}'
		fi
		result="$result"'
		}'
	fi
}

outConfigure() {
	tag="$1"
	file="$2"
	unsetVConf
	. "${file}"
	[ "$mux" != 'true' ] && mux=false || mux=true
	if [ "$protocol" = 'freedom' ]; then
		result='{
		"protocol": "'$protocol'",
		"tag": "'$tag'"
	}'
	elif [ "$protocol" = "vless" -o "$protocol" = "vmess" ]; then
		streamSettingsConfigure
		streamSettingsObj="$result"
		vnextConfigure
		vnextObj="$result"
		result='{
		"protocol": "'$protocol'",
		"tag": "'$tag'",
		"settings": {
			"vnext": ['"$vnextObj"']
		},
		"mux": {
			"enabled": '$mux',
			"concurrency": 64
		},
		"streamSettings": '"$streamSettingsObj"'
	}'
	elif [ "$protocol" = 'trojan' ]; then
		streamSettingsConfigure
		streamSettingsObj="$result"
		trojanConfigure
		trojanObj="$result"
		result='{
		"protocol": "'$protocol'",
		"tag": "'$tag'",
		"settings": {
			"servers": ['"$trojanObj"']
		},
		"mux": {
			"enabled": '$mux',
			"concurrency": 64
		},
		"streamSettings": '"$streamSettingsObj"'
	}'
	else
		exec echo "不支持的协议: $protocol"
	fi
}

routeRulesConfigure() {
	if [ "$deny_ad" = '1' ]; then
	ad_rule='{
			"type": "field",
			"inboundTag": ["socks-in"],
			"domain": ["geosite:category-ads-all"],
			"outboundTag": "denyNetwork"
		}'
	fi
	if [ -n "$globalUdpNodes" ]; then
		global_udp_rule='{
			"type": "field",
			"inboundTag": ["socks-in"],
			"network": "udp",
			"balancerTag": "global-udp-balancers"
		}'
	fi
	if [ -n "$cnNodes" ]; then
		cn_rule='{
			"type": "field",
			"inboundTag": ["resolve"],
			"ip": ["'$cnDNS'"],
			"balancerTag": "cn-balancers"
		}, {
			"type": "field",
			"inboundTag": ["resolve"],
			"ip": ["'$globalDNS'"],
			"balancerTag": "global-balancers"
		}, {
			"type": "field",
			"inboundTag": ["socks-in"],
			"domain": ["geosite:google-cn", "full:cn.ipcelou.com"],
			"balancerTag": "global-balancers"
		}, {
			"type": "field",
			"inboundTag": ["socks-in"],
			"ip": ["geoip:cn"],
			"balancerTag": "cn-balancers"
		}, {
			"type": "field",
			"inboundTag": ["socks-in"],
			"domain": ["geosite:cn", "domain:ikandy.fun"],
			"balancerTag": "cn-balancers"
		}'
	fi
	result='{
			"type": "field",
			"inboundTag": ["socks-in"],
			"port": "53,5353",
			"outboundTag": "dns-out"
		}'
	[ -n "$ad_rule" ] && result="${result}, ${ad_rule}"
	[ -n "$global_udp_rule" ] && result="${result}, ${global_udp_rule}"
	[ -n "$cn_rule" ] && result="${result}, ${cn_rule}"
	result="${result}"', {
			"type": "field",
			"inboundTag": ["socks-in"],
			"balancerTag": "global-balancers"
		}'
}

routeBalancersConfigure() {
	if [ "$observatoryOpt" -le '3' -a "$observatoryOpt" -ge '1' ]; then
		strategyType="leastPing"
	else
		strategyType="random"
	fi
	result='{
			"tag": "global-balancers",
			"selector": ["global-out"],
			"strategy": {
				"type": "'$strategyType'"
			}
		}'
	if [ -n "$cnNodes" ]; then
		result="$result"', {
			"tag": "cn-balancers",
			"selector": ["cn-out"],
			"strategy": {
				"type": "'$strategyType'"
			}
		}'
	fi
	if [ -n "$globalUdpNodes" ]; then
		result="$result"', {
			"tag": "global-udp-balancers",
			"selector": ["global-udp-out"],
			"strategy": {
				"type": "'$strategyType'"
			}
		}'
	fi
}

observatoryConfigure() {
	if [ "$observatoryOpt" = '1' ]; then
		result='
		"subjectSelector": ["global-out", "cn-out", "global-udp-out"],
		"probeURL": "http://www.google.com/generate_204",
		"probeInterval": "'${probeInterval:-30}'s"
	'
	elif [ "$observatoryOpt" = '2' ]; then
		result='
		"subjectSelector": ["global-out", "cn-out", "global-udp-out"],
		"probeURL": "http://connect.rom.miui.com/generate_204",
		"probeInterval": "'${probeInterval:-30}'s"
	'
	else
		result=''
	fi
}

domainToIPs() {
	./downloadCore.sh "$mlHost" 'MLBox' 'Usage'  #下载MLBox
	record=`grep " $add$" domainRecords.txt 2>/dev/null`
	if [ -n "$record" ]; then
		ips=${record% *}
	elif echo "$add" | grep -v ':' |grep -q '[a-zA-Z]'; then
		dnsRetry=0
		unset ips
		while [ "${dnsRetry}" != '3' ]; do
			if [ -z "$ipVersion" ]; then
				echo "正在解析域名: $add"
				ips="`./MLBox -dns=${add} -timeout=10`"
				ips=`echo "$ips" | grep -v '()' | grep -E "[1-9][0-9]{0,2}(\.[0-9]{1,3}){3}|:"`
			elif [ "$ipVersion" = 'ipv4' ]; then
				ips="`./MLBox -dns="-domain=${add} -qtype=A" -timeout=10`"
				ips=`echo "$ips" | grep -v '()' | grep -E "[1-9][0-9]{0,2}(\.[0-9]{1,3}){3}"`
			elif [ "$ipVersion" = 'ips' ]; then
				ips="`./MLBox -dns="-domain=${add} -qtype=AAAA" -timeout=10`"
				ips=`echo "$ips" | grep -v '()' | grep ':'`
			fi
			[ -n "$ips" ] && {
				echo "域名解析结果[$add]: [${ips//$ln/,}]"
				break
			}
			dnsRetry=$((dnsRetry+1))
			#做多解析3次
			[ "$dnsRetry" != '3' ] && echo "第$dnsRetry次重试解析域名: $add"
		done
		#解析失败则跳过
		if [ -z "$ips" ]; then
			echo "域名解析失败: [$add]"
		fi
	else
		ips=$add
	fi
	for ip in $ips; do
		echo "$ip $add" >>domainRecords.txt
	done
}

unsetVConf() {
	unset ipVersion tfoOpt mux protocol add port aid id net type path host tls sni flow scy alpn security uuid remote_host remote_port descriptive_text encryption headerType seed quicSecurity key serviceName mode fp allowInsecure
}

initNodeFile() {
	unset globalNodes
	unset cnNodes
	rm -rf 'CnNode' 'GNode' 'GUNode' 'domainRecords.txt'
	mkdir 'CnNode' 'GNode' 'GUNode'
	i=0
	ln='
'
	[ -z "$(ls ../GNode/*.ini 2>/dev/null)" -a -z "$(ls ../CnNode/*.ini 2>/dev/null)" ] && exec echo '无可用节点，请把需要启动的节点文件放到 GNode 或 CnNode 文件夹再启动'
	if [ -n "$(ls ../GNode/*.ini 2>/dev/null)" ]; then
		for file in ../GNode/*.ini; do
			unsetVConf
			. "$file"
			[ "$add" = 'www.quick-connect.top' ] && exec echo "请删除 模板文件 再启动"
			domainToIPs
			#IP无效则跳过
			if [ -z "$ips" ] || echo "$ips" | grep -Eq '^127\.|^::1$|^\[::1\]$'; then
				continue
			fi
			globalNodes="$globalNodes ${file##*/}"
			ini=`cat "$file"`
			cat >"GNode/${file##*/}" <<-EOF
			${ini/add=\'$add\'/add=\'$ips\'}
			EOF
			i=$((i + 1))
		done
	fi
	if [ -n "$(ls ../GUNode/*.ini 2>/dev/null)" ]; then
		for file in ../GUNode/*.ini; do
			unsetVConf
			. "$file"
			[ "$add" = 'www.quick-connect.top' ] && exec echo "请删除 模板文件 再启动"
			domainToIPs
			#IP无效则跳过
			if [ -z "$ips" ] || echo "$ips" | grep -Eq '^127\.|^::1$|^\[::1\]$'; then
				continue
			fi
			globalUdpNodes="$globalUdpNodes ${file##*/}"
			ini=`cat "$file"`
			cat >"GUNode/${file##*/}" <<-EOF
			${ini/add=\'$add\'/add=\'$ips\'}
			EOF
			i=$((i + 1))
		done
	fi
	if [ -n "$(ls ../CnNode/*.ini 2>/dev/null)" ]; then
		for file in ../CnNode/*.ini; do
			unsetVConf
			. "$file"
			[ "$add" = 'www.quick-connect.top' ] && exec echo "请删除 模板文件 再启动"
			domainToIPs
			#IP无效则跳过
			if [ -z "$ips" ] || echo "$ips" | grep -Eq '^127\.|^::1$|^\[::1\]$'; then
				continue
			fi
			cnNodes="$cnNodes ${file##*/}"
			ini=`cat "$file"`
			cat >"CnNode/${file##*/}" <<-EOF
			${ini/add=\'$add\'/add=\'$ips\'}
			EOF
			i=$((i + 1))
		done
	fi
	if [ -z "$globalNodes" ]; then
		if [ -z "$cnNodes" ]; then
			exec echo '无可用节点，请把需要启动的节点文件放到 GNode 或 CnNode 文件夹再启动'
		fi
		rm -rf 'GNode'
		mv -f 'CnNode' 'GNode'
		globalNodes="$cnNodes"
		unset cnNodes
		[ -n "$cnDNS" ] && globalDNS="$cnDNS"
	fi
}

initNodeFile
dnsConfigure
dnsObj="$result"
inConfigure
inObj="$result"
globalSub=0
for confFile in ${globalNodes}; do
	outConfigure "global-out${globalSub}" "GNode/${confFile}"
	if [ "$globalSub" = '0' ]; then
		outObj="$result"
	else
		outObj="$outObj, $result"
	fi
	globalSub=$((globalSub+1))
done
globalUdpSub=0
for confFile in ${globalUdpNodes}; do
	outConfigure "global-udp-out${globalUdpSub}" "GUNode/${confFile}"
	outObj="$outObj, $result"
	globalUdpSub=$((globalUdpSub+1))
done
cnSub=0
for confFile in ${cnNodes}; do
	outConfigure "cn-out${cnSub}" "CnNode/${confFile}"
	outObj="$outObj, $result"
	cnSub=$((cnSub+1))
done
routeRulesConfigure
routeRulesObj="$result"
routeBalancersConfigure
routeBalancersObj="$result"
observatoryConfigure
observatoryObj="$result"

cat >v2ray.json <<EOF
{
	"log": {
		"loglevel": "none"
	},
	"dns": ${dnsObj},
	"inbounds": [${inObj}],
	"outbounds": [${outObj}, {
		"protocol": "dns",
		"tag": "dns-out"
	}, {
		"protocol": "freedom",
		"tag": "no-proxy"
	}, {
		"protocol": "blackhole",
		"tag": "denyNetwork"
	}],
	"routing": {
		"domainStrategy": "AsIs",
		"domainMatcher": "mph",
		"rules": [$routeRulesObj],
		"balancers": [$routeBalancersObj]
	},
	"observatory": {$observatoryObj}
}
EOF
