
package com.mycompany.sigge;


import com.github.anastaciocintra.escpos.EscPos;
import com.github.anastaciocintra.escpos.image.CoffeeImageImpl;
import com.github.anastaciocintra.escpos.image.EscPosImage;
import com.github.anastaciocintra.output.PrinterOutputStream;
import com.github.anastaciocintra.escpos.EscPosConst;
import com.github.anastaciocintra.escpos.image.RasterBitImageWrapper;
import javax.print.*;
import javax.print.attribute.*;
import javax.print.attribute.standard.MediaSizeName;
import javafx.scene.layout.*;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import javafx.geometry.Insets;
import javafx.scene.control.Alert;
import javafx.print.Printer;
import org.json.JSONObject;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.stage.Stage;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.cell.TextFieldTableCell;
import javax.print.attribute.standard.Copies;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.awt.image.BufferedImage;
import org.apache.pdfbox.rendering.ImageType;
import com.github.anastaciocintra.escpos.image.BitonalThreshold;
import javax.print.PrintService;
import javax.print.PrintServiceLookup;
import javax.print.DocPrintJob;
import javax.print.DocFlavor;
import javax.print.Doc;
import javax.print.SimpleDoc;


public class PrinterSetup {

	private static final Path PRINTER_FILE
		= Paths.get(System.getProperty("user.home"), ".sigge", "printers.json");


	public static void show() {
		Stage stage = new Stage();

		TableView<PrinterRow> table = new TableView<>();
		table.setEditable(true);
		TableColumn<PrinterRow,String> printerCol = new TableColumn<>("Impresora");
		printerCol.setCellValueFactory(data->new javafx.beans.property.SimpleStringProperty(data.getValue().getPrinterName()));
		printerCol.setPrefWidth(350);
		TableColumn<PrinterRow, String> aliasCol= new TableColumn<>("Alias");
		aliasCol.setCellValueFactory(data->new javafx.beans.property.SimpleStringProperty(data.getValue().getAlias()));
		aliasCol.setCellFactory(TextFieldTableCell.forTableColumn());
		aliasCol.setOnEditCommit(e->e.getRowValue().setAlias(e.getNewValue()));
		aliasCol.setPrefWidth(200);
		table.getColumns().addAll(printerCol,aliasCol);

		Map<String,String>aliases=loadAliases();
		for (Printer printer : Printer.getAllPrinters()) {
			String alias = null;
			for (Map.Entry<String,String>entry:aliases.entrySet()){
				if(printer.getName().equals(entry.getValue())){
					alias=entry.getKey();
					break;
				}
			}
			table.getItems().add(new PrinterRow(printer.getName(),alias));
		}

		Button saveBtn = new Button("Guardar configuración");
		saveBtn.setOnAction(e->saveAll(table,stage));
		VBox root = new VBox(10,table,saveBtn);
		root.setPadding(new Insets(15));
		stage.setScene(new Scene(root,600,400));
		stage.setTitle("configuracion de impresoras");
		stage.show();
	}
	
	private static void saveAll(TableView<PrinterRow> table, Stage stage) {

		JSONObject json = new JSONObject();

		for (PrinterRow row : table.getItems()) {
			if (row.getAlias() != null && !row.getAlias().isBlank()) {
				json.put(row.getAlias(), row.getPrinterName());
			}
		}

		if (json.isEmpty()) {
			showAlert("Advertencia", "No se ingresó ningún alias.");
			return;
		}

		try {
			Files.createDirectories(PRINTER_FILE.getParent());
			Files.writeString(PRINTER_FILE, json.toString(2));
			showAlert("Éxito", "Configuración guardada correctamente.");
			stage.close();
		} catch (IOException e) {
			e.printStackTrace();
			showAlert("Error", "No se pudo guardar la configuración.");
		}
	}
	private static Map<String, String> loadAliases() {
		Map<String, String> map = new HashMap<>();

		try {
			if (!Files.exists(PRINTER_FILE)) {
				return map;
			}

			JSONObject json = new JSONObject(Files.readString(PRINTER_FILE));
			for (String key : json.keySet()) {
				map.put(key, json.getString(key));
			}

		} catch (Exception e) {
			e.printStackTrace();
		}

		return map;
	}


	public static PrintService getPrinterByAlias(String alias) {
		try {
			if (!Files.exists(PRINTER_FILE)) {
				return null;
			}

			JSONObject json = new JSONObject(Files.readString(PRINTER_FILE));
			String printerName = json.optString(alias, null);

			if (printerName == null) {
				return null;
			}

			for (PrintService ps : PrintServiceLookup.lookupPrintServices(null, null)) {
				if (ps.getName().equalsIgnoreCase(printerName)) {
					return ps;
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	private static void showAlert(String title, String message) {
		Alert alert = new Alert(Alert.AlertType.INFORMATION);
		alert.setTitle(title);
		alert.setHeaderText(null);
		alert.setContentText(message);
		alert.showAndWait();
	}
	
	public static void printPdf(byte[] pdfBytes, PrintService printer)
		throws PrintException {

		DocFlavor flavor = DocFlavor.BYTE_ARRAY.PDF;
		DocPrintJob job = printer.createPrintJob();

		PrintRequestAttributeSet attrs = new HashPrintRequestAttributeSet();
		attrs.add(new Copies(1));
		attrs.add(MediaSizeName.NA_LETTER);

		Doc doc = new SimpleDoc(pdfBytes, flavor, null);
		job.print(doc, attrs);
	}


	
	public static void printPdfAsEscPosRaster(byte[] pdfBytes, PrintService printer) throws IOException {
		int printerDotsWidth = 384; 
		float renderDpi = 203f;     

		printPdfAsEscPosRaster(pdfBytes, printer, printerDotsWidth, renderDpi);
	}

	public static void printPdfAsEscPosRaster(byte[] pdfBytes, PrintService printer, int printerDotsWidth, float renderDpi)
		throws IOException {

		if (printer == null) {
			throw new IllegalArgumentException("PrintService es null.");
		}
		if (pdfBytes == null || pdfBytes.length < 4) {
			throw new IllegalArgumentException("pdfBytes vacio.");
		}
		String header = new String(pdfBytes, 0, Math.min(8, pdfBytes.length));
		if (!header.startsWith("%PDF")) {
			throw new IOException("El contenido no (%PDF).");
		}
		try (PDDocument doc = org.apache.pdfbox.Loader.loadPDF(pdfBytes); PrinterOutputStream posOut = new PrinterOutputStream(printer); EscPos escpos = new EscPos(posOut)) {
			PDFRenderer renderer = new PDFRenderer(doc);
			RasterBitImageWrapper wrapper = new RasterBitImageWrapper();
			wrapper.setJustification(EscPosConst.Justification.Center);
			BitonalThreshold algorithm = new BitonalThreshold();
			for (int page = 0; page < doc.getNumberOfPages(); page++) {
				BufferedImage img = renderer.renderImageWithDPI(page, renderDpi, ImageType.RGB);
				img = trimBottomWhite(img, 250,120);
				EscPosImage escposImage = new EscPosImage(new CoffeeImageImpl(img), algorithm);
				escpos.write(wrapper, escposImage);
				escpos.feed(2);
			}

			escpos.cut(EscPos.CutMode.FULL);
		}
	}



	private static BufferedImage trimBottomWhite(
		BufferedImage img,
		int whiteThreshold,
		int bottomPaddingPx 
	) {
		int width = img.getWidth();
		int height = img.getHeight();

		int lastY = height - 1;

		outer:
		for (; lastY >= 0; lastY--) {
			for (int x = 0; x < width; x++) {
				int rgb = img.getRGB(x, lastY);

				int r = (rgb >> 16) & 0xFF;
				int g = (rgb >> 8) & 0xFF;
				int b = rgb & 0xFF;

				if (r < whiteThreshold || g < whiteThreshold || b < whiteThreshold) {
					break outer;
				}
			}
		}

		lastY = Math.min(height - 1, lastY + bottomPaddingPx);

		if (lastY < 10) {
			return img;
		}

		return img.getSubimage(0, 0, width, lastY + 1);
	}



}

