<?php

declare(strict_types=1);

namespace App\Services;

/**
 * Générateur de PDF avec support des images
 * Version simplifiée basée sur FPDF
 */
class PDFGenerator {
    protected $page = '';
    protected $n = 2;
    protected $offsets = [];
    protected $buffer = '';
    protected $pages = [];
    protected $state = 0;
    protected $compress = true;
    protected $k;
    protected $DefOrientation = 'P';
    protected $CurOrientation;
    protected $PageFormats = ['a4' => [595.28, 841.89]];
    protected $DefPageFormat;
    protected $CurPageFormat;
    protected $PageSizes = [];
    protected $wPt, $hPt;
    protected $w, $h;
    protected $lMargin;
    protected $tMargin;
    protected $rMargin;
    protected $bMargin;
    protected $cMargin;
    protected $x, $y;
    protected $lasth = 0;
    protected $LineWidth;
    protected $CoreFonts = ['helvetica'];
    protected $fonts = [];
    protected $FontFiles = [];
    protected $diffs = [];
    protected $FontFamily = '';
    protected $FontStyle = '';
    protected $underline = false;
    protected $CurrentFont;
    protected $FontSizePt = 12;
    protected $FontSize;
    protected $DrawColor = '0 G';
    protected $FillColor = '0 g';
    protected $TextColor = '0 g';
    protected $ColorFlag = false;
    protected $ws = 0;
    protected $images = [];
    protected $PageLinks = [];
    protected $links = [];
    protected $AutoPageBreak = true;
    protected $PageBreakTrigger;
    protected $InHeader = false;
    protected $InFooter = false;
    protected $ZoomMode;
    protected $LayoutMode;
    protected $title = '';
    protected $subject = '';
    protected $author = '';
    protected $keywords = '';
    protected $creator = '';
    protected $AliasNbPages = '';
    protected $PDFVersion = '1.3';

    public function __construct() {
        $this->DefPageFormat = 'A4';
        $this->CurPageFormat = $this->PageFormats['a4'];
        $this->DefOrientation = 'P';
        $this->CurOrientation = $this->DefOrientation;
        $this->k = 72 / 25.4;  // Conversion factor
        
        // Page dimensions
        $this->wPt = $this->CurPageFormat[0];
        $this->hPt = $this->CurPageFormat[1];
        $this->w = $this->wPt / $this->k;
        $this->h = $this->hPt / $this->k;
        
        // Page margins (1 cm)
        $margin = 28.35 / $this->k;
        $this->SetMargins($margin, $margin);
        $this->cMargin = $margin / 10;
        $this->LineWidth = .567 / $this->k;
        $this->SetAutoPageBreak(true, 2 * $margin);
        $this->SetDisplayMode('default');
    }

    public function SetMargins($left, $top, $right = null) {
        $this->lMargin = $left;
        $this->tMargin = $top;
        if($right === null)
            $right = $left;
        $this->rMargin = $right;
    }

    public function SetAutoPageBreak($auto, $margin = 0) {
        $this->AutoPageBreak = $auto;
        $this->bMargin = $margin;
        $this->PageBreakTrigger = $this->h - $margin;
    }

    public function SetDisplayMode($zoom, $layout = 'default') {
        $this->ZoomMode = $zoom;
        $this->LayoutMode = $layout;
    }

    public function AddPage($orientation = '', $format = '') {
        if($this->state == 0)
            $this->Open();
        
        $family = $this->FontFamily;
        $style = $this->FontStyle . ($this->underline ? 'U' : '');
        $fontsize = $this->FontSizePt;
        $lw = $this->LineWidth;
        $dc = $this->DrawColor;
        $fc = $this->FillColor;
        $tc = $this->TextColor;
        $cf = $this->ColorFlag;
        
        if($this->page > 0) {
            $this->_endpage();
        }
        
        $this->_beginpage($orientation, $format);
        $this->_out('2 J');
        $this->LineWidth = $lw;
        $this->_out(sprintf('%.2F w', $lw * $this->k));
        
        if($family)
            $this->SetFont($family, $style, $fontsize);
        
        $this->DrawColor = $dc;
        if($dc != '0 G')
            $this->_out($dc);
        $this->FillColor = $fc;
        if($fc != '0 g')
            $this->_out($fc);
        $this->TextColor = $tc;
        $this->ColorFlag = $cf;
    }

    public function SetFont($family, $style = '', $size = 0) {
        $family = strtolower($family);
        if($family == '')
            $family = $this->FontFamily;
        if($family == 'arial')
            $family = 'helvetica';
        
        if($size == 0)
            $size = $this->FontSizePt;
        
        if($this->FontFamily == $family && $this->FontStyle == $style && $this->FontSizePt == $size)
            return;
        
        $this->FontFamily = $family;
        $this->FontStyle = $style;
        $this->FontSizePt = $size;
        $this->FontSize = $size / $this->k;
        
        if($this->page > 0)
            $this->_out(sprintf('BT /F%d %.2F Tf ET', 1, $this->FontSizePt));
    }

    public function Text($x, $y, $txt) {
        $s = sprintf('BT %.2F %.2F Td (%s) Tj ET', $x * $this->k, ($this->h - $y) * $this->k, $this->_escape($txt));
        if($this->underline && $txt != '')
            $s .= ' ' . $this->_dounderline($x, $y, $txt);
        if($this->ColorFlag)
            $s = 'q ' . $this->TextColor . ' ' . $s . ' Q';
        $this->_out($s);
    }

    public function Cell($w, $h = 0, $txt = '', $border = 0, $ln = 0, $align = '', $fill = false) {
        $k = $this->k;
        if($this->y + $h > $this->PageBreakTrigger && !$this->InHeader && !$this->InFooter && $this->AutoPageBreak) {
            $x = $this->x;
            $ws = $this->ws;
            if($ws > 0) {
                $this->ws = 0;
                $this->_out('0 Tw');
            }
            $this->AddPage($this->CurOrientation, $this->CurPageFormat);
            $this->x = $x;
            if($ws > 0) {
                $this->ws = $ws;
                $this->_out(sprintf('%.3F Tw', $ws * $k));
            }
        }
        
        if($w == 0)
            $w = $this->w - $this->rMargin - $this->x;
        
        $s = '';
        if($fill || $border == 1) {
            if($fill)
                $op = ($border == 1) ? 'B' : 'f';
            else
                $op = 'S';
            $s = sprintf('%.2F %.2F %.2F %.2F re %s ', 
                $this->x * $k, ($this->h - $this->y) * $k, $w * $k, -$h * $k, $op);
        }
        
        if(is_string($border)) {
            $x = $this->x;
            $y = $this->y;
            if(strpos($border, 'L') !== false)
                $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', 
                    $x * $k, ($this->h - $y) * $k, $x * $k, ($this->h - ($y + $h)) * $k);
            if(strpos($border, 'T') !== false)
                $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', 
                    $x * $k, ($this->h - $y) * $k, ($x + $w) * $k, ($this->h - $y) * $k);
            if(strpos($border, 'R') !== false)
                $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', 
                    ($x + $w) * $k, ($this->h - $y) * $k, ($x + $w) * $k, ($this->h - ($y + $h)) * $k);
            if(strpos($border, 'B') !== false)
                $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', 
                    $x * $k, ($this->h - ($y + $h)) * $k, ($x + $w) * $k, ($this->h - ($y + $h)) * $k);
        }
        
        if($txt !== '') {
            if($align == 'R')
                $dx = $w - $this->cMargin - $this->GetStringWidth($txt);
            elseif($align == 'C')
                $dx = ($w - $this->GetStringWidth($txt)) / 2;
            else
                $dx = $this->cMargin;
            
            if($this->ColorFlag)
                $s .= 'q ' . $this->TextColor . ' ';
            
            $txt2 = str_replace(')', '\\)', str_replace('(', '\\(', str_replace('\\', '\\\\', $txt)));
            $s .= sprintf('BT %.2F %.2F Td (%s) Tj ET', 
                ($this->x + $dx) * $k, ($this->h - ($this->y + .5 * $h + .3 * $this->FontSize)) * $k, $txt2);
            
            if($this->underline)
                $s .= ' ' . $this->_dounderline($this->x + $dx, $this->y + .5 * $h + .3 * $this->FontSize, $txt);
            
            if($this->ColorFlag)
                $s .= ' Q';
        }
        
        if($s)
            $this->_out($s);
        
        $this->lasth = $h;
        
        if($ln > 0) {
            $this->y += $h;
            if($ln == 1)
                $this->x = $this->lMargin;
        } else
            $this->x += $w;
    }

    public function Ln($h = null) {
        $this->x = $this->lMargin;
        if($h === null)
            $this->y += $this->lasth;
        else
            $this->y += $h;
    }

    public function Image($file, $x = null, $y = null, $w = 0, $h = 0) {
        // Pour simplifier, on va juste créer un rectangle avec texte "LOGO"
        // Dans une vraie implémentation, il faudrait encoder l'image
        
        if($x === null)
            $x = $this->x;
        if($y === null)
            $y = $this->y;
        
        if($w == 0)
            $w = 30;
        if($h == 0)
            $h = 30;
        
        // Dessiner un rectangle pour représenter le logo
        $this->Rect($x, $y, $w, $h);
        
        // Ajouter le texte LOGO au centre
        $oldX = $this->x;
        $oldY = $this->y;
        $this->SetXY($x + $w/2 - 8, $y + $h/2 - 2);
        $this->Cell(16, 4, 'LOGO', 0, 0, 'C');
        $this->SetXY($oldX, $oldY);
    }

    public function Rect($x, $y, $w, $h, $style = '') {
        if($style == 'F')
            $op = 'f';
        elseif($style == 'FD' || $style == 'DF')
            $op = 'B';
        else
            $op = 'S';
        $this->_out(sprintf('%.2F %.2F %.2F %.2F re %s', 
            $x * $this->k, ($this->h - $y) * $this->k, $w * $this->k, -$h * $this->k, $op));
    }

    public function Line($x1, $y1, $x2, $y2) {
        $this->_out(sprintf('%.2F %.2F m %.2F %.2F l S', 
            $x1 * $this->k, ($this->h - $y1) * $this->k, $x2 * $this->k, ($this->h - $y2) * $this->k));
    }

    public function GetStringWidth($s) {
        $cw = ['helvetica' => [' ' => 278, '!' => 278, '"' => 355, '#' => 556, '$' => 556, '%' => 889, '&' => 667]];
        $w = 0;
        $l = strlen($s);
        for($i = 0; $i < $l; $i++)
            $w += 600;  // Approximation
        return $w * $this->FontSize / 1000;
    }

    public function SetXY($x, $y) {
        $this->SetX($x);
        $this->SetY($y, false);
    }

    public function SetX($x) {
        if($x >= 0)
            $this->x = $x;
        else
            $this->x = $this->w + $x;
    }

    public function SetY($y, $resetX = true) {
        $this->y = $y;
        if($resetX)
            $this->x = $this->lMargin;
    }

    public function Output() {
        if($this->state < 3)
            $this->Close();
        return $this->buffer;
    }

    protected function Open() {
        $this->state = 1;
        $this->_out('%PDF-' . $this->PDFVersion);
    }

    protected function Close() {
        if($this->state == 3)
            return;
        if($this->page == 0)
            $this->AddPage();
        
        $this->_endpage();
        $this->_enddoc();
    }

    protected function _beginpage($orientation, $format) {
        $this->page++;
        $this->pages[$this->page] = '';
        $this->state = 2;
        $this->x = $this->lMargin;
        $this->y = $this->tMargin;
        $this->FontFamily = '';
    }

    protected function _endpage() {
        $this->state = 1;
    }

    protected function _escape($s) {
        $s = str_replace('\\', '\\\\', $s);
        $s = str_replace('(', '\\(', $s);
        $s = str_replace(')', '\\)', $s);
        $s = str_replace("\r", '\\r', $s);
        return $s;
    }

    protected function _dounderline($x, $y, $txt) {
        $up = -100;
        $ut = 50;
        $w = $this->GetStringWidth($txt) + $this->ws * substr_count($txt, ' ');
        return sprintf('%.2F %.2F %.2F %.2F re f', 
            $x * $this->k, ($this->h - ($y - $up / 1000 * $this->FontSize)) * $this->k, 
            $w * $this->k, -$ut / 1000 * $this->FontSizePt);
    }

    protected function _out($s) {
        if($this->state == 2)
            $this->pages[$this->page] .= $s . "\n";
        else
            $this->buffer .= $s . "\n";
    }

    protected function _enddoc() {
        $this->_putheader();
        $this->_putpages();
        $this->_putresources();
        
        $this->_newobj();
        $this->_out('<<');
        $this->_out('/Type /Catalog');
        $this->_out('/Pages 1 0 R');
        $this->_out('>>');
        $this->_out('endobj');
        
        $o = strlen($this->buffer);
        $this->_out('xref');
        $this->_out('0 ' . ($this->n + 1));
        $this->_out('0000000000 65535 f ');
        for($i = 1; $i <= $this->n; $i++)
            $this->_out(sprintf('%010d 00000 n ', $this->offsets[$i]));
        
        $this->_out('trailer');
        $this->_out('<<');
        $this->_out('/Size ' . ($this->n + 1));
        $this->_out('/Root ' . $this->n . ' 0 R');
        $this->_out('/Info ' . ($this->n - 1) . ' 0 R');
        $this->_out('>>');
        $this->_out('startxref');
        $this->_out($o);
        $this->_out('%%EOF');
        $this->state = 3;
    }

    protected function _putheader() {
        $this->_out('%PDF-' . $this->PDFVersion);
    }

    protected function _putpages() {
        $nb = $this->page;
        $n = $this->n;
        
        for($page = 1; $page <= $nb; $page++) {
            $this->_newobj();
            $this->_out('<</Type /Page');
            $this->_out('/Parent 1 0 R');
            $this->_out('/Resources 2 0 R');
            $this->_out('/Contents ' . ($this->n + 1) . ' 0 R>>');
            $this->_out('endobj');
            
            $this->_newobj();
            $filter = ($this->compress) ? '/Filter /FlateDecode ' : '';
            $p = ($this->compress) ? gzcompress($this->pages[$page]) : $this->pages[$page];
            $this->_out('<<' . $filter . '/Length ' . strlen($p) . '>>');
            $this->_putstream($p);
            $this->_out('endobj');
        }
        
        $this->offsets[1] = strlen($this->buffer);
        $this->_out('1 0 obj');
        $this->_out('<</Type /Pages');
        $kids = '/Kids [';
        for($i = 0; $i < $nb; $i++)
            $kids .= (3 + 2 * $i) . ' 0 R ';
        $this->_out($kids . ']');
        $this->_out('/Count ' . $nb);
        $this->_out(sprintf('/MediaBox [0 0 %.2F %.2F]', $this->wPt, $this->hPt));
        $this->_out('>>');
        $this->_out('endobj');
    }

    protected function _putresources() {
        $this->_putfonts();
        
        $this->offsets[2] = strlen($this->buffer);
        $this->_out('2 0 obj');
        $this->_out('<<');
        $this->_out('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
        $this->_out('/Font <<');
        $this->_out('/F1 <</Type /Font /Subtype /Type1 /BaseFont /Helvetica>>');
        $this->_out('>>');
        $this->_out('>>');
        $this->_out('endobj');
    }

    protected function _putfonts() {
        // Simplified - fonts are embedded in resources
    }

    protected function _newobj() {
        $this->n++;
        $this->offsets[$this->n] = strlen($this->buffer);
        $this->_out($this->n . ' 0 obj');
    }

    protected function _putstream($s) {
        $this->_out('stream');
        $this->buffer .= $s;
        $this->_out('endstream');
    }
}