package com.utopiaindustries.service;

import com.google.zxing.BarcodeFormat;
import com.utopiaindustries.dao.ctp.BundleDAO;
import com.utopiaindustries.dao.ctp.FinishedItemDAO;
import com.utopiaindustries.dao.ctp.MasterBundleDAO;
import com.utopiaindustries.dao.ctp.StitchingOfflineItemDAO;
import com.utopiaindustries.model.ctp.*;
import com.utopiaindustries.util.BarcodeUtils;
import com.utopiaindustries.util.StringUtils;
import com.zebra.sdk.comm.Connection;
import com.zebra.sdk.comm.TcpConnection;
import com.zebra.sdk.graphics.internal.ZebraImage;
import com.zebra.sdk.printer.ZebraPrinter;
import com.zebra.sdk.printer.ZebraPrinterFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.Font;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;

import java.util.ArrayList;
import java.util.List;

@Service
public class BarcodeService {

    @Value("${ctp.printer.ipAdd}")
    private String ipAddress;

    @Value("${ctp.printer.port}")
    private int port;

    private final BundleDAO bundleDAO;
    private final MasterBundleDAO masterBundleDAO;
    private final FinishedItemDAO finishedItemDAO;
    private final StitchingOfflineItemDAO stitchingOfflineItemDAO;

    public BarcodeService(BundleDAO bundleDAO, MasterBundleDAO masterBundleDAO, FinishedItemDAO finishedItemDAO, StitchingOfflineItemDAO stitchingOfflineItemDAO) {
        this.bundleDAO = bundleDAO;
        this.masterBundleDAO = masterBundleDAO;
        this.finishedItemDAO = finishedItemDAO;
        this.stitchingOfflineItemDAO = stitchingOfflineItemDAO;
    }

    /*
     *  generate barcodes here
     * */
    public void generateBarcodes(List<Long> ids, String artifactType) throws Exception {
        String size = BarcodeStickerSize.SIZE_4_X_7.name();
        BarcodeStickerSize stickerSize = BarcodeStickerSize.getSize(size);

        List<? extends InventoryArtifact> list = new ArrayList<>();
        if (StringUtils.compare(artifactType, Bundle.class.getSimpleName())) {
            list = bundleDAO.findByIds(ids);
        } else if (StringUtils.compare(artifactType, MasterBundle.class.getSimpleName())) {
            list = masterBundleDAO.findByIds(ids);
        } else if (StringUtils.compare(artifactType, FinishedItem.class.getSimpleName())) {
            list = stitchingOfflineItemDAO.findByIds(ids);
        }

        getBarcodeImages(list, stickerSize, artifactType);

    }

    public void getBarcodeImages(List<? extends InventoryArtifact> artifacts,
                                                 BarcodeStickerSize stickerSize,
                                                 String artifactType) throws Exception {

        for (InventoryArtifact artifact : artifacts) {
            // Create a blank BufferedImage (an image with the size of the sticker)
            BufferedImage stickerImage = new BufferedImage((int) stickerSize.getWidth()*2, (int) stickerSize.getHeight()*2, BufferedImage.TYPE_INT_ARGB);
            Graphics2D g2d = stickerImage.createGraphics();
            g2d.scale(2,2);
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

            // Set background color (white for the sticker)
            g2d.setColor(Color.WHITE);
            g2d.fillRect(0, 0, (int) stickerSize.getWidth(), (int) stickerSize.getHeight());

            // Set font for SKU and barcode
            Font font = new Font("Helvetica", Font.BOLD, stickerSize.getTextSize()+10);
            g2d.setFont(font);

            // Add SKU to the image
            String sku = artifact.getSku();
            FontMetrics fontMetrics = g2d.getFontMetrics();
            int textWidth = fontMetrics.stringWidth(sku);
            int x = (int) ((stickerSize.getWidth() - textWidth) / 2);
            g2d.setColor(Color.BLACK);
            g2d.drawString(sku, x, stickerSize.getMarginTop() + fontMetrics.getAscent()+20);

            // Create the barcode image
            byte[] imgBytes = BarcodeUtils.getBarcodeImageByteArray(artifact.getBarcode(), BarcodeFormat.CODE_128, stickerSize.getImageWidthBarcode()+500, stickerSize.getImageHeightBarcode()+30);
            BufferedImage barcodeImage = ImageIO.read(new ByteArrayInputStream(imgBytes));

            // Draw the barcode image on the sticker
            int barcodeX =(int) (stickerSize.getWidth() - barcodeImage.getWidth()) / 2;
            int barcodeY = stickerSize.getMarginTop() + fontMetrics.getAscent() + 30; // Add some margin
            g2d.drawImage(barcodeImage, barcodeX, barcodeY, null);

            // Add the barcode value below the barcode image
            g2d.drawString(artifact.getBarcode(), (stickerSize.getWidth() - fontMetrics.stringWidth(artifact.getBarcode())) / 2,
                    barcodeY + barcodeImage.getHeight() + fontMetrics.getAscent());

            // If artifactType is Bundle, add additional info
            if (artifactType.equalsIgnoreCase(Bundle.class.getSimpleName())) {
                String typeText = String.format("%s : %d", artifact.getType(), artifact.getId());
                g2d.drawString(typeText, (stickerSize.getWidth() - fontMetrics.stringWidth(typeText)) / 2,
                        barcodeY + barcodeImage.getHeight() + fontMetrics.getAscent() + 45);

                g2d.setFont(new Font("Helvetica", Font.BOLD, stickerSize.getTextSize()+20));
                g2d.drawString(String.valueOf(artifactType.toCharArray()[0]), (stickerSize.getWidth() - fontMetrics.stringWidth(String.valueOf(artifactType.toCharArray()[0]))) / 2,
                        barcodeY + barcodeImage.getHeight() + fontMetrics.getAscent() + 77);
            } else {
                // Add first character of artifact type
                String type = String.valueOf(artifactType.charAt(0));
                g2d.setFont(new Font("Helvetica", Font.BOLD, stickerSize.getTextSize()+10));
                g2d.drawString(type, (stickerSize.getWidth() - fontMetrics.stringWidth(type)) / 2,
                        barcodeY + barcodeImage.getHeight() + fontMetrics.getAscent() + 45);
            }

            // Finalize drawing
            g2d.dispose();
            printLabel(stickerImage);
        }
    }

    public void printLabel( BufferedImage bufferedImage ) throws Exception {
        Connection connection = new TcpConnection( ipAddress, port );
        connection.open();
        ZebraPrinter printer = ZebraPrinterFactory.getInstance( connection );
        ZebraImage zebraImage = new ZebraImage( bufferedImage );
        printer.printImage( zebraImage, 0, 0, 0, 0, false );
        connection.close();
    }
}