<?php
defined('BASEPATH') or exit('No direct script access allowed');

require_once FCPATH . 'vendor/setasign/fpdf/fpdf.php';

/**
 * FPDF with centered page footer.
 */
class Koha_table_pdf extends FPDF
{
    public function __construct($orientation = 'P', $unit = 'mm', $size = 'A4')
    {
        parent::__construct($orientation, $unit, $size);
        // MultiCell wraps at (w - 2*cMargin). Keep 0 so height calc matches draw width exactly.
        $this->cMargin = 0;
    }

    public function Footer()
    {
        $this->SetY(-10);
        $this->SetFont('Helvetica', '', 9);
        $this->Cell(0, 5, 'Page ' . $this->PageNo() . ' / {nb}', 0, 0, 'C');
    }

    public function pageWidth()
    {
        return $this->w;
    }

    public function pageHeight()
    {
        return $this->h;
    }

    /**
     * Exact line count for MultiCell(width $w, ...) — matches FPDF wrapping
     * (breaks on spaces; splits oversized words character-by-character).
     */
    public function countLines($w, $txt)
    {
        $txt = str_replace("\r", '', (string) $txt);
        if ($w <= 0) {
            return 1;
        }
        if ($txt === '') {
            return 1;
        }

        // Mirror FPDF MultiCell usable width: w - 2*cMargin
        $w = $w - (2 * $this->cMargin);
        if ($w <= 0) {
            return 1;
        }

        $total = 0;
        $paragraphs = explode("\n", $txt);
        foreach ($paragraphs as $paragraph) {
            $total += $this->countLinesOneParagraph($w, $paragraph);
        }
        return max(1, $total);
    }

    protected function countLinesOneParagraph($w, $s)
    {
        if ($s === '') {
            return 1;
        }

        $lines = 1;
        $current = '';
        $len = strlen($s);
        for ($i = 0; $i < $len; $i++) {
            $ch = $s[$i];
            $test = $current . $ch;
            if ($this->GetStringWidth($test) <= $w + 0.01) {
                $current = $test;
                continue;
            }

            if ($ch === ' ') {
                $lines++;
                $current = '';
                continue;
            }

            $space_pos = strrpos($current, ' ');
            if ($space_pos !== false) {
                $lines++;
                $current = ltrim(substr($current, $space_pos + 1) . $ch);
                if ($this->GetStringWidth($current) <= $w + 0.01) {
                    continue;
                }
                // Still too wide — keep character-wrapping the remainder
                $overflow = $current;
                $current = '';
                $olen = strlen($overflow);
                for ($j = 0; $j < $olen; $j++) {
                    $och = $overflow[$j];
                    if ($current !== '' && $this->GetStringWidth($current . $och) > $w + 0.01) {
                        $lines++;
                        $current = $och;
                    } else {
                        $current .= $och;
                    }
                }
                continue;
            }

            // Character wrap
            $lines++;
            $current = $ch;
        }

        return max(1, $lines);
    }
}

/**
 * Continuous table PDF (FPDF) — rows flow across pages with no wasted space
 * between batches (unlike Dompdf chunk-merge).
 */
class Pdf_table_export
{
    /**
     * @param string $title
     * @param array  $columns label => key
     * @param array  $rows    objects or assoc arrays
     * @param string $filename
     * @param array  $options orientation, paper, today, subtitle, progress_token, progress_rows
     */
    public function stream($title, array $columns, $rows, $filename, array $options = array())
    {
        $rows = is_array($rows) ? $rows : array();
        $total = count($rows);
        $index = 0;
        $this->stream_callback(
            $title,
            $columns,
            function () use (&$rows, &$index, $total) {
                if ($index >= $total) {
                    return null;
                }
                $batch = array_slice($rows, $index, 250);
                $index += count($batch);
                return $batch;
            },
            $filename,
            array_merge($options, array(
                'progress_rows' => isset($options['progress_rows']) ? $options['progress_rows'] : $total,
            ))
        );
    }

    /**
     * @param string   $title
     * @param array    $columns label => key
     * @param callable $next_batch function(): array|null
     * @param string   $filename
     * @param array    $options
     */
    public function stream_callback($title, array $columns, $next_batch, $filename, array $options = array())
    {
        @ini_set('memory_limit', '2048M');
        @set_time_limit(0);

        if (!class_exists('Pdf_progress', false)) {
            require_once APPPATH . 'libraries/Pdf_progress.php';
        }

        $CI =& get_instance();
        $token = '';
        if (!empty($options['progress_token'])) {
            $token = Pdf_progress::sanitize_token($options['progress_token']);
        } else {
            $token = Pdf_progress::sanitize_token((string) $CI->input->post('download_token'));
        }

        $orientation = !empty($options['orientation']) ? strtoupper(substr($options['orientation'], 0, 1)) : 'L';
        if ($orientation !== 'P' && $orientation !== 'L') {
            $orientation = 'L';
        }
        $paper = !empty($options['paper']) ? $options['paper'] : 'A4';
        $today = !empty($options['today']) ? $options['today'] : date('Y-m-d');
        $subtitle = isset($options['subtitle']) ? (string) $options['subtitle'] : '';
        $rows_total = isset($options['progress_rows']) ? (int) $options['progress_rows'] : 0;

        if ($token !== '') {
            Pdf_progress::update($token, 'starting', 0, max(1, $rows_total), array(
                'rows_total' => $rows_total,
                'rows_done' => 0,
                'message' => $rows_total > 0
                    ? ('Preparing continuous PDF for ' . number_format($rows_total) . ' rows…')
                    : 'Preparing continuous PDF…',
            ));
        }

        $pdf = new Koha_table_pdf($orientation, 'mm', $paper);
        $pdf->AliasNbPages();
        $pdf->SetAutoPageBreak(false);
        $pdf->SetMargins(8, 8, 8);
        $pdf->AddPage();

        $page_w = $pdf->pageWidth();
        $page_h = $pdf->pageHeight();
        $margin_l = 8;
        $margin_r = 8;
        $margin_t = 8;
        $margin_b = 12;
        $usable_w = $page_w - $margin_l - $margin_r;

        $labels = array_keys($columns);
        $keys = array_values($columns);
        $col_count = count($keys);
        if ($col_count < 1) {
            $labels = array('Data');
            $keys = array('_empty');
            $col_count = 1;
        }

        $widths = $this->column_widths($labels, $keys, $usable_w);
        $nowrap = array();
        foreach ($labels as $i => $label) {
            $nowrap[$i] = $this->is_nowrap_column($label, $keys[$i]);
        }

        // Fixed professional sizes for all PDF table exports (no per-cell shrinking)
        $header_font = 10;
        $body_font = 9.5;
        $line_h = 4.8; // must match MultiCell line height used when drawing
        $cell_pad_y = 1.2; // top+bottom padding inside each row

        $y = $margin_t;
        $y = $this->draw_brand_header($pdf, $y, $page_w, $margin_l, $usable_w);
        $y = $this->draw_title_block($pdf, $y, $margin_l, $usable_w, $title, $subtitle, $today, $rows_total);
        $y = $this->draw_table_header($pdf, $y, $margin_l, $widths, $labels, $header_font, $line_h, $nowrap, $cell_pad_y);

        $rows_done = 0;
        $last_progress_at = 0;

        while (true) {
            $batch = call_user_func($next_batch);
            if ($batch === null || $batch === false) {
                break;
            }
            if (!is_array($batch) || empty($batch)) {
                continue;
            }

            foreach ($batch as $row) {
                $cells = array();
                foreach ($keys as $key) {
                    $cells[] = $this->cell_text($row, $key);
                }

                $row_h = $this->row_height($pdf, $cells, $widths, $body_font, $line_h, false, $nowrap, $cell_pad_y);
                if ($y + $row_h > $page_h - $margin_b) {
                    $pdf->AddPage();
                    $y = $margin_t;
                    $y = $this->draw_table_header($pdf, $y, $margin_l, $widths, $labels, $header_font, $line_h, $nowrap, $cell_pad_y);
                }

                $y = $this->draw_table_row($pdf, $y, $margin_l, $widths, $cells, $body_font, $line_h, $row_h, $nowrap, $cell_pad_y);
                $rows_done++;

                if ($token !== '' && ($rows_done - $last_progress_at >= 50 || ($rows_total > 0 && $rows_done >= $rows_total))) {
                    $last_progress_at = $rows_done;
                    $total_for_pct = $rows_total > 0 ? $rows_total : max($rows_done, 1);
                    Pdf_progress::update($token, 'rendering', $rows_done, $total_for_pct, array(
                        'rows_total' => $total_for_pct,
                        'rows_done' => $rows_done,
                        'message' => 'Writing PDF row ' . number_format($rows_done)
                            . ($rows_total > 0 ? (' of ' . number_format($rows_total)) : '')
                            . '…',
                    ));
                }
            }

            unset($batch);
            if (function_exists('gc_collect_cycles')) {
                gc_collect_cycles();
            }
        }

        if ($rows_done === 0) {
            $pdf->SetFont('Helvetica', 'I', 10);
            $pdf->SetXY($margin_l, $y + 4);
            $pdf->Cell($usable_w, 6, 'No records', 0, 1, 'L');
            $y += 12;
        }

        $sign_left = isset($options['sign_left']) ? trim((string) $options['sign_left']) : '';
        $sign_right = isset($options['sign_right']) ? trim((string) $options['sign_right']) : '';
        if ($sign_left !== '' || $sign_right !== '') {
            $sign_h = 28;
            if ($y + $sign_h > $page_h - $margin_b) {
                $pdf->AddPage();
                $y = $margin_t;
            }
            $y += 14;
            $half = ($usable_w - 10) / 2;
            $pdf->SetFont('Helvetica', 'B', 10);
            $start_y = $y;
            if ($sign_left !== '') {
                $pdf->SetXY($margin_l, $start_y);
                $pdf->MultiCell($half, 5, $this->pdf_text($sign_left), 0, 'L');
            }
            if ($sign_right !== '') {
                $pdf->SetXY($margin_l + $half + 10, $start_y);
                $pdf->MultiCell($half, 5, $this->pdf_text($sign_right), 0, 'R');
            }
        }

        if ($token !== '') {
            $total_for_pct = $rows_total > 0 ? $rows_total : $rows_done;
            Pdf_progress::update($token, 'done', $total_for_pct, max(1, $total_for_pct), array(
                'rows_total' => $total_for_pct,
                'rows_done' => $rows_done,
                'message' => 'PDF ready — download starting…',
            ));
        }

        $this->send_download_cookie();
        if (stripos($filename, '.pdf') === false) {
            $filename .= '.pdf';
        }
        $filename = str_replace(array('"', "\r", "\n"), '', $filename);
        $pdf->Output('D', $filename);
        exit;
    }

    protected function draw_brand_header(Koha_table_pdf $pdf, $y, $page_w, $margin_l, $usable_w)
    {
        $CI =& get_instance();
        $CI->load->helper('report');
        $logo1 = $CI->session->userdata('logo1');
        $logo2 = $CI->session->userdata('logo2');
        $file1 = report_export_logo_file($logo1, 'uploads/univlogo/KRC_logo.png');
        $file2 = report_export_logo_file($logo2, 'uploads/univlogo/DLRL_logo.png');

        $logo_h = 18;
        $logo_w = 18;
        if ($file1 && !empty($file1['path']) && is_file($file1['path'])) {
            try {
                $pdf->Image($file1['path'], $margin_l, $y, $logo_w, $logo_h);
            } catch (Exception $e) {
                // skip bad logo
            }
        }
        if ($file2 && !empty($file2['path']) && is_file($file2['path'])) {
            try {
                $pdf->Image($file2['path'], $page_w - $margin_l - $logo_w, $y, $logo_w, $logo_h);
            } catch (Exception $e) {
                // skip bad logo
            }
        }

        $pdf->SetFont('Helvetica', 'B', 14);
        $pdf->SetXY($margin_l, $y + 2);
        $pdf->Cell($usable_w, 6, $this->pdf_text('Knowledge Resource Centre (KRC)'), 0, 2, 'C');
        $pdf->SetFont('Helvetica', 'B', 12);
        $pdf->Cell($usable_w, 5, $this->pdf_text('DLRL'), 0, 2, 'C');
        $pdf->SetFont('Helvetica', 'B', 10);
        $pdf->Cell($usable_w, 4.5, $this->pdf_text('Hyderabad'), 0, 2, 'C');

        return $y + $logo_h + 3;
    }

    protected function draw_title_block(Koha_table_pdf $pdf, $y, $margin_l, $usable_w, $title, $subtitle, $today, $rows_total)
    {
        $pdf->SetFont('Helvetica', 'B', 13);
        $pdf->SetXY($margin_l, $y);
        $pdf->Cell($usable_w, 7, $this->pdf_text($title), 0, 2, 'C');
        if ($subtitle !== '') {
            $pdf->SetFont('Helvetica', '', 10);
            $pdf->Cell($usable_w, 5, $this->pdf_text($subtitle), 0, 2, 'C');
        }
        $pdf->SetFont('Helvetica', '', 10);
        $date_label = 'Date: ' . date('d/m/Y', strtotime($today));
        $pdf->Cell($usable_w, 5, $this->pdf_text($date_label), 0, 2, 'R');
        if ($rows_total > 0) {
            $pdf->Cell($usable_w, 5, $this->pdf_text('Total Records : ' . number_format($rows_total)), 0, 2, 'L');
        }
        return $pdf->GetY() + 2;
    }

    protected function draw_table_header(Koha_table_pdf $pdf, $y, $x, array $widths, array $labels, $font_size, $line_h, array $nowrap = array(), $pad_y = 1.2)
    {
        $pdf->SetFont('Helvetica', 'B', $font_size);
        $pdf->SetFillColor(240, 240, 240);
        $cells = array();
        foreach ($labels as $label) {
            $cells[] = $this->pdf_text($label);
        }
        $h = $this->row_height($pdf, $cells, $widths, $font_size, $line_h, true, $nowrap, $pad_y);
        return $this->draw_row_cells($pdf, $x, $y, $widths, $cells, $h, $line_h, true, $font_size, $nowrap, $pad_y);
    }

    protected function draw_table_row(Koha_table_pdf $pdf, $y, $x, array $widths, array $cells, $font_size, $line_h, $row_h, array $nowrap = array(), $pad_y = 1.2)
    {
        $pdf->SetFont('Helvetica', '', $font_size);
        $pdf->SetFillColor(255, 255, 255);
        return $this->draw_row_cells($pdf, $x, $y, $widths, $cells, $row_h, $line_h, false, $font_size, $nowrap, $pad_y);
    }

    /**
     * Draw cells. Nowrap columns (S.No) stay on one line; wrapped text stays inside row height.
     */
    protected function draw_row_cells(Koha_table_pdf $pdf, $x, $y, array $widths, array $cells, $row_h, $line_h, $fill, $base_font, array $nowrap = array(), $pad_y = 1.2)
    {
        $col_x = $x;
        $bold = $fill ? 'B' : '';
        $pdf->SetFont('Helvetica', $bold, $base_font);
        $text_top = $y + ($pad_y / 2);

        foreach ($cells as $i => $text) {
            $w = $widths[$i];
            $pdf->Rect($col_x, $y, $w, $row_h, $fill ? 'DF' : 'D');
            $is_nowrap = !empty($nowrap[$i]);
            $inner_w = max(1.0, $w - 1.2);

            if ($is_nowrap) {
                $pdf->SetXY($col_x, $y);
                $pdf->Cell($w, $row_h, $text, 0, 0, 'C');
            } else {
                // Clip context: MultiCell must use same width as countLines()
                $pdf->SetXY($col_x + 0.6, $text_top);
                $pdf->MultiCell($inner_w, $line_h, $text, 0, 'L');
            }
            $col_x += $w;
        }
        return $y + $row_h;
    }

    protected function row_height(Koha_table_pdf $pdf, array $cells, array $widths, $font_size, $line_h, $bold = false, array $nowrap = array(), $pad_y = 1.2)
    {
        $style = $bold ? 'B' : '';
        $pdf->SetFont('Helvetica', $style, $font_size);
        $max_lines = 1;
        foreach ($cells as $i => $text) {
            if (!empty($nowrap[$i])) {
                continue;
            }
            $inner_w = max(1.0, $widths[$i] - 1.2);
            $lines = $pdf->countLines($inner_w, $text);
            if ($lines > $max_lines) {
                $max_lines = $lines;
            }
        }
        // Grow row to fit wrapped text (+ vertical padding). Soft cap avoids a single cell taller than a page.
        $max_lines = min($max_lines, 20);
        return ($max_lines * $line_h) + $pad_y;
    }

    protected function is_nowrap_column($label, $key)
    {
        $label = strtolower(trim((string) $label));
        $key = strtolower(trim((string) $key));
        $candidates = array('s.no', 's.no.', 'sno', 's_no', 'sl.no', 'sl no', 'serial');
        if (in_array($label, $candidates, true) || in_array($key, $candidates, true)) {
            return true;
        }
        return ($label === 's.no' || $key === 's_no' || strpos($label, 's.no') === 0);
    }

    protected function column_widths(array $labels, array $keys, $usable_w)
    {
        $n = count($labels);
        $fixed = array_fill(0, $n, 0.0);
        $weights = array_fill(0, $n, 0.0);
        $fixed_sum = 0.0;
        $weight_sum = 0.0;

        for ($i = 0; $i < $n; $i++) {
            $label = (string) $labels[$i];
            $key = isset($keys[$i]) ? (string) $keys[$i] : '';
            $ll = strtolower($label);

            if ($this->is_nowrap_column($label, $key)) {
                // Fixed width so "S.No" / numbers stay on one line at fixed font size
                $fixed[$i] = 14.0;
                $fixed_sum += 14.0;
                continue;
            }

            if (strpos($ll, 'title') !== false || strpos($ll, 'author') !== false) {
                $w = 3.2;
            } elseif (strpos($ll, 'remark') !== false || strpos($ll, 'publisher') !== false || strpos($ll, 'supplier') !== false) {
                $w = 2.2;
            } elseif (strpos($ll, 'year') !== false || $ll === 'ed' || strpos($ll, 'page') !== false) {
                $w = 0.9;
            } elseif (strpos($ll, 'barcode') !== false || strpos($ll, 'acc') !== false) {
                $w = 1.4;
            } else {
                $w = max(1.0, min(2.5, strlen($label) / 6));
            }
            $weights[$i] = $w;
            $weight_sum += $w;
        }

        $flex_w = max(10.0, $usable_w - $fixed_sum);
        $widths = array();
        for ($i = 0; $i < $n; $i++) {
            if ($fixed[$i] > 0) {
                $widths[$i] = $fixed[$i];
            } else {
                $widths[$i] = ($flex_w * $weights[$i]) / max(0.001, $weight_sum);
            }
        }
        return $widths;
    }

    protected function cell_text($row, $key)
    {
        if ($key === '_empty') {
            return '';
        }
        $val = '';
        if (is_object($row)) {
            $val = isset($row->$key) ? $row->$key : '';
        } elseif (is_array($row)) {
            $val = isset($row[$key]) ? $row[$key] : '';
        }
        if (is_array($val)) {
            $val = implode(', ', $val);
        }
        return $this->pdf_text($val);
    }

    protected function pdf_text($text)
    {
        $text = (string) $text;
        $text = str_replace(array("\r\n", "\r"), "\n", $text);
        if (function_exists('iconv')) {
            $converted = @iconv('UTF-8', 'ISO-8859-1//TRANSLIT//IGNORE', $text);
            if ($converted !== false) {
                $text = $converted;
            }
        }
        $text = preg_replace('/[^\x09\x0A\x20-\x7E\xA0-\xFF]/', '?', $text);
        return $text;
    }

    protected function send_download_cookie()
    {
        $CI =& get_instance();
        $token = (string) $CI->input->post('download_token');
        if ($token === '') {
            $token = (string) $CI->input->get('download_token');
        }
        $token = preg_replace('/[^a-zA-Z0-9_\-]/', '', $token);
        if ($token === '') {
            return;
        }
        setcookie('kohaDownloadToken', $token, time() + 3600, '/');
    }
}
