import { base64ToPathFn, downloadFile, showLoading, hideLoading, 
		countTextLength, getImageInfo, getModeImage, compressImage } from './util'

import QRCodeAlg from './qrcode'

/**
 * 绘制
 */
export default class Draw{
	constructor(params = {}) {
		let { width, canvasId, height, background, drawDelayTime, delayTime, _this, fileType, quality, isCompressImage } = params
		this.width = width
		this.height = height
		this.canvasId = canvasId || null
		this.background = background || {
			type: 'color',
			w: this.width,
			h: this.height,
			color: '#ffffff'
		}
		this.drawDelayTime = drawDelayTime || 200
		this.delayTime = delayTime || 200
		this._this = _this || null
		this.Context = uni.createCanvasContext(this.canvasId, this._this)
		// 导出图片的类型
		this.fileType = fileType || 'png'
		// 导出图片的质量
		this.quality = quality || 1
		// 是否压缩图片，填写时绘制图片会进行压缩操作。绘制图片也能填写该参数。层级大于当前
		this.isCompressImage = isCompressImage || false
		this.callBack = {
			bgObj: {
				width: this.background.w,
				height: this.background.h
			},
			ctxObj: {
				width,
				height
			}
		}
		this.drawTipsText = params.drawTipsText || '绘制中...'
		this.allCallBack = []
	}

	/**
	 * 绘制矩形
	 * @param { Object } params 绘制内容
	 */
	drawRect(params = {}) {
		let { width, Context } = this
		let { x, y, w, h, r, color, alpha, isFill, lineWidth, windowAlign, rotate, drawImage, borderColor, borderWidth, borderType } = {
			x: params.x || 0,
			y: params.y || 0,
			w: params.w || width,
			h: params.h || 0,
			r: params.r || 0,
			color: params.color || '#000000',
			borderWidth: params.borderWidth || 0,
			borderColor: params.borderColor || '#000000',
			borderType: params.borderType || 'default',
			alpha: params.alpha || 1,
			lineWidth: params.lineWidth || 1,
			isFill: params.isFill == undefined ? true : params.isFill,
			// 窗口对齐的方式 默认: none 可选 居中: center 右边: right
			windowAlign: params.windowAlign || 'none',
			// 旋转
			rotate: params.rotate || {},
			// 是否是在绘制图片,默认不是
			drawImage: params.drawImage == undefined ? false : params.drawImage,
		}
		if (r * 2 > h) {
			r = h / 2
		}
		if (!drawImage && rotate.deg) {
			Context.save()
			this.setRotate(x, y, w, h, rotate)
		}
		Context.beginPath()
		Context.setGlobalAlpha(alpha)
		if (windowAlign != 'none') {
			x = this.computedCenterX(width, w, windowAlign)
		}
		let tr = 0
		let tl = 0
		let br = 0
		let bl = 0
		if (typeof borderType == 'string') {
			switch(borderType) {
				case 'tr':
					tr = r
					break
				case 'tl':
					tl = r
					break
				case 'br':
					br = r
					break
				case 'bl':
					bl = r
					break
				default:
					tr = r
					tl = r
					br = r
					bl = r
			}
		}
		if (borderType instanceof Array) {
			if (borderType.includes('tr')) {
				tr = r
			}
			if (borderType.includes('tl')) {
				tl = r
			}
			if (borderType.includes('br')) {
				br = r
			}
			if (borderType.includes('bl')) {
				bl = r
			}
			if (borderType.includes('default')) {
				tr = r
				tl = r
				br = r
				bl = r
			}
		}
		// 上右 tr
		Context.lineTo(x + tl, y)
		Context.arc(x + w - tr, y + tr, tr, Math.PI * 1.5, 0, false)
		// 下右 br
		Context.lineTo(x + w, y + h - br)
		Context.arc(x + w - br,y + h - br, br, 0, Math.PI * .5, false)
		// 下左 bl
		Context.lineTo(x + bl, y + h)
		Context.arc(x + bl, y + h - bl, bl, Math.PI * .5, Math.PI, false)
		// 上左 tl
		Context.lineTo(x, y + tl)
		Context.arc(x + tl, y + tl, tl, Math.PI * 1, Math.PI * 1.5, false)
		Context.closePath()
		if (isFill) {
			if (borderWidth != 0) {
				Context.setLineWidth(borderWidth)
				Context.setStrokeStyle(borderColor)
				Context.stroke()
			}
			Context.setFillStyle(color)
			Context.fill()
		} else {
			Context.setLineWidth(lineWidth)
			Context.setStrokeStyle(color)
			Context.stroke()
		}
		Context.setGlobalAlpha(1)
		if (!drawImage && rotate.deg) {
			Context.restore()
		}
	}

	/**
	 * 绘制圆
	 * @param { Object } params 绘制内容
	 */
	drawArc(params = {}) {
		let { width, Context } = this
		let { x, y, r, s, e, d, alpha, isFill, lineWidth, color, windowAlign, borderColor, borderWidth } = {
			x: params.x || 0,
			y: params.y || 0,
			r: params.r || 0,
			s: params.s || 0,
			e: params.e || Math.PI * 2,
			// 可选。指定弧度的方向是逆时针还是顺时针。默认是false，即顺时针。
			d: params.d == undefined ? false : params.d,
			alpha: params.alpha || 1,
			isFill: params.isFill == undefined ? true : params.isFill,
			lineWidth: params.lineWidth || 1,
			borderWidth: params.borderWidth || 0,
			borderColor: params.borderColor || '#000000',
			color: params.color || '#000000',
			// 窗口对齐的方式 默认: none 可选 居中: center 右边: right
			windowAlign: params.windowAlign || 'none'
		}
		Context.beginPath()
		Context.setGlobalAlpha(alpha)
		x = x + r
		y = y + r
		if (windowAlign != 'none') {
			x = this.computedCenterX(width, r * 2, windowAlign)
			x += r
		}
		Context.arc(x, y, r, s, e, d)
		if (isFill) {
			if (borderWidth != 0) {
				Context.setLineWidth(borderWidth)
				Context.setStrokeStyle(borderColor)
				Context.stroke()
			}
			Context.setFillStyle(color)
			Context.fill()
		} else {
			Context.setLineWidth(lineWidth)
			Context.setStrokeStyle(color)
			Context.stroke()
		}
		Context.setGlobalAlpha(1)
	}


	/**
	 * 绘制三角形
	 * @param @param { Object } params 绘制内容
	 */
	drawTriangle(params = {}) {
		let { Context, width } = this
		let { x, y, w, h, color, alpha, isFill, lineWidth, drawType, coordinate, rotate, windowAlign, direction, borderWidth, borderColor } = {
			x: params.x || 0,
			y: params.y || 0,
			w: params.w || 0,
			h: params.h || 0,
			color: params.color || '#000000',
			borderWidth: params.borderWidth || 0,
			borderColor: params.borderColor || '#000000',
			alpha: params.alpha || 1,
			isFill: params.isFill == undefined ? true : params.isFill,
			lineWidth: params.lineWidth || 1,
			// 当绘制类别是自定义的时候需要传递的参数
			coordinate: params.coordinate || [],
			// 绘制三角形的类型
			// right: 直角三角形
			// isosceles: 等腰三角形
			// custom: 自定义时，x, y, 宽, 高都不需要传递。需要传递绘制点的坐标类型是数组(coordinate)
			// [[1, 3], [2, 3], [4, 5]]
			drawType: params.drawType || 'isosceles',
			// 三角形顶点朝向 top, left, right, bottom
			direction: params.direction || 'top',
			// 旋转
			rotate: params.rotate || {},
			// 窗口对齐的方式 默认: none 可选 居中: center 右边: right
			windowAlign: params.windowAlign || 'none'
		}
		if (windowAlign != 'none' && drawType != 'custom') {
			x = this.computedCenterX(width, w, windowAlign)
		}
		if (rotate.deg && drawType != 'custom') {
			Context.save()
			this.setTriangleRotate(x, y, w, h, rotate, drawType)
		}
		Context.beginPath()
		Context.setGlobalAlpha(alpha)
		if (drawType == 'isosceles') {
			switch (direction) {
				case 'top':
					Context.lineTo(x + w / 2, y)
					Context.lineTo(x, y + h)
					Context.lineTo(x + w, y + h)
					break
				case 'bottom':
					Context.lineTo(x, y)
					Context.lineTo(x + w, y)
					Context.lineTo(x + w / 2, y + h)
					break
				case 'right':
					Context.lineTo(x, y)
					Context.lineTo(x, y + h)
					Context.lineTo(x + w, y + h / 2)
					break
				case 'left':
					Context.lineTo(x + w, y)
					Context.lineTo(x + w, y + h)
					Context.lineTo(x, y + h / 2)
					break
			}
		} else if (drawType == 'right') {
			switch (direction) {
				case 'top':
					Context.lineTo(x, y)
					Context.lineTo(x, y + h)
					Context.lineTo(x + w, y + h)
					break
				case 'bottom':
					Context.lineTo(x, y)
					Context.lineTo(x + w, y)
					Context.lineTo(x, y + h)
					break
				case 'left':
					Context.lineTo(x, y)
					Context.lineTo(x, y + h)
					Context.lineTo(x + w, y)
					break
				case 'right':
					Context.lineTo(x, y + h)
					Context.lineTo(x + w, y + h)
					Context.lineTo(x + w, y)
					break
			}
			
		} else if (drawType == 'custom') {
			for (let i of coordinate) {
				Context.lineTo(i[0], i[1])
			}
		}
		Context.closePath()
		if (isFill) {
			if (borderWidth != 0) {
				Context.setLineWidth(borderWidth)
				Context.setStrokeStyle(borderColor)
				Context.stroke()
			}
			Context.setFillStyle(color)
			Context.fill()
		} else {
			Context.setLineWidth(lineWidth)
			Context.setStrokeStyle(color)
			Context.stroke()
		}
		Context.setGlobalAlpha(1)
		if (rotate.deg && drawType != 'custom') {
			Context.restore()
		}
	}


	/**
	 * 绘制图片
	 * @param { Object } params 绘制内容
	 */
	drawImage(params = {}) {
		let { width, Context } = this
		return new Promise(async resolve => {
			try {
				let { x, y, w, h, r, src, alpha, drawType, borderWidth, windowAlign, color, mode, rotate, triangle, isCompressImage, quality, borderColor, borderType } = {
					x: params.x || 0,
					y: params.y || 0,
					w: params.w || width,
					h: params.h || 0,
					r: params.r || 0,
					src: params.src || '',
					alpha: params.alpha || 1,
					mode: params.mode || 'aspectFill',
					// default: 默认，rect: 圆角矩形, arc: 圆形 triangle: 三角形
					drawType: params.drawType || 'default',
					borderWidth: params.borderWidth || 0,
					borderColor: params.borderColor || '#000000',
					borderType: params.borderType || 'default',
					color: params.color || '#ffffff',
					// 窗口对齐的方式 默认: none 可选 居中: center 右边: right
					windowAlign: params.windowAlign || 'none',
					// 旋转
					rotate: params.rotate || {},
					// 绘制三角形图片时三角形的内容
					// triangle.type 三角形的类型 right: 直角三角形 isosceles: 等腰三角形 custom: 自定义三角形(不支持旋转)
					// triangle.coordinate 自定义三角形时传递的坐标
					// triangle.direction 三角形顶点朝向
					triangle: params.triangle || {},
					// 是否压缩图片
					isCompressImage: params.isCompressImage != undefined ? params.isCompressImage : this.isCompressImage,
					// 压缩图片时图片的质量只对jpg类型的图片生效
					quality: params.quality || 80
				}
				if (!/\S/.test(src)) {
					return resolve({
						success: false,
						message: '图片路径为空'
					})
				}
				src = await base64ToPathFn(src)
				// #ifndef MP-TOUTIAO
				if (src.includes('http')) {
					let downlaod = await downloadFile(src)
					if (downlaod.data.statusCode != 200) {
						hideLoading()
						return resolve({
							success: false,
							msg: `图片路径为:${src}的文件下载失败`
						})
					}
					if (!downlaod.success) {
						hideLoading()
						return resolve({
							success: false,
							msg: '下载图片失败'
						})
					}
					src = downlaod.data.tempFilePath
				}
				// #endif
				if (windowAlign != 'none') {
					x = this.computedCenterX(width, w, windowAlign)
				}
				// #ifndef H5
				// 压缩图片(不支持H5)
				// if (isCompressImage) {
				// 	let compressRes = await compressImage({
				// 		src,
				// 		quality
				// 	})
				// 	if (!compressRes.success) {
				// 		return resolve(compressRes)
				// 	}
				// 	src = compressRes.src
				// }
				// #endif
				// showLoading('获取图片信息中....')
				let imageInfo = await getImageInfo(src)
				if (!imageInfo.success) {
					hideLoading()
					return resolve(imageInfo)
				}
				// hideLoading()
				let modeImage = getModeImage(Number(imageInfo.width), Number(imageInfo.height), x, y, w, h, mode)
				let { dx, dy, dw, dh, sw, sh, sx, sy } = modeImage
				Context.beginPath()
				if (drawType == 'default') {
					Context.save()
					this.setRotate(x, y, w, h, rotate)
					this.drawRect({
						x,
						y,
						w,
						h,
						alpha,
						color,
						drawImage: true
					})
					Context.clip()
					Context.setGlobalAlpha(alpha)
					if (mode != 'default') {
						await Context.drawImage(src, dx, dy, dw, dh, sx, sy, sw, sh)
					} else {
						await Context.drawImage(src, dx, dy, dw, dh)
					}
					Context.restore()
				} else if (drawType == 'arc') {
					// 绘制圆形图片
					Context.save()
					this.setRotate(x, y, w, h, rotate)
					this.drawArc({
						x,
						y,
						r: w / 2,
						borderWidth,
						borderColor,
						color,
					}, true)
					Context.clip()
					Context.setGlobalAlpha(alpha)
					if (mode != 'default') {
						await Context.drawImage(src, dx, dy, dw, dh, sx, sy, sw, sh)
					} else {
						await Context.drawImage(src, dx, dy, dw, dh)
					}
					Context.restore()
				} else if (drawType == 'rect') {
					// 绘制矩形图片
					Context.save()
					this.setRotate(x, y, w, h, rotate)
					this.drawRect({
						x,
						y,
						w,
						h,
						alpha,
						borderWidth,
						borderColor,
						borderType,
						r,
						color,
						drawImage: true
					}, true)
					Context.clip()
					Context.setGlobalAlpha(alpha)
					if (mode != 'default') {
						await Context.drawImage(src, dx, dy, dw, dh, sx, sy, sw, sh)
					} else {
						await Context.drawImage(src, dx, dy, dw, dh)
					}
					Context.restore()
				} else if (drawType == 'triangle') {
					// 绘制三角形图片
					Context.save()
					let type = triangle.type || 'isosceles'
					let coordinate = triangle.coordinate || []
					let direction = triangle.direction || 'top'
					if (type != 'custom') {
						this.setTriangleRotate(x, y, w, h, rotate, type)
					}
					this.drawTriangle({
						x,
						y,
						w,
						h,
						alpha,
						borderWidth,
						borderColor,
						color,
						coordinate,
						direction,
						drawType: type
					}, true)
					Context.clip()
					Context.setGlobalAlpha(alpha)
					if (mode != 'default') {
						await Context.drawImage(src, dx, dy, dw, dh, sx, sy, sw, sh)
					} else {
						await Context.drawImage(src, dx, dy, dw, dh)
					}
					Context.restore()
				}
				Context.setGlobalAlpha(1)
				return resolve({
					success: true,
					data: src
				})
			} catch(e) {
				return resolve({
					success: false,
					msg: '绘制图片出错'
				})
			}

		})
	}

	/**
	 * 绘制文字
	 * @param { Object } params 绘制内容 
	 */
	drawText(params = {}) {
		let { width, Context } = this
		let { x, y, w, text, textIndent, lastWidth, font, color, alpha, isFill, line, windowAlign, textAlign, baseline } = {
			x: params.x || 0,
			y: params.y || 0,
			w: params.w || width - params.x,
			text: String(params.text) || '',
			textIndent: params.textIndent || 0,
			lastWidth: params.lastWidth || 0,
			font: this.getFont(params.font),
			color: params.color || '#000000',
			alpha: params.alpha || 1,
			isFill: params.isFill == undefined ? true : params.isFill,
			// 文字在窗口对齐的方式 默认: none 可选 居中: center 右边: right
			windowAlign: params.windowAlign || 'none',
			// 文字的对齐方式(在容器里面) none 默认 center: 居中 right: 右边
			textAlign: params.textAlign || 'none',
			// 水平对齐方式
			baseline: params.baseline || 'top',
			line: this.getTextLine(params.line)
		}
		Context.beginPath()
		Context.setGlobalAlpha(alpha)
		Context.font = font.style
		Context.setTextBaseline(baseline)
		if (typeof text != 'string') {
			text += ''
		}
		let textArr = params.textArr
		if (!textArr) {
			textArr = this.computedFontTextLineHeight(x, y, w, text, textIndent, lastWidth, font, line, textAlign, windowAlign)
		}
		if (isFill) {
			Context.setFillStyle(color)
			for (let i of textArr) {
				let { text, x, y, tx, ty, tw } = i
				Context.fillText(text, x, y)
				if (line.lineStyle != 'none') {
					this.drawLine({
						x: tx,
						y: ty,
						w: tw,
						color,
						lineType: line.lineType,
						lineWidth: line.lineWidth
					}, true)
				}
			}
		} else {
			Context.setStrokeStyle(color)
			for (let i of textArr) {
				let { text, x, y, tx, ty, tw } = i
				Context.strokeText(text, x, y)
				if (line.lineStyle != 'none') {
					this.drawLine({
						x: tx,
						y: ty,
						w: tw,
						color,
						lineType: line.lineType,
						lineWidth: line.lineWidth
					}, true)
				}
			}
		}
		Context.setGlobalAlpha(1)
	}


	/**
	 * 获取字体样式
	 * @param { Object } font 字体
	 */
	getFont(font = {}) {
		let { fontSize, fontFamily, fontStyle, fontVariant, fontWeight } = {
			fontSize: font.size || 12,
			fontFamily: font.family || 'sans-serif',
			// 斜体: italic, 倾斜体:oblique
			fontStyle: font.style || 'normal',
			fontVariant: font.variant || 'normal',
			// 粗体
			fontWeight: font.weight || 'normal'
		}
		return {
			fontSize, fontFamily, fontStyle, fontVariant, fontWeight,
			style: `${fontStyle} ${fontVariant} ${fontWeight} ${fontSize}px ${fontFamily}`
		}
	}

	/**
	 * 获取文字line样式
	 * @param { Object } line 行高
	 */
	getTextLine(line = {}) {
		return {
			// 显示的行数 -1 不限制
			lineNum: line.num || -1,
			// 行高
			lineHeight: line.height || 16,
			// none: 不需要 underline: 下划线, lineThrough 删除线
			lineStyle: line.style || 'none',
			// 线的类型 dashed 虚线 solid 实线
			lineType: line.type || 'solid',
			// 线宽度
			lineWidth: line.width || 1
		}
	}

	/**
	 * 画线
	 * @param { Object } params 绘制内容 
	 */
	drawLine(params = {}) {
		let { width, Context } = this
		let { x, y, color, w, algin, alpha, lineType, pattern, offset, lineCap, lineWidth, windowAlign } = {
			x: params.x || 0,
			y: params.y || 0,
			w: params.w || width - params.x,
			color: params.color || '#000000',
			algin: params.algin || 'right',
			alpha: params.alpha || 1,
			// dashed 虚线 solid 实线
			lineType: params.lineType || 'solid',
			// 详看CanvasContext.setLineDash文档
			// 一组描述交替绘制线段和间距（坐标空间单位）长度的数字
			pattern: params.pattern || [5, 5],
			// 虚线偏移量
			offset: params.offset || 5,
			lineWidth: params.lineWidth || 1,
			lineCap: params.lineCap || 'butt',
			// 窗口对齐的方式 默认: none 可选 居中: center 右边: right
			windowAlign: params.windowAlign || 'none'
		}
		Context.beginPath()
		Context.setGlobalAlpha(alpha)
		if (lineType == 'dashed') {
			Context.setLineDash(pattern, offset)
		}
		Context.setLineCap(lineCap)
		Context.setLineWidth(lineWidth)
		Context.setStrokeStyle(color)
		switch (algin) {
			case 'right':
				if (windowAlign != 'none') {
					x = this.computedCenterX(width, w, windowAlign)
				}
				Context.moveTo(x, y)
				Context.lineTo(w + x, y)
				break
			case 'left':
				if (windowAlign != 'none') {
					x = this.computedCenterX(width, w, windowAlign)
				}
				Context.moveTo(x, y)
				Context.lineTo(windowAlign == 'none' ? x - w : x + w, y)
				break
			case 'top':
				Context.moveTo(x, y)
				Context.lineTo(x, -(y + w))
				break
			case 'bottom':
				Context.moveTo(x, y)
				Context.lineTo(x, y + w)
				break
		}
		Context.stroke()
		Context.closePath()
		Context.setLineDash()
		Context.setGlobalAlpha(1)
	}


	/**
	 * 绘制二维码
	 * @param { Object } params 二维码参数
	 */
	drawQrCode(params = {}) {
		let { Context, width } = this
		return new Promise(async resolve => {
			let { x, y, image, windowAlign, ...options } = {
				x: params.x || 0,
				y: params.y || 0,
				text: String(params.text) || '',
				size: params.size || 100,
				// 容错级别 默认3
				correctLevel: params.lv || 3,
				// 二维码背景色
				background: params.background || '#000000',
				// 二维码前景色
				foreground: params.foreground || '#ffffff',
				// 二维码角标色
				pdground: params.pdground || '#ffffff',
				image: params.image || {},
				// 窗口对齐的方式 默认: none 可选 居中: center 右边: right
				windowAlign: params.windowAlign || 'none'
			}
			if (windowAlign != 'none') {
				x = this.computedCenterX(width, options.size, windowAlign)
			}
			//使用QRCodeAlg创建二维码结构
			let qrcodeAlgObjCache = []
			let qrCodeAlg = null
			let l = qrcodeAlgObjCache.length
			let d = 0
			for (let i = 0;i < l; i++) {
				d = i
				if (qrcodeAlgObjCache[i].text == options.text && qrcodeAlgObjCache[i].text.correctLevel == options.correctLevel) {
					qrCodeAlg = qrcodeAlgObjCache[i].obj
					break
				}
			}
			if (d == l) {
				qrCodeAlg = new QRCodeAlg(options.text, options.correctLevel)
				qrcodeAlgObjCache.push({
					text: options.text,
					correctLevel: options.correctLevel,
					obj: qrCodeAlg
				})
			}
			/**
			 * 计算矩阵点的前景色
			 * @param {Obj} config
			 * @param {Number} config.row 点x坐标
			 * @param {Number} config.col 点y坐标
			 * @param {Number} config.count 矩阵大小
			 * @param {Number} config.options 组件的options
			 * @return {String}
			 */
			let getForeGround = function (config) {
				let options = config.options
				if (options.pdground && (
					(config.row > 1 && config.row < 5 && config.col > 1 && config.col < 5) ||
					(config.row > (config.count - 6) && config.row < (config.count - 2) && config.col > 1 && config.col < 5) ||
					(config.row > 1 && config.row < 5 && config.col > (config.count - 6) && config.col < (config.count - 2))
				)) {
					return options.pdground
				}
				return options.foreground
			}
			let count = qrCodeAlg.getModuleCount()
			let ratioSize = options.size
			let ratioImgSize = image.size || 30
			//计算每个点的长宽
			let tileW = (ratioSize / count).toPrecision(4)
			let tileH = (ratioSize / count).toPrecision(4)
			//绘制
			for (let row = 0; row < count; row++) {
				for (let col = 0; col < count; col++) {
					let w = (Math.ceil((col + 1) * tileW) - Math.floor(col * tileW))
					let h = (Math.ceil((row + 1) * tileW) - Math.floor(row * tileW))
					let foreground = getForeGround({
						row: row,
						col: col,
						count: count,
						options: options
					})
					Context.setFillStyle(qrCodeAlg.modules[row][col] ? foreground : options.background)
					Context.fillRect(x + Math.round(col * tileW), y + Math.round(row * tileH), w, h)
				}
			}
			if (image.src) {
				let { src, r, color, borderWidth, borderColor } = image
				let dx = x + Number(((ratioSize - ratioImgSize) / 2).toFixed(2))
                let dy = y + Number(((ratioSize - ratioImgSize) / 2).toFixed(2))
				let drawImage = await this.drawImage({
					x: dx,
					y: dy,
					w: ratioImgSize,
					h: ratioImgSize,
					src,
					r,
					color,
					borderWidth,
					borderColor,
					drawType: 'rect',
					isCompressImage: false,
				}, true)
				if (!drawImage.success) {
					return resolve(drawImage)
				}
			}
			return resolve({
				success: true
			})
		})
	}


	/**
	 * 计算出文字一共有多少列,渲染位置之类
	 * @param { Number } x x轴
	 * @param { Number } y y轴
	 * @param { Number } w 文字宽度
	 * @param { String } text 文字内容
	 * @param { Number } textIndent 首行缩进
	 * @param { Number } lastWidth 最后一行的宽度 
	 * @param { Object } font 字体
	 * @param { Object } line 行高
	 * @param { String } textAlign 文字对齐方式
	 * @param { String } windowAlign 窗口对齐方式
	 * @returns 
	 */
	computedFontTextLineHeight(x, y, w, text, textIndent, lastWidth, font, line, textAlign, windowAlign) {
		let { Context, width } = this
		let { fontSize } = font
		let { lineNum, lineHeight, lineStyle } = line
		// 文字的总长度
		let textLength = countTextLength(Context, text, fontSize)
		let temp = ''
		let row = []
		if (text.includes('\n')) {
			let texts = text.split('\n')
			for (let text of texts) {
				computedTextLength(text)
			}
		} else {
			computedTextLength(text)
		}
		function computedTextLength(text) {
			for (let i in text) {
				let tempLength = countTextLength(Context, temp, fontSize)
				if (row.length == 0 && textIndent != 0 && textAlign == 'none' && windowAlign == 'none') {
					tempLength += textIndent * fontSize
				}
				if (tempLength <= w && countTextLength(Context, (temp + text[i]), fontSize) <= w) {
					temp += text[i]
				} else {
					row.push(temp)
					temp = text[i]
				}
				if (i == text.length - 1) {
					row.push(temp)
					temp = ''
				}
			}
		}
		if (lineNum != -1 && lastWidth != 0 && row.length != 0 && row.length > lineNum) {
			let lastText = row[lineNum - 1]
			let temp = ''
			for (let i in lastText) {
				let tempLength = countTextLength(Context, temp, fontSize)
				if (tempLength <= lastWidth && countTextLength(Context, (temp + lastText[i]), fontSize) <= lastWidth) {
					temp += lastText[i]
					continue
				}
				break
			}
			row[lineNum - 1] = temp
		}
		lineHeight = lineHeight == 1 ? fontSize + 2 : lineHeight
		// 获取一共有多少列
		let lineSize = Math.ceil(textLength / w)
		if (text.includes('\n') && lineNum == -1) {
			lineNum = row.length
		} else if (text.includes('\n') && lineNum != -1) {
			lineSize = row.length
			lineNum = lineNum > lineSize ? lineSize : lineNum
		} else if (lineNum != -1) {
			lineNum = lineNum > lineSize ? lineSize : lineNum
		}
		let size = lineNum != -1 ? lineNum : lineSize
		let textArr = []
		for (let i = 0; i < size; i++) {
			let obj = {}
			let text = row[i]
			let textLength = countTextLength(Context, text, fontSize)
			let wx = x
			let tx = x
			if (i == 0 && textIndent != 0 && textAlign == 'none' && windowAlign == 'none') {
				textLength += textIndent * fontSize
				wx += textIndent * fontSize
				tx = wx
			}
			if (textAlign != 'none' && textLength < w) { 	
				tx = this.computedCenterX(w, textLength, textAlign)
				wx = tx
			}
			if (windowAlign != 'none' && textAlign != 'none') {
				wx = this.computedCenterX(width, w, windowAlign)
				wx += tx
				tx = wx
			} else if (windowAlign != 'none') {
				wx = this.computedCenterX(width, w, windowAlign)
				tx = wx
			}
			if (text && lineNum != -1 && i == lineNum - 1) {
				if ((textLength + fontSize) >= w) {
					text = text.substring(0, text.length - 1) + '...'
				} else if (lastWidth != 0 && (textLength + fontSize) >= lastWidth) {
					text = text.substring(0, text.length - 1) + '...'
				}
			}
			if (lineStyle != 'none') {
				obj.tx = tx
				obj.tw = textLength
				if (lineStyle == 'underline') {
					obj.ty =  y + (i * lineHeight) + fontSize
				}
				if (lineStyle == 'lineThrough') {
					obj.ty = y + (i * lineHeight) + fontSize / 2
				}
			}
			obj.text = text
			obj.x = wx
			obj.y = y + (i * lineHeight)
			text && textArr.push(obj)
		}
		return textArr
	}
	
	/**
	 * 计算内容需要显示在画布中间的x轴的位置
	 * @param { Number | String } boxWidth 容器的宽度
	 * @param { Number | String } contentWidth 内容宽度
	 * @param { String } type 类型 center: 水平 right: 右边
	 * @returns 
	 */
	computedCenterX(boxWidth, contentWidth, type = 'center') {
		if (type == 'center') {
			return (boxWidth - contentWidth) / 2
		}
		if (type == 'right') {
			return boxWidth - contentWidth
		}
	}

	/**
	 * 设置旋转角度
	 * @param { String } x x轴
	 * @param { String } y y轴
	 * @param { String } w 宽度
	 * @param { String } h 高度
	 * @param { Object } rotate 旋转内容
	 * @param { String } rotate.deg 旋转角度
	 * @param { String } rotate.type 类型 topLeft: 中心点在上左 topMiddle 中心点在上中 topRight 中心点在上右
	 *                             middleLeft: 中心点在中左 bottomMiddle 中心点在正中间 middleRight 中心点在中右
	 * 							   bottomLeft: 中心点在下左 bottomMiddle 中心点在下中 middleRight 中心点在下右
	 */
	setRotate(x, y, w, h, rotate) {
		let { Context } = this
		let deg = rotate.deg || 0
		let type = rotate.type || 'middle'
		let rx = x
		let ry = y
		switch (type) {
			case 'topLeft':
				Context.translate(rx, ry)
				Context.rotate(deg * Math.PI / 180)
				Context.translate(-rx, -ry)
				break
			case 'topMiddle':
				rx = x + (w / 2)
				Context.translate(rx, ry)
				Context.rotate(deg * Math.PI / 180)
				Context.translate(-rx, -ry)
				break
			case 'topRight':
				rx = x + w
				Context.translate(rx, ry)
				Context.rotate(deg * Math.PI / 180)
				Context.translate(-rx, -ry)
				break
			case 'bottomLeft':
				ry = y + h
				Context.translate(rx, ry)
				Context.rotate(deg * Math.PI / 180)
				Context.translate(-rx, -ry)
				break
			case 'bottomMiddle':
				rx = x + (w / 2)
				ry = y + h
				Context.translate(rx, ry)
				Context.rotate(deg * Math.PI / 180)
				Context.translate(-rx, -ry)
				break
			case 'bottomRight':
				rx = x + w
				ry = y + h
				Context.translate(rx, ry)
				Context.rotate(deg * Math.PI / 180)
				Context.translate(-rx, -ry)
				break
			case 'middleLeft':
				ry = y + (h / 2)
				Context.translate(rx, ry)
				Context.rotate(deg * Math.PI / 180)
				Context.translate(-rx, -ry)
				break
			case 'middleRight':
				rx = x + w
				ry = y + (h / 2)
				Context.translate(rx, ry)
				Context.rotate(deg * Math.PI / 180)
				Context.translate(-rx, -ry)
				break
			case 'middle':
				rx = x + (w / 2)
				ry = y + (h / 2)
				Context.translate(rx, ry)
				Context.rotate(deg * Math.PI / 180)
				Context.translate(-rx, -ry)
				break
		}
	}

	/**
	 * 设置三角形旋转角度
	 * @param { String } x x轴
	 * @param { String } y y轴
	 * @param { String } w 宽度
	 * @param { String } h 高度
	 * @param { Object } rotate 旋转内容
	 * @param { String } rotate.deg 旋转角度
	 * @param { String } rotate.type 类型 top: 上 left: 左 right: 右 middle: 中心
	 * @param { String } triangType 三角形类型(不支持自定义的三角形 ) right: 直角三角形 isosceles: 等腰三角形
	 */
	setTriangleRotate(x, y, w, h, rotate, triangType) {
		let { Context } = this
		let deg = rotate.deg || 0
		let type = rotate.type || 'top'
		let rx = x
		let ry = y
		switch (type) {
			case 'top':
				if (triangType == 'right') {
					rx = x
					ry = y
				} else {
					rx = x + w / 2
					ry = y
				}
				Context.translate(rx, ry)
				Context.rotate(deg * Math.PI / 180)
				Context.translate(-rx, -ry)
				break
			case 'left':
				rx = x
				ry = y + h
				Context.translate(rx, ry)
				Context.rotate(deg * Math.PI / 180)
				Context.translate(-rx, -ry)
				break
			case 'right':
				rx = x + w
				ry = y + h
				Context.translate(rx, ry)
				Context.rotate(deg * Math.PI / 180)
				Context.translate(-rx, -ry)
				break
			case 'middle':
				rx = x + w / 2
				ry = y + h / 2
				Context.translate(rx, ry)
				Context.rotate(deg * Math.PI / 180)
				Context.translate(-rx, -ry)
				break
		}
	}

	/**
	 * 排序drawArray 根据数据的zIndex进行排序，修改渲染顺序
	 * @param { Array } drawArray 绘制内容
	 */
	sortDrawArray(drawArray) {
		function compare() {
			return function(after, current) {
				let aZIndex = after.zIndex || 0
				let cZIndex = current.zIndex || 0
				return aZIndex - cZIndex
			}
		}
		drawArray.sort(compare())
		return drawArray
	}

	/**
	 * 往所有的回调信息里面添加内容
	 * @param { Object } params 内容
	 */
	setAllCallBack(params) {
		let { width } = this
		let { type, x, y, r, w, h, lineWidth, size, name } = params
		w = w || width
		h = h || 0
		x = x || 0
		y = y || 0
		r = r || 0
		lineWidth = lineWidth || 1
		size = size || 0
		name = name || ''
		let sx = x
		let sy = y
		let ex = x + w
		let ey = y + h
		// 圆形
		if (type == 'arc') {
			ex = x + r * 2
			ey = y + r * 2
			w = r * 2
			h = r * 2
		}
		// 文字
		if (type == 'text') {
			let { text, textIndent, lastWidth, font, line, textAlign, windowAlign } = {
				text: String(params.text) || '',
				textIndent: params.textIndent || 0,
				lastWidth: params.lastWidth || 0,
				font: this.getFont(params.font),
				line: this.getTextLine(params.line),
				textAlign: params.textAlign || 'none',
				windowAlign: params.windowAlign || 'none',
			}
			if (w == width) {
				w -= x
			}
			let textArr = this.computedFontTextLineHeight(x, y, w, text, textIndent, lastWidth, font, line, textAlign, windowAlign)
			let lastText = textArr[textArr.length - 1]	
			ex = lastText.x + (font.fontSize * lastText.text.length)
			ey = lastText.y + font.fontSize
			params.textArr = textArr
			h = ey - sy
		}
		// 线
		if (type == 'line') {
			ey = y + lineWidth
			h = lineWidth
		}
		// 二维码
		if (type == 'qrcode') {
			ex = x + size
			ey = y + size
			w = size
			h = size
		}
		this.allCallBack.push({
			sx,
			sy,
			ex,
			ey,
			w,
			h,
			name
		})
	}

	/**
	 * 绘制内容
	 * @returns 
	 */
	drawCanvas(drawArray) {
		let { Context } = this
		return new Promise(async resolve => {
			try {
				for (let i of drawArray) {
					if (i.callBack && typeof i.callBack == 'function' && i.type != 'custom') {
						let beforeInfo = this.allCallBack.length == 0 ? {} : this.allCallBack[this.allCallBack.length - 1]
						let callBackInfo = i.callBack(beforeInfo, this.allCallBack) || {}
						let { callBack, ...data } = i
						i = { ...data, ...callBackInfo }
					}
					if (i.type != 'custom' && i.drawType != 'custom') {
						this.setAllCallBack(i)
					}
					switch (i.type) {
						// 文字
						case 'text': 
							this.drawText(i)
							break
						// 矩形
						case 'rect':
							this.drawRect(i)
							break
						// 图片
						case 'image': 
							let image = await this.drawImage(i)
							if (!image.success) {
								return resolve(image)
							}
							break
						// 圆形
						case 'arc':
							this.drawArc(i)
							break
						// 三角形
						case 'triangle':
							this.drawTriangle(i)
							break
						// 线条
						case 'line':
							this.drawLine(i)
							break
						// 二维码
						case 'qrcode':
							await this.drawQrCode(i)
							break
						// 自定义
						case 'custom':
							i.setDarw(Context, this)
							break
					}
				}
				resolve({
					success: true
				})
			} catch(e) {
				hideLoading()
				return resolve({
					success: false,
					msg: '绘制内容失败:' + e
				})
			}
	
		})
	}

	/**
	 * 绘制背景
	 * @returns 
	 */
	drawBackground() {
		let { background, width, height } = this
		return new Promise(async resolve => {
			let { type, ...params } = background
			// 绘制背景色
			if (type == 'color') {
				this.drawRect({
					...params,
					w: params.w || width,
					h: params.h || height,
					color: params.color || '#ffffff'
				}, true)
			}
			// 绘制背景图
			if (type == 'image') {
				await this.drawImage({
					...params,
					w: params.w || width,
					h: params.h || height,
				}, true)
			}
			resolve({
				success: true
			})
		})
	}

	/**
	 * 创建canvas导出文件
	 * @returns 
	 */
	createdCanvasFilePath() {
		let { canvasId, width, height, _this, fileType, quality } = this
		return new Promise(resolve => {
			try {
				uni.canvasToTempFilePath({
					canvasId,
					x: 0,
					y: 0,
					width,
					height,
					quality,
					fileType,
					success: res => {
						resolve({
							success: true,
							data: res.tempFilePath,
							msg: '绘画成功'
						})
					},
					fail: err => {
						resolve({
							success: false,
							msg: `导出图片失败: ${JSON.stringify(err)}`
						})
						hideLoading()
					}
				}, _this || null)
			} catch (e) {
				hideLoading()
				resolve({
					success: false,
					msg: '导出图片失败: 绘画错误'
				})
			}
		})
	}

	/**
	 * 预绘制背景
	 * @returns 
	 */
	preDrawBackground() {
		return new Promise(async resolve => {
			try {
				showLoading('初始化中...')
				// 绘制背景
				const drawBackground = await this.drawBackground()
				if (!drawBackground.success) {
					hideLoading()
					return resolve({
						success: false,
						msg: '初始化失败，绘制背景图失败'
					})
				}
				hideLoading()
				return resolve({
					success: true,
					msg: '初始化成功'
				})
			} catch(e) {
				hideLoading()
				resolve({
					success: false,
					msg: e
				})
			}
		})
	}

	/**
	 * 将绘制的内容绘制到画布上
	 * @param { Boolean } complete 是否完成，如果为true会绘制图片并返回图片路径
	 * @returns 
	 */
	canvasDraw(complete = true) {
		let { Context, drawDelayTime } = this
		return new Promise(async resolve => {
			setTimeout(async () => {
				hideLoading()
				await Context.draw(true, async () => {
					if (complete) {
						return resolve(await this.exportImage())
					}
					return resolve({
						success: true,
						msg: '成功'
					})
				})
			}, drawDelayTime || 200)
		})
	}

	/**
	 * 预绘画
	 * @param { Function } drawArray 绘制内容 返回一个数组
	 * @param { Boolean } complete 是否完成，如果为true会绘制图片
	 * @returns 
	 */
	preDraw(drawArray, complete = false) {
		return new Promise(async resolve => {
			try {
				let { callBack, drawTipsText } = this
				showLoading(drawTipsText)
				let drawCanvas = await this.drawCanvas(this.sortDrawArray(drawArray(callBack)))
				// 绘制内容
				if (!drawCanvas.success) {
					hideLoading()
					return resolve(drawCanvas)
				}
				return resolve(await this.canvasDraw(complete))
			} catch(e) {
				hideLoading()
				resolve({
					success: false,
					msg: e
				})
			}

		})
	}


	/**
	 * 导出图片
	 * @returns 
	 */
	exportImage() {
		let { canvasId, width, height, _this, delayTime } = this
		return new Promise(resolve => {
			showLoading('加载图片中...')
			// 绘制图片
			setTimeout(async () => {
				resolve(await this.createdCanvasFilePath(canvasId, width, height, _this))
				hideLoading()
			}, delayTime || 200)
		})
	}

	
	/**
	 * 创建绘制海报
	 * @param { Function } drawArray 绘制内容 返回一个数组
	 * @returns 
	 */
	createdSharePoster(drawArray) {
		let { callBack } = this
		return new Promise(async resolve => {
			if (drawArray == null || drawArray == undefined) {
				return resolve({
					success: false,
					msg: '请传递绘制内容'
				})
			}
			let backgroundRes = await this.preDrawBackground()
			if (!backgroundRes.success) {
				return resolve(backgroundRes)
			}
			showLoading('绘制中,请稍等')
			let drawRes = await this.drawCanvas(this.sortDrawArray(await drawArray(callBack)))
			// 绘制内容
			if (!drawRes.success) {
				hideLoading()
				return resolve(drawRes)
			}
			return resolve(await this.canvasDraw())
		})
	}
}