package com.utopiaindustries.util;


import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.Writer;
import com.google.zxing.aztec.AztecWriter;
import com.google.zxing.client.j2se.MatrixToImageConfig;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.datamatrix.DataMatrixWriter;
import com.google.zxing.oned.*;
import com.google.zxing.pdf417.PDF417Writer;
import com.google.zxing.qrcode.QRCodeWriter;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Base64;
import java.util.Map;

public class BarcodeUtils {

    /**
     * get qr code byte image array
     */
    public static byte[] getQRCodeImageBytesArray( String data, int width, int height ) throws Exception {
        // generate bit matrix
        QRCodeWriter writer = new QRCodeWriter();
        BitMatrix bitMatrix = writer.encode( data, BarcodeFormat.QR_CODE, width, height );
        // remove whitespace
        int padding = 0;
        int[] rec = bitMatrix.getEnclosingRectangle();
        int newWidth = rec[2] + 2 * padding;
        int newHeight = rec[3] + 2 * padding;
        BitMatrix newBitMatrix = new BitMatrix( newWidth, newHeight );
        newBitMatrix.clear();
        for ( int i = 0; i < newWidth; i++ ) {
            for ( int j = 0; j < newHeight; j++ ) {
                if ( bitMatrix.get(i + rec[0], j + rec[1] ) ) {
                    newBitMatrix.set( i + padding, j + padding );
                }
            }
        }
        // convert matrix to image
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        MatrixToImageConfig config = new MatrixToImageConfig();
        MatrixToImageWriter.writeToStream( newBitMatrix, "PNG", outputStream, config );
        // convert to byte array and return
        byte[] imgData = outputStream.toByteArray();
        outputStream.close();
        return imgData;
    }

    /**
     * get qr code base64 encoded string
     */
    public static String getQRCodeImageBase64Encoded( String data, int width, int height ) throws Exception {
        // get image bytes
        byte[] imgBytes = getQRCodeImageBytesArray( data, width, height );
        // convert to base64 and return
        return Base64.getEncoder().encodeToString( imgBytes );
    }

    /**
     * get writer
     */
    private static Writer getWriter(BarcodeFormat format ) throws Exception {
        if ( format.equals( BarcodeFormat.AZTEC ) ) {
            return new AztecWriter();
        } else if ( format.equals( BarcodeFormat.CODABAR ) ) {
            return new CodaBarWriter();
        } else if ( format.equals( BarcodeFormat.CODE_39 ) ) {
            return new Code39Writer();
        } else if ( format.equals( BarcodeFormat.CODE_93 ) ) {
            return new Code93Writer();
        } else if ( format.equals( BarcodeFormat.CODE_128 ) ) {
            return new Code128Writer();
        } else if ( format.equals( BarcodeFormat.DATA_MATRIX ) ) {
            return new DataMatrixWriter();
        } else if ( format.equals( BarcodeFormat.EAN_8 ) ) {
            return new EAN8Writer();
        } else if ( format.equals( BarcodeFormat.EAN_13 ) ) {
            return new EAN13Writer();
        } else if ( format.equals( BarcodeFormat.ITF ) ) {
            return new ITFWriter();
        } else if ( format.equals( BarcodeFormat.PDF_417 ) ) {
            return new PDF417Writer();
        } else if ( format.equals( BarcodeFormat.QR_CODE ) ) {
            return new QRCodeWriter();
        } else if ( format.equals( BarcodeFormat.UPC_A ) ) {
            return new UPCAWriter();
        } else if ( format.equals( BarcodeFormat.UPC_E ) ) {
            return new UPCEWriter();
        }
        throw new Exception( "Unsupported barcode format: " + format );
    }

    /**
     * get barcode byte array
     */
    public static byte[] getBarcodeImageByteArray( String data, BarcodeFormat format, int width, int height ) throws Exception {
        try {
            Writer barcodeWriter = getWriter( format );
            BitMatrix bitMatrix = barcodeWriter.encode( data, format, width, height );
            // convert matrix to image data
            return getBarcodeImageByteArrayData( bitMatrix );
        } catch ( Exception e ) {
            System.out.println( e.getMessage() );
        }
        return new byte[0];
    }

    public static byte[] getBarcodeImageByteArray( String data, BarcodeFormat format, int width, int height, Map<EncodeHintType, Object> hints ) throws Exception {
        try {
            Writer barcodeWriter = getWriter( format );
            BitMatrix bitMatrix = barcodeWriter.encode( data, format, width, height, hints );
            // convert matrix to image data
            return getBarcodeImageByteArrayData( bitMatrix );
        } catch ( Exception e ) {
            e.printStackTrace();
        }
        return new byte[0];
    }

    private static byte[] getBarcodeImageByteArrayData( BitMatrix bitMatrix ) throws IOException {
        // convert matrix to image
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        MatrixToImageConfig config = new MatrixToImageConfig();
        MatrixToImageWriter.writeToStream( bitMatrix, "PNG", outputStream, config );
        // convert to byte array and return
        byte[] imgData = outputStream.toByteArray();
        outputStream.close();
        return imgData;
    }

    /**
     * generate barcode base64 encoded string
     */
    public static String getBarcodeImageBase64Encoded( String data, BarcodeFormat format, int width, int height ) throws Exception {
        byte[] imgData = getBarcodeImageByteArray( data, format, width, height );
        // convert to base64 and return
        return Base64.getEncoder().encodeToString( imgData );
    }
}
