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

/**
 * Escpos.php — ESC/POS Generator untuk Printer Thermal 58mm
 * Mendukung template fleksibel dengan input, row2, multiply, spacer
 */
class Escpos {

    const LF  = "\x0A";
    const ESC = "\x1B";
    const GS  = "\x1D";

    const INIT         = "\x1B\x40";
    const CUT_PARTIAL  = "\x1D\x56\x01";
    const ALIGN_LEFT   = "\x1B\x61\x00";
    const ALIGN_CENTER = "\x1B\x61\x01";
    const ALIGN_RIGHT  = "\x1B\x61\x02";
    const BOLD_ON      = "\x1B\x45\x01";
    const BOLD_OFF     = "\x1B\x45\x00";

    const PAPER_COLS = 32;

    private $buffer = '';

    public function init()                { $this->buffer = self::INIT; return $this; }
    public function feed($n = 1)          { $this->buffer .= str_repeat(self::LF, $n); return $this; }
    public function cut()                 { $this->buffer .= self::CUT_PARTIAL; return $this; }
    public function bold($on = TRUE)      { $this->buffer .= $on ? self::BOLD_ON : self::BOLD_OFF; return $this; }

    public function align($a = 'left') {
        $map = ['left' => self::ALIGN_LEFT, 'center' => self::ALIGN_CENTER, 'right' => self::ALIGN_RIGHT];
        $this->buffer .= $map[$a] ?? self::ALIGN_LEFT;
        return $this;
    }

    public function font_size($size = 1) {
        $n = (($size - 1) & 0x07) | ((($size - 1) & 0x07) << 4);
        $this->buffer .= self::GS . '!' . chr($n);
        return $this;
    }

    public function text($str) {
        $this->buffer .= $this->_sanitize($str);
        return $this;
    }

    public function textln($str) { return $this->text($str . self::LF); }

    public function line($char = '-', $w = self::PAPER_COLS) {
        $this->buffer .= str_repeat($char, $w) . self::LF;
        return $this;
    }

    // Teks dua kolom kiri-kanan dalam 1 baris
    public function text_columns($left, $right, $w = self::PAPER_COLS) {
        $left  = $this->_sanitize((string)$left);
        $right = $this->_sanitize((string)$right);
        $rlen  = mb_strlen($right);
        $avail = $w - $rlen;
        $left  = mb_substr($left, 0, max(1, $avail - 1));
        $pad   = $avail - mb_strlen($left);
        $this->buffer .= $left . str_repeat(' ', max(0, $pad)) . $right . self::LF;
        return $this;
    }

    // Format angka sesuai tipe
    private function _format_number($value, $format, $prefix = '', $suffix = '') {
        $num = (float)$value;
        switch ($format) {
            case 'rupiah':
                return 'Rp ' . number_format($num, 0, ',', '.');
            case 'number_sep':
                return $prefix . number_format($num, 0, ',', '.') . $suffix;
            case 'number':
                return $prefix . number_format($num, 0, '.', '') . $suffix;
            default: // plain
                return $prefix . (strpos((string)$num, '.') !== false ? rtrim(rtrim(number_format($num, 2, '.', ''), '0'), '.') : (string)(int)$num) . $suffix;
        }
    }

    /**
     * Render template fleksibel
     * $elements = layout_json array
     * $data     = system placeholders (store_name, date, dll) + custom_fields
     */
    public function render_template(array $elements, array $data = []) {
        $this->init();

        // Kumpulkan semua nilai custom field (input + row2) untuk kalkulasi multiply
        $field_values = [];
        foreach ($data as $k => $v) $field_values[$k] = $v;

        usort($elements, fn($a, $b) => (int)($a['y'] ?? 0) <=> (int)($b['y'] ?? 0));

        foreach ($elements as $el) {
            $type      = $el['element_type'] ?? 'text';
            $align     = $el['align'] ?? 'left';
            $font_size = (int)($el['font_size'] ?? 1);
            $is_bold   = !empty($el['bold']);

            switch ($type) {

                case 'spacer':
                    $this->feed(1);
                    break;

                case 'line':
                    $cols = (int)(self::PAPER_COLS / max(1, $font_size));
                    $this->line('-', $cols);
                    break;

                case 'text':
                    $this->align($align);
                    if ($font_size > 1) $this->font_size($font_size);
                    if ($is_bold) $this->bold(TRUE);

                    $value = $this->_replace_placeholders($el['value'] ?? '', $data);

                    if (($el['label'] ?? '') === 'items') {
                        $this->text($value);
                    } else {
                        $this->textln($value);
                    }

                    if ($is_bold)   $this->bold(FALSE);
                    if ($font_size > 1) $this->font_size(1);
                    break;

                case 'input':
                    // Field nilai dari custom_fields
                    $input_name = $el['input_name'] ?? ($el['label'] ?? '');
                    $raw_value  = $field_values[$input_name] ?? '';
                    $format     = $el['format'] ?? 'plain';
                    $prefix     = $el['prefix'] ?? '';
                    $suffix     = $el['suffix'] ?? '';
                    $disp_label = $el['input_label'] ?? $input_name;
                    $label_w    = (int)($el['label_width'] ?? 0);

                    $input_type = $el['input_type'] ?? 'text';
                    if ($input_type === 'number' && $format !== 'plain') {
                        $formatted = $this->_format_number($raw_value, $format, $prefix, $suffix);
                    } else {
                        $formatted = $prefix . $raw_value . $suffix;
                    }

                    $this->align($align);
                    if ($font_size > 1) $this->font_size($font_size);
                    if ($is_bold) $this->bold(TRUE);

                    if ($label_w > 0 && !empty($disp_label)) {
                        $this->textln(str_pad($disp_label, $label_w) . $formatted);
                    } else {
                        $this->textln(($disp_label ? $disp_label . ': ' : '') . $formatted);
                    }

                    if ($is_bold)   $this->bold(FALSE);
                    if ($font_size > 1) $this->font_size(1);
                    break;

                case 'row2':
                    // Dua kolom dalam 1 baris
                    $col1 = $el['col1'] ?? [];
                    $col2 = $el['col2'] ?? [];

                    $w1 = (int)($col1['width'] ?? 50);
                    $w2 = 100 - $w1;
                    $chars1 = (int)(self::PAPER_COLS * $w1 / 100);
                    $chars2 = self::PAPER_COLS - $chars1;

                    $v1 = $this->_get_col_value($col1, $field_values);
                    $v2 = $this->_get_col_value($col2, $field_values);

                    // Truncate dan pad
                    $v1 = mb_substr((string)$v1, 0, $chars1);
                    $v2 = mb_substr((string)$v2, 0, $chars2);

                    $align1 = $col1['align'] ?? 'left';
                    $align2 = $col2['align'] ?? 'right';

                    if ($align1 === 'left') {
                        $part1 = str_pad($v1, $chars1);
                    } else {
                        $part1 = str_pad($v1, $chars1, ' ', STR_PAD_LEFT);
                    }
                    if ($align2 === 'right') {
                        $part2 = str_pad($v2, $chars2, ' ', STR_PAD_LEFT);
                    } else {
                        $part2 = str_pad($v2, $chars2);
                    }

                    $this->align('left');
                    $this->textln($part1 . $part2);
                    break;

                case 'multiply':
                    $input_a = $el['input_a'] ?? '';
                    $input_b = $el['input_b'] ?? '';
                    $val_a   = (float)($field_values[$input_a] ?? 0);
                    $val_b   = (float)($field_values[$input_b] ?? 0);
                    $result  = $val_a * $val_b;

                    $format  = $el['format'] ?? 'rupiah';
                    $prefix  = $el['prefix'] ?? '';
                    $suffix  = $el['suffix'] ?? '';
                    $disp    = $el['display_label'] ?? 'Total';
                    $label_w = (int)($el['label_width'] ?? 16);
                    $formatted = $this->_format_number($result, $format, $prefix, $suffix);

                    $this->align($align);
                    if ($font_size > 1) $this->font_size($font_size);
                    if ($is_bold) $this->bold(TRUE);

                    if ($align === 'right') {
                        $this->text_columns($disp, $formatted);
                    } else {
                        $this->textln(str_pad($disp, $label_w) . $formatted);
                    }

                    if ($is_bold)   $this->bold(FALSE);
                    if ($font_size > 1) $this->font_size(1);
                    break;
            }
        }

        $this->align('left')->feed(4)->cut();
        return $this;
    }

    private function _get_col_value(array $col, array $fields) {
        $type = $col['type'] ?? 'input';
        if ($type === 'text_static') {
            return $col['value'] ?? '';
        }
        $input_name = $col['input_name'] ?? '';
        $raw        = $fields[$input_name] ?? '';
        $format     = $col['format'] ?? 'plain';
        $prefix     = $col['prefix'] ?? '';
        $suffix     = $col['suffix'] ?? '';
        $input_type = $col['input_type'] ?? 'text';

        if ($input_type === 'number' && $format !== 'plain') {
            return $this->_format_number($raw, $format, $prefix, $suffix);
        }
        return $prefix . $raw . $suffix;
    }

    private function _replace_placeholders($str, array $data) {
        return preg_replace_callback('/\{\{(\w+)\}\}/', function($m) use ($data) {
            return $data[$m[1]] ?? '';
        }, $str);
    }

    public function get_raw()    { return $this->buffer; }
    public function get_base64() { return base64_encode($this->buffer); }

    private function _sanitize($str) {
        $search  = ["\u{2013}", "\u{2014}", "\u{2018}", "\u{2019}", "\u{201C}", "\u{201D}", "\u{2026}"];
        $replace = ['-', '-', "'", "'", '"', '"', '...'];
        $str = str_replace($search, $replace, $str);
        $conv = @iconv('UTF-8', 'CP437//TRANSLIT//IGNORE', $str);
        return ($conv !== FALSE && $conv !== '') ? $conv : preg_replace('/[^\x09\x0A\x0D\x20-\x7E]/', '?', $str);
    }
}
