// 导入所有条形码
// @ts-nocheck
import barcodes from './jsbarcode/barcodes/';
import fixOptions from './jsbarcode/help/fixOptions';
import merge from './jsbarcode/help/merge';
import defaultOptions from './jsbarcode/options/defaults';
import { InvalidInputException } from './jsbarcode/exceptions/exceptions';
import linearizeEncodings from './jsbarcode/help/linearizeEncodings';
import { calculateEncodingAttributes, getTotalWidthOfEncodings, getMaximumHeightOfEncodings } from "./jsbarcode/renderers/shared";
import { LBarcodeOptions, EncodeResult } from './type'

function autoSelectBarcode() : string {
	// 如果有 CODE128，则使用它
	if (barcodes["CODE128"]) {
		return "CODE128";
	}

	// 否则，使用第一个（或唯一）条形码
	return Object.keys(barcodes)[0];
}
// encode() 处理编码器调用并生成要呈现的二进制数据
function encode(text : string, Encoder : any, options : LBarcodeOptions) : any {
	// 确保文本是字符串
	text = "" + text;

	let encoder = new Encoder(text, options);

	// 如果输入对编码器无效，则抛出错误。如果已设置有效回调选项，则调用该选项
	if (!encoder.valid()) {
		throw new InvalidInputException(encoder.constructor.name, text);
	}

	// 为要呈现的二进制数据发出请求（和其他信息）
	let encoded = encoder.encode();

	// 编码可以像 [1-1, 1-2], 2, [3-1, 3-2] 一样嵌套
	// 将其转换为 [1-1, 1-2, 2, 3-1, 3-2]
	encoded = linearizeEncodings(encoded);

	// 合并
	for (let i = 0; i< encoded.length; i++) {
	    encoded[i].options = merge(options, encoded[i].options);
	}

	return encoded;
}


let timer = null
function prepareCanvas(element : any, encodings : any, options : LBarcodeOptions) {
	const ctx = element.getContext('2d');
	ctx.save();
	calculateEncodingAttributes(encodings, options, ctx);
	const totalWidth = getTotalWidthOfEncodings(encodings);
	const maxHeight = getMaximumHeightOfEncodings(encodings);
	const width = totalWidth + options.marginLeft + options.marginRight
	// 为避免报黄 Multiple readback operations using getImageData
	element.width = width;
	element.height = maxHeight
}

function drawCanvasBarcode(element : any, options : LBarcodeOptions, encoding : EncodeResult) {
	// Get the canvas context
	const ctx = element.getContext("2d");
	const binary = encoding.data;
	// Creates the barcode out of the encoded binary
	let yFrom = 0;
	if (options.textPosition == "top") {
		yFrom = options.marginTop + options.fontSize + options.textMargin;
	}
	else {
		yFrom = options.marginTop;
	}
	ctx.fillStyle = options.lineColor;
	for (let b = 0; b < binary.length; b++) {
		let x = b * options.width + encoding.barcodePadding;
		if (binary[b] === "1") {
			ctx.fillRect(x, yFrom, options.width, options.height);
		}
		else if (binary[b]) {
			ctx.fillRect(x, yFrom, options.width, options.height * parseInt(binary[b]));
		}
	}
}
function drawCanvasText(element : any, options : LBarcodeOptions, encoding : EncodeResult) {
	// Get the canvas context
	const ctx = element.getContext("2d");
	
	const font = `${options.fontOptions} ${options.fontSize}px ${options.font}`.trim();
	
	// Draw the text if displayValue is set
	if (options.displayValue) {
		let x = 0, y = 0;
	
		if (options.textPosition == "top") {
			y = options.marginTop + options.fontSize - options.textMargin;
		}
		else {
			y = options.height + options.textMargin + options.marginTop + options.fontSize;
		}
	
		ctx.font = font;
	
		// Draw the text in the correct X depending on the textAlign option
		if (options.textAlign == "left" || encoding.barcodePadding > 0) {
			x = 0;
			ctx.textAlign = 'left';
		}
		else if (options.textAlign == "right") {
			x = encoding.width - 1;
			
			ctx.textAlign = 'right';
		}
		// In all other cases, center the text
		else {
			x = encoding.width / 2;
			ctx.textAlign = 'center';
		}
		ctx.fillText(encoding.text, x, y);
	}
}
function moveCanvasDrawing(element : any, encoding: EncodeResult) {
	const ctx = element.getContext("2d");
	ctx.translate(encoding.width, 0);
}

export function CanvasRenderer(element : any, text : string, options : LBarcodeOptions, cb: ()=> void) {
	if (typeof element === "undefined") {
		throw Error("No element to render on was provided.");
	}
	if (text) {
		let newOptions = { ...defaultOptions, text };
		for (let key in options) {
			const value = options[key]
			if (value || typeof value == 'boolean') {
				newOptions[key] = value
			}
		}
		newOptions = fixOptions(newOptions);
		if (!newOptions.format || newOptions.format == "auto") {
			newOptions.format = autoSelectBarcode();
		}
		if (!element.getContext) {
			throw new Error('不存在 getContext');
		}
		const ctx = element.getContext('2d');
		const Encoder = barcodes[newOptions.format.toUpperCase()];
		const encodings = encode(text, Encoder, newOptions);
		prepareCanvas(element, encodings, newOptions)
		// Paint the canvas
		// clearTimeout(timer)
		setTimeout(() => {
			ctx.clearRect(0, 0, element.width, element.height);
			if (newOptions.background) {
				ctx.fillStyle = newOptions.background;
				ctx.fillRect(0, 0, element.width, element.height);
			}
			ctx.translate(newOptions.marginLeft, 0);
			// render
			for (let i = 0; i < encodings.length; i++) {
				const encodingOptions = merge(newOptions, encodings[i].options);

				drawCanvasBarcode(element, encodingOptions, encodings[i]);
				drawCanvasText(element, encodingOptions, encodings[i]);

				moveCanvasDrawing(element, encodings[i]);
			}
			// render end
			ctx.restore();
			if (ctx.draw) {
				ctx.draw(false, cb)
			} else {
				cb && cb()
			}
		}, 300)
	}

}