package cn.hmsoft.helper.qrcode;

import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.Hashtable;
import java.util.Map;

import javax.imageio.ImageIO;

import com.google.zxing.BinaryBitmap;
import com.google.zxing.DecodeHintType;
import com.google.zxing.EncodeHintType;
import com.google.zxing.LuminanceSource;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.Result;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.google.zxing.qrcode.encoder.ByteMatrix;
import com.google.zxing.qrcode.encoder.Encoder;
import com.google.zxing.qrcode.encoder.QRCode;

public abstract class QrCodeHelper {
	public static void encode(QrCodeEntity entity, OutputStream stream) {

		Map<EncodeHintType, Object> hints = new Hashtable<>();
		// 指定纠错等级
		hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L);
		// 指定编码格式
		hints.put(EncodeHintType.CHARACTER_SET, "GBK");
		try {
			// BitMatrix bitMatrix = new MultiFormatWriter().encode(contents,
			// BarcodeFormat.QR_CODE, width, height, hints);
			ErrorCorrectionLevel errorCorrectionLevel = ErrorCorrectionLevel.L;
			int quietZone = entity.getQrMargin();
			if (hints != null) {
				ErrorCorrectionLevel requestedECLevel = (ErrorCorrectionLevel) hints.get(EncodeHintType.ERROR_CORRECTION);
				if (requestedECLevel != null) {
					errorCorrectionLevel = requestedECLevel;
				}
				Integer quietZoneInt = (Integer) hints.get(EncodeHintType.MARGIN);
				if (quietZoneInt != null) {
					quietZone = quietZoneInt;
				}
			}

			QRCode code = Encoder.encode(entity.getQrContent(), errorCorrectionLevel, hints);
			// return renderResult(code, width, height, quietZone);
			BitMatrix bitMatrix = renderResult(code, entity.getQrWidth(), entity.getQrHeight(), quietZone);

			if (entity.getScaleImage() != null && entity.getScaleImage().exists() && entity.getScaleImage().isFile()) {
				int width = bitMatrix.getWidth();
				int height = bitMatrix.getHeight();
				BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

				for (int x = 0; x < width; x++) {
					for (int y = 0; y < height; y++) {
						image.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF);
					}
				}
				Graphics2D gs = image.createGraphics();

				// 载入logo
				Image img = ImageIO.read(entity.getScaleImage());
				gs.drawImage(img, bitMatrix.getWidth() / 2 - entity.getImageWidth() /2, bitMatrix.getHeight() / 2 - entity.getImageHeight() / 2, entity.getImageWidth(), entity.getImageHeight(), null);
				gs.dispose();
				img.flush();
				ImageIO.write(image, "png", stream);
				return;
			}

			MatrixToImageWriter.writeToStream(bitMatrix, "png", stream);
			try{
				stream.close();
			}catch(Exception e){
				
			}
		} catch (Exception e) {
			// e.printStackTrace();
			
		}
	}

	
	private static BitMatrix renderResult(QRCode code, int width, int height, int quietZone) {
		ByteMatrix input = code.getMatrix();
		if (input == null) {
			throw new IllegalStateException();
		}
		int inputWidth = input.getWidth();
		int inputHeight = input.getHeight();
		int qrWidth = inputWidth;
		int qrHeight = inputHeight;
		int outputWidth = Math.max(width, qrWidth);
		int outputHeight = Math.max(height, qrHeight);

		int multiple = Math.min(outputWidth / qrWidth, outputHeight / qrHeight);

		int leftPadding = (outputWidth - (inputWidth * multiple)) / 2;
		int topPadding = (outputHeight - (inputHeight * multiple)) / 2;

		if (leftPadding >= 0) {
			outputWidth = outputWidth - 2 * leftPadding;
			leftPadding = 0;
		}
		if (topPadding >= 0) {
			outputHeight = outputHeight - 2 * topPadding;
			topPadding = 0;
		}

		BitMatrix output = new BitMatrix(outputWidth, outputHeight);

		for (int inputY = 0, outputY = topPadding; inputY < inputHeight; inputY++, outputY += multiple) {
			// Write the contents of this row of the barcode
			for (int inputX = 0, outputX = leftPadding; inputX < inputWidth; inputX++, outputX += multiple) {
				if (input.get(inputX, inputY) == 1) {
					output.setRegion(outputX, outputY, multiple, multiple);
				}
			}
		}

		return output;
	}

	/**
	 * 生成二维码
	 */
	public static boolean encode(QrCodeEntity entity, String imgPath) {
		try {
			FileOutputStream stream = new FileOutputStream(new File(imgPath));
			encode(entity, stream);
		} catch (Exception e) {
			return false;
		}
		return true;
	}

	/**
	 * 解析二维码
	 */
	public static String decode(String imgPath) {
		BufferedImage image = null;
		Result result = null;
		try {
			image = ImageIO.read(new File(imgPath));
			if (image == null) {
				System.out.println("the decode image may be not exit.");
			}
			LuminanceSource source = new BufferedImageLuminanceSource(image);
			BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));

			Map<DecodeHintType, Object> hints = new Hashtable<>();
			hints.put(DecodeHintType.CHARACTER_SET, "GBK");

			result = new MultiFormatReader().decode(bitmap, hints);
			return result.getText();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

}
