"""
Printer comprehensive test script — Jolimark TP850UE
Usage:  python print_test.py [IP] [PORT]
Default: 10.98.20.25:9100

Prints 6 pages:
  Page 1 — ESC ! modes, Font A, English
  Page 2 — ESC ! modes, Font B, English
  Page 3 — ESC ! modes, Font A, Greek
  Page 4 — ESC ! modes, Font B, Greek
  Page 5 — GS ! character size multipliers (both fonts)
  Page 6 — Beep tests + misc (underline, invert, symbols)

ESC ! (0x1B 0x21 n) correct bit map for TP850UE:
  Bit 0 (0x01) — Font B instead of Font A
  Bit 3 (0x08) — Emphasize / Bold
  Bit 4 (0x10) — Double-height
  Bit 5 (0x20) — Double-width
  Bit 7 (0x80) — Underline

GS ! (0x1D 0x21 n) character size multiplier:
  Low  nibble (bits 0-3): height multiplier  (0=1x, 1=2x, 2=3x … 7=8x)
  High nibble (bits 4-7): width  multiplier  (0=1x, 1=2x, 2=3x … 7=8x)
  e.g. n=0x00 → 1×1,  n=0x11 → 2×2,  n=0x22 → 3×3,  n=0x77 → 8×8
"""
import sys
import time

PRINTER_IP   = sys.argv[1] if len(sys.argv) > 1 else "10.98.20.25"
PRINTER_PORT = int(sys.argv[2]) if len(sys.argv) > 2 else 9100

from escpos.printer import Network


# ── Low-level helpers ──────────────────────────────────────────────────────────

def _gr(text: str) -> bytes:
    return text.encode('cp737', errors='replace')

def _open():
    p = Network(PRINTER_IP, PRINTER_PORT, timeout=10)
    p._raw(b'\x1b\x40')       # ESC @ — full reset
    p._raw(b'\x1b\x74\x1d')   # ESC t 29 — CP737 Greek code page
    return p

def _t(p, text: str):
    p._raw(_gr(text))

def _reset(p):
    """Reset to: Font A, normal size, no bold, left-align."""
    p._raw(b'\x1b\x4d\x00')   # ESC M 0 — Font A
    p._raw(b'\x1b\x21\x00')   # ESC ! 0 — normal
    p._raw(b'\x1d\x21\x00')   # GS  ! 0 — 1×1 size
    p._raw(b'\x1b\x45\x00')   # ESC E 0 — bold off
    p._raw(b'\x1b\x61\x00')   # ESC a 0 — left align

def _center(p): p._raw(b'\x1b\x61\x01')
def _left(p):   p._raw(b'\x1b\x61\x00')

def _divider(p, char="-", width=48):
    _left(p)
    _t(p, char * width + "\n")

def _page_header(p, title: str):
    _center(p)
    p._raw(b'\x1b\x21\x28')   # double-width + bold (bits 3+5 = 0x28)
    _t(p, title + "\n")
    _reset(p)
    _divider(p, "=")


# ── ESC ! mode table ───────────────────────────────────────────────────────────
#
# Each entry: (esc_bang_byte, esc_e_bold, label)
# esc_bang_byte sets the mode via ESC ! n
# esc_e_bold adds ESC E on top (independent bold layer)
# We test every useful combination so you can see the exact visual result.

ESC_BANG_MODES = [
    # (byte, extra_bold, label)
    (0x00, False, "0x00        Normal"),
    (0x00, True,  "0x00 +ESC E Normal + Bold (ESC E)"),
    (0x08, False, "0x08        Bold only (bit3)"),
    (0x10, False, "0x10        Double-height (bit4)"),
    (0x10, True,  "0x10 +ESC E Double-height + Bold"),
    (0x18, False, "0x18        Double-height + Bold (bits 3+4)"),
    (0x20, False, "0x20        Double-width (bit5)"),
    (0x20, True,  "0x20 +ESC E Double-width + Bold"),
    (0x28, False, "0x28        Double-width + Bold (bits 3+5)"),
    (0x30, False, "0x30        Double-width + Double-height (bits 4+5)"),
    (0x38, False, "0x38        Double-width + Double-height + Bold (bits 3+4+5)"),
]


def _esc_bang_section(p, english: bool):
    lang = "EN" if english else "GR"
    sample_normal = "TEST PRINT  Hello 123" if english else "ΔΟΚΙΜΗ ΕΚΤΥΠΩΣΗΣ"
    sample_lower  = "test print  hello 123" if english else "δοκιμη εκτυπωσης"

    for (byte_val, extra_bold, label) in ESC_BANG_MODES:
        _left(p)
        # Print the label in small normal text first
        p._raw(b'\x1b\x21\x00')
        p._raw(b'\x1b\x45\x00')
        _t(p, f"[{label}]\n")

        # Apply mode
        p._raw(bytes([0x1b, 0x21, byte_val]))
        if extra_bold:
            p._raw(b'\x1b\x45\x01')

        _t(p, sample_normal + "\n")
        _t(p, sample_lower  + "\n")

        # Reset
        _reset(p)
        _t(p, "\n")

    _divider(p)


# ── Pages 1–4: ESC ! modes ────────────────────────────────────────────────────

def page_esc_bang(font_b: bool, english: bool):
    font_label = "Font B (8x16 small)" if font_b else "Font A (12x24 standard)"
    lang_label = "GREEK" if not english else "ENGLISH"
    p = _open()

    # Select font
    p._raw(b'\x1b\x4d\x01' if font_b else b'\x1b\x4d\x00')

    _page_header(p, f"ESC! MODES — {lang_label} — {font_label[:6]}")
    _t(p, f"Font: {font_label}\n")
    _divider(p)

    _esc_bang_section(p, english)

    p._raw(b'\n\n\n')
    p.cut()
    p.close()


# ── Page 5: GS ! size multipliers ─────────────────────────────────────────────

# Combinations worth seeing: square multipliers + some asymmetric
GS_SIZES = [
    (0x00, "1x1  normal"),
    (0x01, "1w x 2h"),
    (0x10, "2w x 1h"),
    (0x11, "2x2"),
    (0x22, "3x3"),
    (0x33, "4x4"),
    (0x44, "5x5"),
    (0x55, "6x6"),
    (0x02, "1w x 3h"),
    (0x20, "3w x 1h"),
    (0x21, "3w x 2h"),
    (0x12, "2w x 3h"),
]

def page_gs_sizes():
    p = _open()
    _page_header(p, "GS! SIZE MULTIPLIERS")
    _t(p, "GS ! n  (0x1D 0x21 n)\n")
    _t(p, "Low nibble=height, High nibble=width\n")
    _divider(p)

    for (byte_val, label) in GS_SIZES:
        _left(p)
        # Label in tiny normal text
        p._raw(b'\x1b\x21\x00')
        p._raw(b'\x1d\x21\x00')
        _t(p, f"[n=0x{byte_val:02X}  {label}]\n")

        # Font A sample
        p._raw(b'\x1b\x4d\x00')
        p._raw(bytes([0x1d, 0x21, byte_val]))
        _t(p, "Aa SAMPLE\n")
        p._raw(b'\x1d\x21\x00')

        # Font B sample on same size
        p._raw(b'\x1b\x4d\x01')
        p._raw(bytes([0x1d, 0x21, byte_val]))
        _t(p, "Bb SMALL\n")
        p._raw(b'\x1d\x21\x00')
        p._raw(b'\x1b\x4d\x00')  # back to Font A

        _t(p, "\n")

    _divider(p)

    # Also show GS ! combined with ESC ! bold
    _t(p, "\n")
    _divider(p, "=")
    p._raw(b'\x1b\x21\x00')
    _t(p, "GS! + ESC E bold combined:\n")
    _divider(p, "=")
    for (byte_val, label) in [(0x11,"2x2"), (0x22,"3x3"), (0x33,"4x4")]:
        p._raw(b'\x1b\x21\x00')
        p._raw(b'\x1d\x21\x00')
        _t(p, f"[{label} + bold]\n")
        p._raw(b'\x1b\x45\x01')
        p._raw(bytes([0x1d, 0x21, byte_val]))
        _t(p, "BOLD LARGE\n")
        p._raw(b'\x1b\x45\x00')
        p._raw(b'\x1d\x21\x00')
        _t(p, "\n")

    p._raw(b'\n\n\n')
    p.cut()
    p.close()


# ── Page 6: Beep + misc ────────────────────────────────────────────────────────

def page_beep_misc():
    p = _open()
    _page_header(p, "BEEP + MISC TESTS")

    # ── Beep section ──
    _t(p, "BEEP TESTS\n")
    _divider(p, "-")
    _t(p, "Sending beeps now...\n\n")

    # BEL — single beep (0x07)
    _t(p, "[1] BEL single beep (0x07)\n")
    p._raw(b'\x07')
    time.sleep(0.5)

    # ESC BEL n1 n2 n3 — beep for appointment
    # n1=beep length (100ms units), n2=intermission (100ms), n3=count
    _t(p, "[2] ESC BEL: 1 beep, 200ms long\n")
    p._raw(bytes([0x1b, 0x07, 2, 2, 1]))   # 200ms on, 200ms off, 1 beep
    time.sleep(0.8)

    _t(p, "[3] ESC BEL: 3 short beeps\n")
    p._raw(bytes([0x1b, 0x07, 1, 1, 3]))   # 100ms on, 100ms off, 3 beeps
    time.sleep(1.5)

    _t(p, "[4] ESC BEL: 1 long beep (500ms)\n")
    p._raw(bytes([0x1b, 0x07, 5, 2, 1]))   # 500ms on, 200ms off, 1 beep
    time.sleep(1.2)

    _t(p, "[5] GS BEL: 2 beeps\n")
    p._raw(bytes([0x1d, 0x07, 2, 3, 2]))   # 2 beeps, 300ms long, 200ms off
    time.sleep(1.5)

    _t(p, "Beep tests done.\n")
    _divider(p)

    # ── Underline ──
    _t(p, "\nUNDERLINE\n")
    _divider(p, "-")
    for ul in [1, 2]:
        p._raw(bytes([0x1b, 0x2d, ul]))
        _t(p, f"Underline mode {ul}: Hello World 123\n")
        p._raw(b'\x1b\x2d\x00')
    _t(p, "\n")
    _divider(p)

    # ── White-on-black invert ──
    _t(p, "\nWHITE-ON-BLACK (GS B)\n")
    _divider(p, "-")
    p._raw(b'\x1d\x42\x01')
    _t(p, " INVERTED NORMAL \n")
    p._raw(b'\x1d\x21\x11')   # 2x2 inverted
    _t(p, " INVERTED 2x2 \n")
    p._raw(b'\x1d\x21\x00')
    p._raw(b'\x1d\x42\x00')
    _t(p, "Normal after invert\n")
    _divider(p)

    # ── 90-degree rotation ──
    _t(p, "\n90-DEGREE ROTATION (ESC V)\n")
    _divider(p, "-")
    p._raw(b'\x1b\x56\x01')
    _t(p, "ROTATED TEXT\n")
    p._raw(b'\x1b\x56\x00')
    _t(p, "Normal again\n")
    _divider(p)

    # ── CP737 useful symbols at normal size ──
    _t(p, "\nUSEFUL CP737 SYMBOLS\n")
    _divider(p, "-")
    symbols = [
        (0xFB, "tick / checkmark"),
        (0xFE, "filled square"),
        (0xF9, "middle dot"),
        (0xFA, "small bullet"),
        (0xF8, "degree"),
        (0xDB, "full block"),
        (0xDC, "lower half block"),
        (0xDF, "upper half block"),
        (0xB0, "light shade"),
        (0xB1, "medium shade"),
        (0xB2, "dark shade"),
        (0xC4, "thin horiz line"),
        (0xCD, "double horiz line"),
        (0xBA, "vertical bar"),
        (0xC9, "top-left corner dbl"),
        (0xBB, "top-right corner dbl"),
        (0xC8, "bot-left corner dbl"),
        (0xBC, "bot-right corner dbl"),
    ]
    for code, desc in symbols:
        p._raw(bytes([code, 0x20, code, 0x20, code, 0x20]))
        _t(p, f"  {desc}\n")

    _divider(p)

    p._raw(b'\n\n\n')
    p.cut()
    p.close()


# ── Main ───────────────────────────────────────────────────────────────────────

def main():
    print(f"Connecting to {PRINTER_IP}:{PRINTER_PORT}")
    print("Printing 6 pages...\n")

    page_esc_bang(font_b=False, english=True)
    print("Page 1 done — ESC! modes, Font A, English")

    page_esc_bang(font_b=True,  english=True)
    print("Page 2 done — ESC! modes, Font B, English")

    page_esc_bang(font_b=False, english=False)
    print("Page 3 done — ESC! modes, Font A, Greek")

    page_esc_bang(font_b=True,  english=False)
    print("Page 4 done — ESC! modes, Font B, Greek")

    page_gs_sizes()
    print("Page 5 done — GS! size multipliers")

    page_beep_misc()
    print("Page 6 done — Beep tests + misc")

    print("\nAll done.")

if __name__ == "__main__":
    main()
