import beziercurve from '../bezier-curve'
import {
	calcLength,
	getPointOnQuadraticBezier,
	getTensionPointsClosed,
	expandPoints
} from '../bezier-curve/core/path2d'

import {
	deepClone,
	eliminateBlur,
	checkPointIsInCircle,
	getTwoPointDistance,
	checkPointIsInSector,
	getRegularPolygonPoints,
	checkPointIsInPolygon,
	checkPointIsNearPolyline,
	checkPointIsInRect
} from '../plugin/util'

import {
	drawPolylinePath,
	drawBezierCurvePath
} from '../plugin/canvas'

const {
	polylineToBezierCurve,
	bezierCurveToPolyline
} = beziercurve

export const circle = {
	shape: {
		rx: 0,
		ry: 0,
		r: 0
	},

	validator({
		shape
	}) {
		const {
			rx,
			ry,
			r
		} = shape

		if (typeof rx !== 'number' || typeof ry !== 'number' || typeof r !== 'number') {
			console.error('Circle shape configuration is abnormal!')

			return false
		}

		return true
	},

	draw({
		ctx
	}, {
		shape
	}) {
		ctx.beginPath()

		const {
			rx,
			ry,
			r
		} = shape

		ctx.arc(rx, ry, r > 0 ? r : 0.01, 0, Math.PI * 2)

		ctx.fill()
		ctx.stroke()

		ctx.closePath()

		// ctx.draw()
	},

	hoverCheck(position, {
		shape
	}) {
		const {
			rx,
			ry,
			r
		} = shape

		return checkPointIsInCircle(position, rx, ry, r)
	},

	setGraphCenter(e, {
		shape,
		style
	}) {
		const {
			rx,
			ry
		} = shape

		style.graphCenter = [rx, ry]
	},

	move({
		movementX,
		movementY
	}, {
		shape
	}) {
		this.attr('shape', {
			rx: shape.rx + movementX,
			ry: shape.ry + movementY
		})
	}
}

export const ellipse = {
	shape: {
		rx: 0,
		ry: 0,
		hr: 0,
		vr: 0
	},

	validator({
		shape
	}) {
		const {
			rx,
			ry,
			hr,
			vr
		} = shape

		if (typeof rx !== 'number' || typeof ry !== 'number' || typeof hr !== 'number' || typeof vr !== 'number') {
			console.error('Ellipse shape configuration is abnormal!')

			return false
		}

		return true
	},

	draw({
		ctx
	}, {
		shape
	}) {
		ctx.beginPath()

		let {
			rx,
			ry,
			hr,
			vr
		} = shape

		ctx.ellipse(rx, ry, hr > 0 ? hr : 0.01, vr > 0 ? vr : 0.01, 0, 0, Math.PI * 2)

		ctx.fill()
		ctx.stroke()

		ctx.closePath()
		// ctx.draw()
	},

	hoverCheck(position, {
		shape
	}) {
		const {
			rx,
			ry,
			hr,
			vr
		} = shape

		const a = Math.max(hr, vr)
		const b = Math.min(hr, vr)

		const c = Math.sqrt(a * a - b * b)

		const leftFocusPoint = [rx - c, ry]
		const rightFocusPoint = [rx + c, ry]

		const distance = getTwoPointDistance(position, leftFocusPoint) + getTwoPointDistance(position,
			rightFocusPoint)

		return distance <= 2 * a
	},

	setGraphCenter(e, {
		shape,
		style
	}) {
		const {
			rx,
			ry
		} = shape

		style.graphCenter = [rx, ry]
	},

	move({
		movementX,
		movementY
	}, {
		shape
	}) {
		this.attr('shape', {
			rx: shape.rx + movementX,
			ry: shape.ry + movementY
		})
	}
}

export const rect = {
	shape: {
		x: 0,
		y: 0,
		w: 0,
		h: 0
	},

	validator({
		shape
	}) {
		const {
			x,
			y,
			w,
			h
		} = shape

		if (typeof x !== 'number' || typeof y !== 'number' || typeof w !== 'number' || typeof h !== 'number') {
			console.error('Rect shape configuration is abnormal!')

			return false
		}

		return true
	},

	draw({
		ctx
	}, {
		shape
	}) {
		ctx.beginPath()

		let {
			x,
			y,
			w,
			h
		} = shape

		ctx.rect(x, y, w, h)

		ctx.fill()
		ctx.stroke()

		ctx.closePath()
		// ctx.draw()


	},

	hoverCheck(position, {
		shape
	}) {
		let {
			x,
			y,
			w,
			h
		} = shape

		return checkPointIsInRect(position, x, y, w, h)
	},

	setGraphCenter(e, {
		shape,
		style
	}) {
		const {
			x,
			y,
			w,
			h
		} = shape

		style.graphCenter = [x + w / 2, y + h / 2]
	},

	move({
		movementX,
		movementY
	}, {
		shape
	}) {
		this.attr('shape', {
			x: shape.x + movementX,
			y: shape.y + movementY
		})
	}
}

export const rectRound = {
	shape: {
		x: 0,
		y: 0,
		w: 0,
		h: 0,
		radius:[0,0,0,0],
		close:false,//true时为填充,否则描边。
	},

	validator({
		shape
	}) {
		const {
			x,
			y,
			w,
			h
		} = shape

		if (typeof x !== 'number' || typeof y !== 'number' || typeof w !== 'number' || typeof h !== 'number') {
			console.error('Rect shape configuration is abnormal!')

			return false
		}

		return true
	},

	draw({
		ctx
	}, {
		shape
	}) {
		ctx.beginPath()

		let {
			x,
			y,
			w,
			h,
			radius,
			close
		} = shape
		
		if(typeof radius ==='number'){
			radius = [radius,radius,radius,radius]
		}
			const cxt = ctx;
		 let width = w,height = h;
		  var  r0 = radius[0],r1 = radius[1],r2 = radius[2],r3 = radius[3];
		  cxt.beginPath();
		  //从右下角顺时针绘制,弧度从0到1/2PI  
		  cxt.arc(width - r0+x, height - r0+y, r0, 0, Math.PI / 2);
	
		  //矩形下边线  
		  cxt.lineTo(r1+x, height+y);
	
		  //左下角圆弧,弧度从1/2PI到PI  
		  cxt.arc(r1+x, height - r1+y, r1, Math.PI / 2, Math.PI);
	
		  //矩形左边线  
		  cxt.lineTo(x, r2+y);
	
		  //左上角圆弧,弧度从PI到3/2PI  
		  cxt.arc(r2+x, r2+y, r2, Math.PI, Math.PI * 3 / 2);
	
		  //上边线  
		  cxt.lineTo(width - r3+x, y);
	
		  //右上角圆弧  
		  cxt.arc(width - r3+x, r3+y, r3, Math.PI * 3 / 2, Math.PI * 2);
	
		  //右边线  
		  cxt.lineTo(width+x, height+y-r0);
		  ctx.stroke()
		  if(close){
			  ctx.fill()
		  }

		cxt.closePath();


	},

	hoverCheck(position, {
		shape,style,
	}) {
		let {
			x,
			y,
			w,
			h
		} = shape
		let {lineWidth} = style;
		return checkPointIsInRect(position, x-lineWidth, y-lineWidth, w+lineWidth, h+lineWidth)
	},

	setGraphCenter(e, {
		shape,
		style
	}) {
		const {
			x,
			y,
			w,
			h
		} = shape

		style.graphCenter = [x + w / 2, y + h / 2]
	},

	move({
		movementX,
		movementY
	}, {
		shape
	}) {
		this.attr('shape', {
			x: shape.x + movementX,
			y: shape.y + movementY
		})
	}
}


export const ring = {
	shape: {
		rx: 0,
		ry: 0,
		r: 0
	},

	validator({
		shape
	}) {
		const {
			rx,
			ry,
			r
		} = shape

		if (typeof rx !== 'number' || typeof ry !== 'number' || typeof r !== 'number') {
			console.error('Ring shape configuration is abnormal!')

			return false
		}

		return true
	},

	draw({
		ctx
	}, {
		shape
	}) {
		ctx.beginPath()

		const {
			rx,
			ry,
			r
		} = shape

		ctx.arc(rx, ry, r > 0 ? r : 0.01, 0, Math.PI * 2)

		ctx.stroke()

		ctx.closePath()
		// ctx.draw()
	},

	hoverCheck(position, {
		shape,
		style
	}) {
		const {
			rx,
			ry,
			r
		} = shape

		const {
			lineWidth
		} = style

		const halfLineWidth = lineWidth / 2

		const minDistance = r - halfLineWidth
		const maxDistance = r + halfLineWidth

		const distance = getTwoPointDistance(position, [rx, ry])

		return (distance >= minDistance && distance <= maxDistance)
	},

	setGraphCenter(e, {
		shape,
		style
	}) {
		const {
			rx,
			ry
		} = shape

		style.graphCenter = [rx, ry]
	},

	move({
		movementX,
		movementY
	}, {
		shape
	}) {
		this.attr('shape', {
			rx: shape.rx + movementX,
			ry: shape.ry + movementY
		})
	}
}

export const arc = {
	shape: {
		rx: 0,
		ry: 0,
		r: 0,
		startAngle: 0,
		endAngle: 0,
		clockWise: true
	},

	validator({
		shape
	}) {
		const keys = ['rx', 'ry', 'r', 'startAngle', 'endAngle']

		if (keys.find(key => typeof shape[key] !== 'number')) {
			console.error('Arc shape configuration is abnormal!')

			return false
		}

		return true
	},

	draw({
		ctx
	}, {
		shape
	}) {
		ctx.beginPath()

		const {
			rx,
			ry,
			r,
			startAngle,
			endAngle,
			clockWise
		} = shape

		ctx.arc(rx, ry, r > 0 ? r : 0.001, startAngle, endAngle, !clockWise)

		ctx.stroke()

		ctx.closePath()
		// ctx.draw(true)
	},

	hoverCheck(position, {
		shape,
		style
	}) {
		const {
			rx,
			ry,
			r,
			startAngle,
			endAngle,
			clockWise
		} = shape

		const {
			lineWidth
		} = style

		const halfLineWidth = lineWidth / 2

		const insideRadius = r - halfLineWidth
		const outsideRadius = r + halfLineWidth

		return !checkPointIsInSector(position, rx, ry, insideRadius, startAngle, endAngle, clockWise) &&
			checkPointIsInSector(position, rx, ry, outsideRadius, startAngle, endAngle, clockWise)
	},

	setGraphCenter(e, {
		shape,
		style
	}) {
		const {
			rx,
			ry
		} = shape

		style.graphCenter = [rx, ry]
	},

	move({
		movementX,
		movementY
	}, {
		shape
	}) {
		this.attr('shape', {
			rx: shape.rx + movementX,
			ry: shape.ry + movementY
		})
	}
}

export const sector = {
	shape: {
		rx: 0,
		ry: 0,
		r: 0,
		startAngle: 0,
		endAngle: 0,
		clockWise: true
	},

	validator({
		shape
	}) {
		const keys = ['rx', 'ry', 'r', 'startAngle', 'endAngle']

		if (keys.find(key => typeof shape[key] !== 'number')) {
			console.error('Sector shape configuration is abnormal!')

			return false
		}

		return true
	},

	draw({
		ctx
	}, {
		shape
	}) {
		ctx.beginPath()

		const {
			rx,
			ry,
			r,
			startAngle,
			endAngle,
			clockWise
		} = shape

		ctx.arc(rx, ry, r > 0 ? r : 0.01, startAngle, endAngle, !clockWise)

		ctx.lineTo(rx, ry)

		ctx.closePath()

		ctx.stroke()
		ctx.fill()
		// ctx.draw()
	},

	hoverCheck(position, {
		shape
	}) {
		const {
			rx,
			ry,
			r,
			startAngle,
			endAngle,
			clockWise
		} = shape

		return checkPointIsInSector(position, rx, ry, r, startAngle, endAngle, clockWise)
	},

	setGraphCenter(e, {
		shape,
		style
	}) {
		const {
			rx,
			ry
		} = shape

		style.graphCenter = [rx, ry]
	},

	move({
		movementX,
		movementY
	}, {
		shape
	}) {
		const {
			rx,
			ry
		} = shape

		this.attr('shape', {
			rx: rx + movementX,
			ry: ry + movementY
		})
	}
}

export const regPolygon = {
	shape: {
		rx: 0,
		ry: 0,
		r: 0,
		side: 0
	},

	validator({
		shape
	}) {
		const {
			side
		} = shape

		const keys = ['rx', 'ry', 'r', 'side']

		if (keys.find(key => typeof shape[key] !== 'number')) {
			console.error('RegPolygon shape configuration is abnormal!')

			return false
		}

		if (side < 3) {
			console.error('RegPolygon at least trigon!')

			return false
		}

		return true
	},

	draw({
		ctx
	}, {
		shape,
		cache
	}) {
		ctx.beginPath()

		const {
			rx,
			ry,
			r,
			side
		} = shape

		if (!cache.points || cache.rx !== rx || cache.ry !== ry || cache.r !== r || cache.side !== side) {
			const points = getRegularPolygonPoints(rx, ry, r, side)

			Object.assign(cache, {
				points,
				rx,
				ry,
				r,
				side
			})
		}

		const {
			points
		} = cache

		drawPolylinePath(ctx, points)

		ctx.closePath()

		ctx.stroke()
		ctx.fill()
		// ctx.draw()
	},

	hoverCheck(position, {
		cache
	}) {
		let {
			points
		} = cache

		return checkPointIsInPolygon(position, points)
	},

	setGraphCenter(e, {
		shape,
		style
	}) {
		const {
			rx,
			ry
		} = shape

		style.graphCenter = [rx, ry]
	},

	move({
		movementX,
		movementY
	}, {
		shape,
		cache
	}) {
		const {
			rx,
			ry
		} = shape

		cache.rx += movementX
		cache.ry += movementY

		this.attr('shape', {
			rx: rx + movementX,
			ry: ry + movementY
		})

		cache.points = cache.points.map(([x, y]) => [x + movementX, y + movementY])
	}
}

export const polyline = {
	shape: {
		points: [],
		close: false
	},

	validator({
		shape
	}) {
		const {
			points
		} = shape

		if (!(points instanceof Array)) {
			console.error('Polyline points should be an array!')

			return false
		}

		return true
	},

	draw({
		ctx
	}, {
		shape,
		style: {
			lineWidth
		}
	}) {
		ctx.beginPath()

		let {
			points,
			close
		} = shape

		if (lineWidth === 1) points = eliminateBlur(points)

		drawPolylinePath(ctx, points)

		if (close) {
			ctx.closePath()

			ctx.fill()
			ctx.stroke()
		} else {
			ctx.stroke()

		}
		// ctx.draw()
	},

	hoverCheck(position, {
		shape,
		style
	}) {
		const {
			points,
			close
		} = shape

		const {
			lineWidth
		} = style

		if (close) {
			return checkPointIsInPolygon(position, points)
		} else {
			return checkPointIsNearPolyline(position, points, lineWidth)
		}
	},

	setGraphCenter(e, {
		shape,
		style
	}) {
		const {
			points
		} = shape

		style.graphCenter = points[0]
	},

	move({
		movementX,
		movementY
	}, {
		shape
	}) {
		const {
			points
		} = shape

		const moveAfterPoints = points.map(([x, y]) => [x + movementX, y + movementY])

		this.attr('shape', {
			points: moveAfterPoints
		})
	}
}

export const smoothline = {
	shape: {
		points: [],
		close: false
	},

	validator({
		shape
	}) {
		const {
			points
		} = shape

		if (!(points instanceof Array)) {
			console.error('Smoothline points should be an array!')

			return false
		}

		return true
	},

	draw({
		ctx
	}, {
		shape,
		cache
	}) {
		const {
			points,
			close
		} = shape

		if (!cache.points || cache.points.toString() !== points.toString()) {
			const bezierCurve = polylineToBezierCurve(points, close)
			const hoverPoints = bezierCurveToPolyline(bezierCurve)

			Object.assign(cache, {
				points: deepClone(points, true),
				bezierCurve,
				hoverPoints
			})
		}

		const {
			bezierCurve
		} = cache

		ctx.beginPath()

		drawBezierCurvePath(ctx, bezierCurve.slice(1), bezierCurve[0])

		if (close) {
			ctx.closePath()

			ctx.fill()
			ctx.stroke()
		} else {
			ctx.stroke()
		}
		// ctx.draw()
	},

	hoverCheck(position, {
		cache,
		shape,
		style
	}) {
		const {
			hoverPoints
		} = cache

		const {
			close
		} = shape

		const {
			lineWidth
		} = style

		if (close) {
			return checkPointIsInPolygon(position, hoverPoints)
		} else {
			return checkPointIsNearPolyline(position, hoverPoints, lineWidth)
		}
	},

	setGraphCenter(e, {
		shape,
		style
	}) {
		const {
			points
		} = shape

		style.graphCenter = points[0]
	},

	move({
		movementX,
		movementY
	}, {
		shape,
		cache
	}) {
		const {
			points
		} = shape

		const moveAfterPoints = points.map(([x, y]) => [x + movementX, y + movementY])

		cache.points = moveAfterPoints

		const [fx, fy] = cache.bezierCurve[0]
		const curves = cache.bezierCurve.slice(1)

		cache.bezierCurve = [
			[fx + movementX, fy + movementY],
			...curves.map(curve => curve.map(([x, y]) => [x + movementX, y + movementY]))
		]

		cache.hoverPoints = cache.hoverPoints.map(([x, y]) => [x + movementX, y + movementY])

		this.attr('shape', {
			points: moveAfterPoints
		})
	}
}

export const bezierCurve = {
	shape: {
		points: [],
		close: false
	},

	validator({
		shape
	}) {
		const {
			points
		} = shape

		if (!(points instanceof Array)) {
			console.error('BezierCurve points should be an array!')

			return false
		}

		return true
	},

	draw({
		ctx
	}, {
		shape,
		cache
	}) {
		let {
			points,
			close
		} = shape

		if (!cache.points || cache.points.toString() !== points.toString()) {
			const hoverPoints = bezierCurveToPolyline(points, 20)

			Object.assign(cache, {
				points: deepClone(points, true),
				hoverPoints
			})
		}

		ctx.beginPath()

		drawBezierCurvePath(ctx, points.slice(1), points[0])

		if (close) {
			ctx.closePath()

			ctx.fill()
			ctx.stroke()
		} else {
			ctx.stroke()
		}
		// ctx.draw()
	},

	hoverCheck(position, {
		cache,
		shape,
		style
	}) {
		const {
			hoverPoints
		} = cache

		const {
			close
		} = shape

		const {
			lineWidth
		} = style

		if (close) {
			return checkPointIsInPolygon(position, hoverPoints)
		} else {
			return checkPointIsNearPolyline(position, hoverPoints, lineWidth)
		}
	},

	setGraphCenter(e, {
		shape,
		style
	}) {
		const {
			points
		} = shape

		style.graphCenter = points[0]
	},

	move({
		movementX,
		movementY
	}, {
		shape,
		cache
	}) {
		const {
			points
		} = shape

		const [fx, fy] = points[0]
		const curves = points.slice(1)

		const bezierCurve = [
			[fx + movementX, fy + movementY],
			...curves.map(curve => curve.map(([x, y]) => [x + movementX, y + movementY]))
		]

		cache.points = bezierCurve
		cache.hoverPoints = cache.hoverPoints.map(([x, y]) => [x + movementX, y + movementY])

		this.attr('shape', {
			points: bezierCurve
		})
	}
}

export const text = {
	shape: {
		content: '',
		position: [],
		x: 0,
		y: 0,
		rowGap: 0
	},

	validator({
		shape,
		style
	}, ctx) {
		const {
			content,
			position,
			rowGap
		} = shape

		if (typeof content !== 'string') {
			console.error('Text content should be a string!')

			return false
		}

		if (!(position instanceof Array)) {
			console.error('Text position should be an array!')

			return false
		}

		if (typeof rowGap !== 'number') {
			console.error('Text rowGap should be a number!')

			return false
		}
		this.textWidth = 0
		this.textHeight = 0

		return true
	},
	measureSize(ctx, text) {
		var _context = ctx,
			fontSize = this.fontSize(),
			metrics;
		_context.save();
		_context.font = ctx.font;
		metrics = _context.measureText(text + "");
		_context.restore();
		return {
			width: metrics.width,
			height: fontSize,
		};
	},
	draw({
		ctx,
		area
	}, {
		shape,
		style
	}) {

		let {
			content,
			position,
			maxWidth,
			rowGap,

		} = shape
		let {
			lineWidth
		} = style;
		const {
			textBaseline,
			font,

		} = ctx
		const [w, h] = area
		const fontSize = parseInt(font.replace(/\D/g, ''))

		let [x, y] = position

		content = content.split('\n')
		const rowNum = content.length

		const lineHeight = fontSize + rowGap
		const allHeight = rowNum * lineHeight - rowGap

		let offset = 0

		if (textBaseline === 'middle') {
			offset = allHeight / 2
			y += fontSize / 2
		}

		if (textBaseline === 'bottom') {
			offset = allHeight
			y += fontSize
		}

		position = new Array(rowNum).fill(0).map((foo, i) => [x, y + i * lineHeight - offset])

		if (typeof maxWidth == 'undefined' || !maxWidth) maxWidth = w
		// #ifdef H5
		maxWidth = maxWidth * ctx.dpr
		
		// #endif
		let maxwi = []
		ctx.beginPath()
		content.forEach((text, i) => {
			ctx.fillText(text, ...position[i], maxWidth)
			if (lineWidth > 0) {
				ctx.strokeText(text, ...position[i], maxWidth)
			}

			maxwi.push(ctx.measureText(text + "").width)

		})

		ctx.closePath()
		this.textWidth = Math.max(...maxwi)
		
		this.textHeight = allHeight

	},

	hoverCheck(position, {
		cache,
		shape,
		style
	}) {
		const [x, y] = shape.position;
		const {
			textBaseline
		} = style;
		var w = this?.textWidth ?? 0;
		var h = this?.textHeight ?? 0;

		let isCheck = false;

		if (textBaseline == 'top') {
			if (position[0] >= x && position[0] <= x + w && position[1] >= y && position[1] <= y + h) {
				isCheck = true;
			}
		} else if (textBaseline == 'bottom') {
			if (position[0] >= x && position[0] <= x + w && position[1] >= y + h && position[1] <= y + h * 2) {
				isCheck = true;
			}
		} else if (textBaseline == 'middle') {
			if (position[0] >= x && position[0] <= x + w && position[1] >= y - h / 2 && position[1] <= y + h - h /
				2) {
				isCheck = true;
			}
		}
		return isCheck;

	},

	setGraphCenter(e, {
		shape,
		style
	}) {
		const {
			position
		} = shape

		style.graphCenter = [...position]
	},

	move({
		movementX,
		movementY
	}, {
		shape
	}) {
		const {
			position: [x, y]
		} = shape

		this.attr('shape', {
			position: [x + movementX, y + movementY]
		})
	}
}

export const path = {
	shape: {
		points: [],
		close: false
	},

	validator({
		shape
	}) {
		const {
			points
		} = shape

		if (!(points instanceof Array)) {
			console.error('Polyline points should be an array!')

			return false
		}

		return true
	},

	draw({
		ctx
	}, {
		shape,
		style: {
			lineWidth
		}
	}) {
		ctx.beginPath()

		let {
			points,
			close
		} = shape

		if (lineWidth === 1) points = eliminateBlur(points)

		drawPolylinePath(ctx, points)

		if (close) {
			ctx.closePath()

			ctx.fill()
			ctx.stroke()
		} else {
			ctx.stroke()

		}
		ctx.draw()
	},

	hoverCheck(position, {
		shape,
		style
	}) {
		const {
			points,
			close
		} = shape

		const {
			lineWidth
		} = style

		if (close) {

			return checkPointIsInPolygon(position, points)
		} else {
			return checkPointIsNearPolyline(position, points, lineWidth)
		}
	},

	setGraphCenter(e, {
		shape,
		style
	}) {
		const {
			points
		} = shape

		style.graphCenter = points[0]
	},

	move({
		movementX,
		movementY
	}, {
		shape
	}) {
		const {
			points
		} = shape

		const moveAfterPoints = points.map(([x, y]) => [x + movementX, y + movementY])

		this.attr('shape', {
			points: moveAfterPoints
		})
	}
}

export const image = {
	shape: {
		x: 0,
		y: 0,
		w: 0,
		h: 0,
		sx: 0,
		sy: 0,
		src: ''
	},

	validator({
		shape
	}) {
		const {
			x,
			y,
			w,
			h,
			src
		} = shape

		if (typeof x !== 'number' || typeof y !== 'number' || typeof w !== 'number' || typeof h !== 'number' || !
			src) {
			console.error('image x,y,w,h,src必填。')

			return false
		}

		return true
	},

	draw({
		ctx,
		cav
	}, {
		shape
	}) {

		let {
			x,
			y,
			w,
			h,
			sx,
			sy,
			src
		} = shape

		let t = this;
		// #ifdef MP-WEIXIN
		//1加载中,2加载完成,3从未加载。
		if (typeof this['isLoad'] == 'undefined' || this['isLoad'] == 3) {
			
			this['isLoad'] = 1;
			const bg = cav.createImage()


			bg.onload = () => {

				setTimeout(function() {
					console.warn('tm-render:图片加载完成')
					ctx.drawImage(bg, x, y, w, h)
					t['isLoad'] = 2;
					shape.src = bg
					if(t?.load){
						t.load();
					}
				}, 400)
			}
			bg.onerror = () => t['isLoad'] = 3
			bg.src = src;
		}
		if (this['isLoad'] == 2) {
			
			ctx.drawImage(src, x, y, w, h)
			
		} else {
			console.log('image loadding...');
		}

		// #endif

		// #ifndef MP-WEIXIN
		
		if(typeof this['isLoad'] =='undefined') this['isLoad'] = 3;
		if(this['isLoad']===3){
			this['isLoad'] = 1;
			
			setTimeout(()=>{
				t['isLoad'] = 2;
				if(this?.load){
					this.load();
				}
			},1200)
		}
		
		ctx.drawImage(src, x, y, w, h, sx, sy)
		
		// #endif


	},

	hoverCheck(position, {
		shape
	}) {
		let {
			x,
			y,
			w,
			h
		} = shape

		return checkPointIsInRect(position, x, y, w, h)
	},

	setGraphCenter(e, {
		shape,
		style
	}) {
		const {
			x,
			y,
			w,
			h
		} = shape

		style.graphCenter = [x + w / 2, y + h / 2]
	},

	move({
		movementX,
		movementY
	}, {
		shape
	}) {
		this.attr('shape', {
			x: shape.x + movementX,
			y: shape.y + movementY
		})
	}
}

export const star = {
	shape: {
		points: [],
		close: false,
		x: 0,
		y: 0,
		numPoints: 5, //星星的角数量
		innerRadius: 40, //内部凹进去的比例
		outerRadius: 70, //角向外凸出的比例。
	},

	validator({
		shape
	}) {
		const {
			points,
			x,
			y
		} = shape

		if (typeof x !== 'number' || typeof y !== 'number') {
			console.error('Polyline points should be an array!')

			return false
		}

		return true
	},

	draw({
		ctx
	}, {
		shape,
		style: {
			lineWidth
		}
	}) {
		let context = ctx;
		let {
			points,
			close,
			x,
			y,
			numPoints,
			innerRadius,
			outerRadius
		} = shape;

		context.beginPath();
		context.moveTo(x, y - outerRadius);


		points.push([x, y - outerRadius])

		for (var n = 1; n <= numPoints * 2; n++) {
			var radius = n % 2 === 0 ? outerRadius : innerRadius;
			var x2 = radius * Math.sin((n * Math.PI) / numPoints);
			var y2 = -1 * radius * Math.cos((n * Math.PI) / numPoints);
			context.lineTo(x2 + x, y2 + y);

			points.push([x2 + x, y2 + y])
		}

		this.shape.points = points;
		if (lineWidth === 1) points = eliminateBlur(points)

		if (close) {
			ctx.closePath()

			ctx.fill()
			ctx.stroke()
		} else {
			ctx.stroke()

		}
		// ctx.draw()
	},

	hoverCheck(position, {
		shape,
		style
	}) {
		const {
			points,
			close
		} = shape

		const {
			lineWidth
		} = style

		if (close) {
			return checkPointIsInPolygon(position, points)
		} else {
			return checkPointIsNearPolyline(position, points, lineWidth)
		}
	},

	setGraphCenter(e, {
		shape,
		style
	}) {
		const {
			points
		} = shape

		style.graphCenter = points[0]
	},

	move({
		movementX,
		movementY
	}, {
		shape
	}) {
		const {
			points
		} = shape

		const moveAfterPoints = points.map(([x, y]) => [x + movementX, y + movementY])

		this.attr('shape', {
			points: moveAfterPoints
		})
	}
}

export const arrow = {
	shape: {
		points: [],
		close: true,
		x: 0,
		y: 0,
		tension: 0, //弯曲程度。
		pointerLength: 0, //箭头指针长度。
		pointerWidth: 0, //箭头指针宽度。
		pointerAtBeginning: false, //我们需要在两边画指针吗?默认值为 false。
		pointerAtEnding: true, //结束端显示箭头。
		hitPoints: [], //检测命中点。
	},

	validator({
		shape
	}) {
		const {
			points,
			x,
			y,
			close,
			tension,
			pointerLength,
			pointerWidth,
			pointerAtBeginning,
			pointerAtEnding
		} = shape

		if (typeof x !== 'number' || typeof y !== 'number') {
			console.error('Polyline points should be an array!')

			return false
		}

		return true
	},

	draw({
		ctx
	}, {
		shape,
		style: {
			lineWidth
		}
	}) {
		let context = ctx;

		let {
			points,
			x,
			y,
			close,
			tension,
			pointerLength,
			pointerWidth,
			pointerAtBeginning,
			pointerAtEnding
		} = shape
		let old_x = points[2] - points[0]
		let old_y = points[3] - points[1]
		points[0] = this.shape.x
		points[1] = this.shape.y
		points[2] = this.shape.x + old_x
		points[3] = this.shape.y + old_y
		var PI2 = Math.PI * 2;

		var tp = points;
		var fromTension = tension !== 0 && points.length > 4;
		if (fromTension) {

			if (close) {
				tp = getTensionPointsClosed(points, tension);
			} else {
				tp = expandPoints(points, tension);
			}
			console.log(tp);
		}
		var length = pointerLength;
		var n = points.length;
		var dx, dy;
		if (fromTension) {
			const lp = [
				tp[tp.length - 4],
				tp[tp.length - 3],
				tp[tp.length - 2],
				tp[tp.length - 1],
				points[n - 2],
				points[n - 1],
			];
			const lastLength = calcLength(tp[tp.length - 4], tp[tp.length - 3], 'C', lp);
			const previous = getPointOnQuadraticBezier(Math.min(1, 1 - length / lastLength), lp[0], lp[1], lp[2],
				lp[3], lp[4], lp[5]);
			dx = points[n - 2] - previous.x;
			dy = points[n - 1] - previous.y;
		} else {
			dx = points[n - 2] - points[n - 4];
			dy = points[n - 1] - points[n - 3];
		}
		var radians = (Math.atan2(dy, dx) + PI2) % PI2;
		var width = pointerWidth;
		this.shape.hitPoints = []
		ctx.save();
		ctx.beginPath();
		ctx.moveTo(points[0], points[1]);
		// #ifdef H5 || APP-VUE
		ctx.lineTo(points[2], points[3]);
		// #endif
		// #ifdef MP
		ctx.lineTo(points[2], points[3]);
		// #endif
		ctx.closePath();
		if (pointerAtEnding) {
			ctx.translate(points[n - 2], points[n - 1]);
			ctx.rotate(radians);
			// #ifdef H5 || APP-VUE

			ctx.moveTo(points[2], points[3]);
			ctx.lineTo(points[2], points[3] - width / 2);
			ctx.lineTo(length + points[2], points[3]);
			ctx.lineTo(points[2], width / 2 + points[3]);

			// #endif
			// #ifdef MP
			ctx.moveTo(0, 0);
			ctx.lineTo(-length, width / 2);
			ctx.lineTo(-length, -width / 2);
			// #endif
			ctx.closePath();
			ctx.restore();
			this.shape.hitPoints.push([points[2], points[3] - width / 2]);
			this.shape.hitPoints.push([length + points[2], points[3]]);
			this.shape.hitPoints.push([points[2], width / 2 + points[3]]);

		}
		if (pointerAtBeginning) {
			if (pointerAtBeginning) {
				ctx.save();
			}

			ctx.translate(x, y);
			if (fromTension) {
				dx = (tp[0] + tp[2]) / 2 - points[0];
				dy = (tp[1] + tp[3]) / 2 - points[1];
			} else {
				dx = points[2] - points[0];
				dy = points[3] - points[1];
			}
			ctx.rotate((Math.atan2(-dy, -dx) + PI2) % PI2);
			// #ifdef H5 || APP-VUE
			ctx.moveTo(points[0], points[1]);
			ctx.lineTo(points[0], points[1] - width / 2);
			ctx.lineTo(-length + points[0], points[1]);
			ctx.lineTo(points[0], width / 2 + points[1]);
			// #endif

			// #ifdef MP
			ctx.moveTo(0, 0);
			ctx.lineTo(-length, width / 2);
			ctx.lineTo(-length, -width / 2);
			// #endif

			ctx.closePath();
			ctx.restore();

		}

		if (close) {

			ctx.fill()
			ctx.stroke()
		} else {
			ctx.stroke()

		}

	},

	hoverCheck(position, {
		shape,
		style
	}) {
		const {
			points,
			hitPoints,
			close,
			pointerLength,
			pointerWidth
		} = shape

		const {
			lineWidth
		} = style

		//   if (close) {
		// console.log( checkPointIsInPolygon(position, hitPoints));
		//     return checkPointIsInPolygon(position, hitPoints)
		//   } else {
		//     return checkPointIsNearPolyline(position, hitPoints, lineWidth)
		//   }
		// #ifdef H5 || APP-VUE
		return checkPointIsInRect(position, points[2], points[3] - pointerWidth / 2, pointerLength, pointerWidth)
		// #endif
		// #ifdef MP
		return checkPointIsInRect(position, points[2] - pointerLength, points[3] - pointerWidth / 2, pointerLength,
			pointerWidth)
		// #endif
	},

	setGraphCenter(e, {
		shape,
		style
	}) {
		const {
			points
		} = shape

		style.graphCenter = points[0]
	},

	move({
		movementX,
		movementY
	}, {
		shape
	}) {
		const {
			points
		} = shape

		const moveAfterPoints = points.map(([x, y]) => [x + movementX, y + movementY])

		this.attr('shape', {
			points: moveAfterPoints
		})
	}
}


const graphs = new Map([

	['rectRound', rectRound],
	['arrow', arrow],
	['star', star],
	['image', image],
	['path', path],
	['circle', circle],
	['ellipse', ellipse],
	['rect', rect],
	['ring', ring],
	['arc', arc],
	['sector', sector],
	['regPolygon', regPolygon],
	['polyline', polyline],
	['smoothline', smoothline],
	['bezierCurve', bezierCurve],
	['text', text]
])

export default graphs

/**
 * @description Extend new graph
 * @param {String} name   Name of Graph
 * @param {Object} config Configuration of Graph
 * @return {Undefined} Void
 */
export function extendNewGraph(name, config) {
	if (!name || !config) {
		console.error('ExtendNewGraph Missing Parameters!')

		return
	}

	if (!config.shape) {
		console.error('Required attribute of shape to extendNewGraph!')

		return
	}

	if (!config.validator) {
		console.error('Required function of validator to extendNewGraph!')

		return
	}

	if (!config.draw) {
		console.error('Required function of draw to extendNewGraph!')

		return
	}

	graphs.set(name, config)
}