/*
 * @Author: gj
 * @Date: 2020-06-29 15:08:18
 * @Description: 树结构图表
 * @FilePath: \chart-component\index.js
 */
/*
 * 节点数据nodetype小于10为右侧节点，大于10为左侧节点
 * data需要渲染的数据，
 * loadApiData加载数据的接口方法
 * setMsg提示信息的方法
 */
const TreeStructure = (data, loadApiData, setMsg) => {
	function formatTree(data, flag) {
		data = data.map((item) => {
			if (item.children && item.children.length > 10) {
				let obj = { name: "搜索", tag: 'search' }
				if (item.children.length <= 20) {
					obj = { name: "展开", tag: 'open' }
				}
				if (flag) {
					item.otherData = item.children.slice(10).reverse();
				} else {
					item.otherData = item.children.slice(10);
				}
				item.children.splice(10)
				item.children.push({
					name: `${obj.name}(${item.otherData.length})`,
					tag: obj.tag,
					openLent: item.otherData.length
				})
			}
			if (flag) item.children.reverse()
			return item
		})
		if (flag) data.reverse();
		return data;
	}

	let nodeItem = [], nodeIndex = 0, nodeLen = data.children.length || 0, foldCount = 0;
	let dataL = {
		...data,
		children: []
	}
	data.children = data.children.filter((item) => {
		if (item.nodetype > 10) dataL.children.push(item)
		return item.nodetype < 10
	})
	data.children = formatTree(data.children)
	dataL.children = formatTree(dataL.children, true)
	let showCircleFlag = true;
	//以上处理原始数据
	var clientHeight = (document.documentElement.clientHeight || document.body.clientHeight) - 30;
	var tree = document.getElementById('tree-chart');
	const div = document.getElementById("tips")
	tree.style.height = clientHeight + 'px';
	var rootNameLent = data.name.length * 14 + 14 * 3;
	var width2 = tree.clientWidth / 2;
	var TX = width2 - rootNameLent / 2;
	var TY = clientHeight / 2;
	var scale = 1;
	var time = '';
	d3.select("#tree-chart").selectAll("*").remove();
	// 移动, 缩放
	var zoom = d3.zoom()
		.scaleExtent([0.1, 3])
		.on('zoom', () => {
			scale = d3.event.transform.k;
			treeG.attr("transform", d3.event.transform);
		})

	var svg = d3.select('#tree-chart') //定义svg大小
		.append('svg')
		.attr("class", "linkG")
		.attr('xmlns', 'http://www.w3.org/2000/svg')
		.attr('width', tree.offsetWidth)
		.attr('height', clientHeight)
		.call(zoom)

	var treeG = svg.append('g') //添加g元素
		.attr('class', 'gbox')
		.attr('transform', 'translate(' + 0 + ',' + 0 + ') scale(1)');

	// 节点与连线的拐点间距, 改变数据看看图的变化可知道含义
	let breakPointLength = 28

	let XYFn = {
		up(x, y) {
			return { x, y: -y }
		},
		down(x, y) {
			return { x, y }
		},
		right(x, y) {
			return { x: y, y: x }
		},
		left(x, y) {
			return { x: -y, y: -x }
		}
	}
	new Chart(data, 'right')
	new Chart(dataL, 'left')
	// Chart 类
	function Chart(data, direction) {
		// x,y 赋值函数
		let xyFn = XYFn[direction] || XYFn['right']
		data.x0 = width2;
		data.y0 = TY;
		let rootNode = d3.hierarchy(data)
		renderTree(rootNode)

		function renderTree(root) {
			// 根节点
			d3.tree()
				.separation(function(a, b) { //上下相同深度节点位置间隔函数
					if (a.parent === b.parent) return 0.5
					return 0.9
				})
				.nodeSize([80, 200])(root); //左右相邻深度节点位置间隔函数

			var nodes = rootNode.descendants();
			var links = rootNode.links();

			// 子树的 g, 例如左树或右树
			treeG.select(`#subTreeRoot-${direction}`).remove();

			let subTreeG = treeG.insert('g')
				.attr('id', `subTreeRoot-${direction}`)
				.attr("transform", d => {
					if (direction === "left") return `translate(${TX+rootNameLent},${TY})`
					return `translate(${TX},${TY})`
				});

			const linkLine = subTreeG.append("g")
				.attr("fill", "none")
				.attr("stroke", "#555")
				.attr("stroke-opacity", 0.4)
				.attr("stroke-width", 1.5)
				.selectAll()
				.data(links)
				.enter()
				.append("path")
				.attr('fill', 'none')
				.attr('stroke', '#D8D8D8')
				.attr("stroke-width", 0.5)
				.attr("stroke-opacity", 0.9);

			// 节点的g
			const nodeSel = subTreeG
				.selectAll(`.node-wrap`)
				.data(nodes, function(d, i) {
					return d.id || (d.id = 'node' + ++i);
				})
				.enter()
				.append('g')
				.attr("stroke-linejoin", "round")
				.attr("stroke-width", 3)
				.style('cursor', "pointer")
				.on('click', toggleListener)

			function toggleListener(d) {
				click(d, data, direction, this);
			}

			// 文本
			var nameText = nodeSel.append("text")
				.attr("dy", "0.31em")
				.attr("x", d => {
					if (d.depth === 0) return 21
					return direction === "right" ? 11 : 31
				})
				.style('font-size', 14)
				.attr("fill", "rgb(51, 51, 51)")
				.clone(true);

			nameText.append("tspan")
				.attr("fill", d => colorData(d, "#128bed", "#c6a351", "#999", "#fff"))
				.text(d => d.data.name);

			nameText.append("tspan")
				.attr("fill", "#999")
				.text(d => appendT(d, " ["));
			nameText.append("tspan")
				.attr("fill", d => {
					if (d.data.desc) return "#efae00"
					return "#999"
				})
				.text(d => appendT(d));
			nameText.append("tspan")
				.attr("fill", "#999")
				.text(d => appendT(d, "]"));

			//获取文本text的宽度 计算出rect的宽
			nameText.each(function(d) {
				d.textRectWidth = this.getBBox().width + 14 * 3
			})

			//节点 rect
			nodeSel.append("rect")
				.attr("y", -17)
				.attr("width", (d) => {
					return d.data.type != '3' ? d.textRectWidth : d.textRectWidth - 16
				})
				.attr("height", 34)
				.attr("rx", 2) //圆角
				.style("stroke", "#d8d8d8")
				.style('stroke-width', d => {
					return (d.depth % 2 === 0 && d.depth !== 0) ? d.data.tag ? 0 : 0.5 : 0;
				})
				.style('fill', d => colorData(d, "#EAF2FF", "#fcf2d6", "#F1F1F1", "#128bed", "rect"))
				.lower();


			// 三角形箭头
			nodeSel.append("g")
				.attr("transform", d => {
					if (direction === "left" && d.textRectWidth) return `translate(${d.textRectWidth + breakPointLength/2-4},0)`
					return `translate(${(-breakPointLength / 2) - 4},0)`
				})
				.raise()
				.append("path")
				.attr('fill', '#128BED')
				.attr("style", "stroke-width:0")
				.attr("d", d => Arrow(d, direction));

			// 展开收缩圆圈
			var circleG = nodeSel.append("g")
				.attr("transform", d => {
					if (direction === "left") return `translate(16,0)`
					return `translate(${d.textRectWidth-16},0)`
				});
			circleG.append("circle")
				.attr("fill", d => colorData(d, "#EAF2FF", "#fcf2d6", "#F1F1F1", "#128bed", "rect"))
				.attr("stroke", d => circleColor(d))
				.style('stroke-width', "1")
				.attr('r', function(d) {
					if (d.data.type == 1) {
						if (d.data.id == '') {
							showCircleFlag = false;
						} else {
							showCircleFlag = true;
						}
					} else if (d.data.type == 3) {
						showCircleFlag = false;
					} else {
						showCircleFlag = true;
					}
					return (d.depth === 0 || showCircleFlag == false) ? 0 : 6;
				});

			circleG.append("text")
				.attr("y", 4.5)
				.attr("text-anchor", "middle")
				.attr("cursor", "pointer")
				.style("fill", d => circleColor(d))
				.style('font-size', '14px')
				.style('cursor', "pointer")
				.text(d => PlusSymbol(d, data, direction));

			textMaxLent(rootNode);

			// 修复 y
			rootNode.each(d => {
				Object.assign(d, xyFn(d.x, rootNode['distance' + d.depth]))
			});

			nodeSel.attr("transform", d => {
				let x = d.x;
				if (direction === 'left') x = d.x - d.textRectWidth //左边树位置是负数
				return `translate(${x},${d.y})`
			});

			// 更新线
			linkLine.attr("d", d => {
				let { target, source } = d;
				let addiX = target.x - breakPointLength;
				if (direction === 'left') addiX = target.x + breakPointLength
				let line = d3.line()
					.x(d => d.x)
					.y(d => d.y)
					.curve(d3.curveStepAfter);
				return line([source, {
						x: addiX,
						y: target.y
					},
					target
				])
			})
		}
	}

	// 处理相同方向的相同深度最长的文本
	function textMaxLent(rootNode) {
		rootNode.each(d => {
			let dDepth = d.depth;
			if (d.depth !== 0) {
				if (d.parent.hasCalcMaxLength) {
					return
				}
				// 同一个parent的节点最长段
				let curtGroupMaxDistence = maxLenght(d);
				// 当前深度已知的最长距离
				let oldDepthMaxDistance = rootNode['distance' + dDepth];
				let oldDepthMaxLength = rootNode['maxLength' + dDepth];
				// 当前节点到根节点的距离
				let distance = rootNode['distance' + (dDepth - 1)] + rootNode['maxLength' + (dDepth - 1)];
				// 现在此深度的最长距离
				let newDepthMaxDistance = Math.max(distance, oldDepthMaxDistance || 0)
				// 更新此深度的最长距离
				rootNode['distance' + dDepth] = newDepthMaxDistance;
				// 更新此深度最长段的长度
				rootNode['maxLength' + dDepth] = Math.max(curtGroupMaxDistence, oldDepthMaxLength || 0);
				d.parent.hasCalcMaxLength = true;
			} else {
				d.distance0 = 0
				d.maxLength0 = d.textRectWidth + breakPointLength * 2
			}
			function maxLenght(d) {
				return Math.max.apply(null, d.parent.children.map(d => d.textRectWidth + breakPointLength * 2))
			}
		})
	}

	// rect text 圆bg颜色
	function colorData(d, blue, yellow, tag, root, flag) {
		let color = "";
		if (d.depth % 2 !== 0) {
			switch (d.data.nodetype) {
				case '01':
				case '04':
				case '05':
				case '12':
				case '13':
				case '22':
					color = blue
					break;
				default:
					color = yellow
			}
		} else {
			if (flag === "rect") {
				if (d.data.type === "2") {
					color = "#F3F9FD"
				} else {
					color = "#fff"
				}
			}
		}
		if (d.data.tag) color = tag
		if (d.depth === 0) color = root
		return color
	}

	// text 文本显示文案
	function appendT(d, tex) {
		if (d.depth % 2 === 0 && d.depth !== 0) {
			let text = '';
			text = d.data.desc || '';
			if (tex) text = tex;
			if (d.data.tag) text = ""
			return text
		}
	}

	// 展开收缩圆圈 字体 颜色
	function circleColor(d) {
		let color = "#666666"
		if (d.children && d.children.length) {
			color = "#999999"
		}
		if (d.plusStat) {
			if (d.plusStat === "+") {
				color = "#666666"
			} else {
				color = "#999999"
			}
		}
		return color
	}

	// 箭头方向
	function Arrow(d, direction) {
		if (d.depth % 2 === 0 || d.depth === 0) {
			return ""
		} else {
			switch (d.data.nodetype) {
				case '01':
				case '02':
				case '03':
				case '04':
				case '05':
				case '15':
				case '16':
					if (direction === 'left') return "M0,0L0,3L9,0L0,-3Z" // 反方向
					return "M0,0L9,-3L9,3Z" // 👈
					break;
				default:
					if (direction === 'left') return "M0,0L9,-3L9,3Z" // 反方向
					return "M0,0L0,3L9,0L0,-3Z" // 👉
			}
		}
	}

	// 展开收缩 功能
	function click(d, originData, direction, ctx, sourceType) {
		if(sourceType == 'fold') {
			foldCount = 1;
		} else {
			foldCount = 0;
		}
		if (d.data.type === '3') return;
		let rawData = d.data;
		if (d.depth === 0) return
		if (d.plusStat === '+') {
			d.plusStat = '-'
		} else {
			d.plusStat = '+'
		}
		if (rawData.tag) {
			switch (rawData.tag) {
				case 'open':
					rawData.name = '收起'
					rawData.tag = 'close'
					const data = d.parent.data;
					let tagsData = [];
					if (direction === 'left') {
						tagsData = data.children.splice(0, 1);
						d.parent.data.children = tagsData.concat(data.otherData).concat(data.children)
					} else {
						tagsData = data.children.splice(10);
						d.parent.data.children = data.children.concat(data.otherData).concat(tagsData)
					}
					break;
				case 'close':
					if (direction === 'left') {
						d.parent.data.children.reverse().splice(10)
					} else {
						d.parent.data.children.splice(10);
					}
					d.parent.data.children.push({
						name: `展开(${rawData.openLent})`,
						tag: 'open',
						openLent: rawData.openLent
					})
					if (direction === 'left') d.parent.data.children.reverse()
					d.plusStat = '+'
					break;
				case 'search':
					const mask = document.getElementById("mask")
					const ul = document.getElementById("ul")
					let html = '';
					mask.style.display = "block";
					d.parent.data.otherData.map((it) => {
						let str = it;
						delete str.ID;
						html += `<li
							style="
								font-size: 12px;
								border-bottom: 1px solid #EEEEEE;
								cursor: pointer;
								box-sizing: border-box;
								height: 38px;
								line-height: 38px;
								-webkit-tap-highlight-color: #fff;
								overflow: hidden;
								text-overflow: ellipsis;
								white-space: nowrap;
							"
							title="点击添加:${it.name}"
							val=${JSON.stringify(str)}
						>${it.name}</li>`
					})
					ul.innerHTML = html;
					ul.onclick = function(e) {
						if (e.target === this) return;
						const list = JSON.parse(e.target.getAttribute("val"))
						d.parent.data.otherData = d.parent.data.otherData.filter((it) => it.name !== list.name)

						if (direction === 'left') {
							d.parent.data.children.splice(1, 0, list);
						} else {
							d.parent.data.children.splice(d.parent.data.children.length - 1, 0, list);
						}

						if (d.parent.data.otherData.length === 0) {
							if (direction === 'left') {
								d.parent.data.children.shift()
							} else {
								d.parent.data.children.pop()
							}
							mask.style.display = "none";
						} else {
							d.data.openLent = d.data.openLent - 1;
							d.data.NAME = `搜索${d.data.openLent}`
						}

						ul.removeChild(e.target);
						Chart(originData, direction); //重新渲染数据
					}
					break;
				default:
					rawData.children = undefined
			}
			Chart(originData, direction); //重新渲染数据
			return
		}
		if (rawData.children) { // 收缩
			rawData._children = rawData.children; // 展开
			rawData.children = null;
		} else {
			rawData.children = rawData._children;
			rawData._children = null;
			let paramTypeOne, paramTypeTwo;
			if (rawData.children === undefined) {
				switch (d.data.type) {
					case '1':
						if (d.data.id && d.data.id != '') {
							paramTypeOne = {
								key: d.data.id,
								formatFlag: 1
							}
						} else if (d.data.relatedId && d.data.relatedId != '') {
							paramTypeOne = {
								key: d.data.relatedId,
								formatFlag: 1
							}
						} else {
							return;
						}
						// 接口加载数据
						loadApiData(paramTypeOne.key, ({
							data,code
						}) => {
							if (code == 200) {
								let childrenData = data.children || [];
								rawData.children = formatTree(childrenData, direction === 'left');
							} else {
								rawData.children = undefined;
								setMsg('暂无下探数据'); // 提示信息
							}
							Chart(originData, direction); //重新渲染数据
							return;
						},(err) => {
							rawData.children = undefined;
							setMsg('暂无下探数据'); // 提示信息
							Chart(originData, direction); //重新渲染数据
							return;
						})
						break;
					case '2':
						if (d.data.relatedId && d.data.relatedId != '') {
							paramTypeTwo = {
								name: d.data.name,
								id: d.data.relatedId,
								formatFlag: 1
							}
						} else {
							paramTypeTwo = {
								name: d.data.name,
								id: d.parent.parent.data.id,
								formatFlag: 1
							}
						}
						//接口获取数据
						loadApiData(paramTypeTwo, ({
							data,code
						}) => {
							if (code == 200) {
								let childrenData = data.children || [];
								rawData.children = formatTree(childrenData, direction === 'left');
							} else {
								rawData.children = undefined;
								setMsg('暂无下探数据'); // 提示信息
							}
							Chart(originData, direction); //重新渲染数据
							return;
						},(err) => {
							rawData.children = undefined;
							setMsg('暂无下探数据'); // 提示信息
							Chart(originData, direction); //重新渲染数据
							return;
						})
						break;
					case '3':
						break;
				}
			}
		}
		Chart(originData, direction); //重新渲染数据
	}

	// 展开收缩 符号+-
	function PlusSymbol(d, data, direction) {
		let initStat = '+'
		if ((d.children && d.children.length) || d.data.tag === 'close') {
			initStat = '-'
		}
		if (d.depth === 0) initStat = ''
		if (d.data.type == 1 && d.data.id == '') {
			initStat = ''
		}
		if (d.data.type == 3) {
			initStat = ''
		}
		const len = nodeItem.length;
 		if (nodeIndex < 0 && (d.plusStat == '-' || initStat == '-')) {
			nodeItem.forEach((v, indx) => {
				if (v.d.id == d.id && v.d.data.name == d.data.name) {
					nodeItem[indx] = {d, data, direction}
				}
			})
 		}
		if (initStat == '-' && len < nodeLen) {
			nodeItem.push({d, data, direction});
		}
		return d.plusStat ? d.plusStat : initStat
	}
	const mask = document.getElementById("mask");
	mask.onclick = function(e) {
	    e.stopPropagation();
	    if (e.target != this) return;
	    else {
	      mask.style.display = "none"
	    }
	  }
	//放大缩小
	function maoScale(type) {
		if (type == 1) {
			scale += 0.2;
		} else if (type == 2) {
			scale -= 0.2;
		}
		if (scale < 0.1) {
			scale = 0.1;
		}
		if (scale > 3) {
			scale = 3;
		}
		zoom.scaleTo(svg, scale);
	}
	//刷新
	function maoRefresh() {
		treeG.attr('transform', 'translate(' + 0 + ',' + 0 + ') scale(1)');
		// draw();
	}
	//全屏
	function changeScreen(dom) {
		if (!isFullScreen()) {
			const screenArea = document.getElementById("screenArea")
			dom.innerHTML = '<span class="screened"></span>退出'
			launchFullScreen(screenArea);
		} else {
			dom.innerHTML = '<span class="screen"></span>全屏'
			exitFullScreen();
		}
	}

	//全屏和退出
	function isFullScreen() {
		if (document.fullscreen) {
			return true;
		} else if (document.mozFullScreen) {
			return true;
		} else if (document.webkitIsFullScreen) {
			return true;
		} else if (document.msFullscreenElement) {
			return true;
		} else {
			return false;
		}
	}

	function launchFullScreen(el) {
		if (el.requestFullscreen) {
			el.requestFullscreen();
		} else if (el.mozRequestFullScreen) {
			el.mozRequestFullScreen();
		} else if (el.webkitRequestFullscreen) {
			el.webkitRequestFullscreen();
		} else if (el.msRequestFullscreen) {
			el.msRequestFullscreen();
		}
	}

	function exitFullScreen() {
		if (document.exitFullscreen) {
			document.exitFullscreen();
		} else if (document.mozCancelFullScreen) {
			document.mozCancelFullScreen();
		} else if (document.msExitFullscreen) {
			document.msExitFullscreen();
		} else if (document.webkitCancelFullScreen) {
			document.webkitCancelFullScreen();
		}
	}

	//图片下载
	function saveImg() {
		var _svgWidth = d3.select('svg').attr('width');
		var _svgHeight = d3.select('svg').attr('height');
		var svgXml = document.getElementById("tree-chart").innerHTML;
		var image = new Image();
		image.src = 'data:image/svg+xml;base64,' + window.btoa(unescape(encodeURIComponent(svgXml)));

		setTimeout(function() {
			var canvas = document.createElement('canvas'); //准备空画布
			canvas.width = _svgWidth * 2;
			canvas.height = _svgHeight * 2;
			var context = canvas.getContext('2d'); //取得画布的2d绘图上下文
			context.fillStyle = "#fff";
			context.fillRect(0, 0, canvas.width, canvas.height);
			context.drawImage(image, 0, 0);
			//前台下载图片
			var qual = 1;
			if (canvas.width > 3000) {
				qual = 0.8;
			} else if (canvas.width > 8000) {
				qual = 0.5;
			}
			var a = document.createElement("a");
			a.href = canvas.toDataURL('image/jpeg', qual);
			a.download = 'chart.jpeg';
			a.click();
		}, 100);
	}
	// 保存图片
	d3V3.select("#save").on('click', () => {
		saveImg();
	})
	// 放大
	d3V3.select("#big").on('click', () => {
		maoScale(1);
	})
	// 缩小
	d3V3.select("#small").on('click', () => {
		maoScale(2);
	})
	// 一键收起
	d3V3.select("#fold").on('click', () => {
		if(foldCount == 0) {
			nodeItem.forEach((v, index) => {
				if (v.d.plusStat != '+') {
					nodeIndex = index;
					click(v.d, v.data, v.direction, '', 'fold');
				}
			});
		}
	})
}