package stirling.software.SPDF.controller.api;


import java.awt.Color;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

import org.apache.pdfbox.multipdf.LayerUtility;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject;
import org.apache.pdfbox.util.Matrix;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import stirling.software.SPDF.model.api.general.MergeMultiplePagesRequest;
import stirling.software.SPDF.utils.WebResponseUtils;

@RestController
@RequestMapping("/api/v1/general")
@Tag(name = "General", description = "General APIs")
public class MultiPageLayoutController {

	private static final Logger logger = LoggerFactory.getLogger(MultiPageLayoutController.class);

	@PostMapping(value = "/multi-page-layout", consumes = "multipart/form-data")
	@Operation(
	    summary = "Merge multiple pages of a PDF document into a single page",
	    description = "This operation takes an input PDF file and the number of pages to merge into a single sheet in the output PDF file. Input:PDF Output:PDF Type:SISO"
	)
	public ResponseEntity<byte[]> mergeMultiplePagesIntoOne(@ModelAttribute MergeMultiplePagesRequest request)
	        throws IOException {

	    int pagesPerSheet = request.getPagesPerSheet();
	    MultipartFile file = request.getFileInput();
	    boolean addBorder = request.isAddBorder();
	    
	    if (pagesPerSheet != 2 && pagesPerSheet != 3 && pagesPerSheet != (int) Math.sqrt(pagesPerSheet) * Math.sqrt(pagesPerSheet)) {
	        throw new IllegalArgumentException("pagesPerSheet must be 2, 3 or a perfect square");
	    }

	    int cols = pagesPerSheet == 2 || pagesPerSheet == 3 ? pagesPerSheet : (int) Math.sqrt(pagesPerSheet);
	    int rows = pagesPerSheet == 2 || pagesPerSheet == 3 ? 1 : (int) Math.sqrt(pagesPerSheet);

	    PDDocument sourceDocument = PDDocument.load(file.getInputStream());
	    PDDocument newDocument = new PDDocument();
	    PDPage newPage = new PDPage(PDRectangle.A4);
	    newDocument.addPage(newPage);

	    int totalPages = sourceDocument.getNumberOfPages();
	    float cellWidth = newPage.getMediaBox().getWidth() / cols;
	    float cellHeight = newPage.getMediaBox().getHeight() / rows;

	    PDPageContentStream contentStream = new PDPageContentStream(newDocument, newPage, PDPageContentStream.AppendMode.APPEND, true, true);
	    LayerUtility layerUtility = new LayerUtility(newDocument);

	    float borderThickness = 1.5f; // Specify border thickness as required
	    contentStream.setLineWidth(borderThickness);
	    contentStream.setStrokingColor(Color.BLACK);
	    
	    for (int i = 0; i < totalPages; i++) {
	        if (i != 0 && i % pagesPerSheet == 0) {
	            // Close the current content stream and create a new page and content stream
	            contentStream.close();
	            newPage = new PDPage(PDRectangle.A4);
	            newDocument.addPage(newPage);
	            contentStream = new PDPageContentStream(newDocument, newPage, PDPageContentStream.AppendMode.APPEND, true, true);
	        }

	        PDPage sourcePage = sourceDocument.getPage(i);
	        PDRectangle rect = sourcePage.getMediaBox();
	        float scaleWidth = cellWidth / rect.getWidth();
	        float scaleHeight = cellHeight / rect.getHeight();
	        float scale = Math.min(scaleWidth, scaleHeight);

	        int adjustedPageIndex = i % pagesPerSheet;  // This will reset the index for every new page
	        int rowIndex = adjustedPageIndex / cols;
	        int colIndex = adjustedPageIndex % cols;

	        float x = colIndex * cellWidth + (cellWidth - rect.getWidth() * scale) / 2;
	        float y = newPage.getMediaBox().getHeight() - ((rowIndex + 1) * cellHeight - (cellHeight - rect.getHeight() * scale) / 2);

	        contentStream.saveGraphicsState();
	        contentStream.transform(Matrix.getTranslateInstance(x, y));
	        contentStream.transform(Matrix.getScaleInstance(scale, scale));

	        PDFormXObject formXObject = layerUtility.importPageAsForm(sourceDocument, i);
	        contentStream.drawForm(formXObject);

	        contentStream.restoreGraphicsState();
	        
	        if(addBorder) {
		        // Draw border around each page
		        float borderX = colIndex * cellWidth;
		        float borderY = newPage.getMediaBox().getHeight() - (rowIndex + 1) * cellHeight;
		        contentStream.addRect(borderX, borderY, cellWidth, cellHeight);
		        contentStream.stroke();
	        }
	    }


	    contentStream.close(); // Close the final content stream
	    sourceDocument.close();

	    ByteArrayOutputStream baos = new ByteArrayOutputStream();
	    newDocument.save(baos);
	    newDocument.close();

	    byte[] result = baos.toByteArray();
	    return WebResponseUtils.bytesToWebResponse(result, file.getOriginalFilename().replaceFirst("[.][^.]+$", "") + "_layoutChanged.pdf");
	}


}
