import io
import re
import streamlit as st
import numpy as np
from PIL import Image, ImageOps

try:
    import zxingcpp
except ImportError:
    zxingcpp = None

# ================= НАСТРОЙКИ ПЕЧАТИ =================
DPI = 203
LABEL_SIZE_MM = 20
TOTAL_DOTS = int((LABEL_SIZE_MM / 25.4) * DPI)  # 160 пикселей

def parse_cz_mark_to_parts(raw_string):
    """
    1. Очищает от человекочитаемых скобок GS1 (если вставили скопированный текст).
    2. Исправляет русскую раскладку.
    3. Разделяет марку на логические блоки для правильной генерации.
    """
    # 0. Удаление лишнего форматирования и восстановление системных разделителей GS1
    if "(01)" in raw_string and "(21)" in raw_string:
        raw_string = raw_string.replace("(01)", "01", 1)
        raw_string = raw_string.replace("(21)", "21", 1)
        raw_string = raw_string.replace("(91)", "\x1d91", 1)
        raw_string = raw_string.replace("(92)", "\x1d92", 1)
        raw_string = raw_string.replace("(93)", "\x1d93", 1)

    # 1. Защита от русской раскладки аппаратных сканеров
    if re.search(r'[А-Яа-яЁё]', raw_string):
        ru_lower = "йцукенгшщзхъфывапролджэячсмитьбюё"
        en_lower = "qwertyuiop[]asdfghjkl;'zxcvbnm,.`"
        ru_upper = "ЙЦУКЕНГШЩЗХЪФЫВАПРОЛДЖЭЯЧСМИТЬБЮЁ"
        en_upper = "QWERTYUIOP{}ASDFGHJKL:\"ZXCVBNM<>~"
        ru_spec = ".,\"№;:?"
        en_spec = "/?@#$^&"
        
        layout_map = str.maketrans(
            ru_lower + ru_upper + ru_spec,
            en_lower + en_upper + en_spec
        )
        raw_string = raw_string.translate(layout_map)
        
    raw_string = raw_string.replace('<GS>', '\x1d').replace('[GS]', '\x1d')
    
    # Если в строке уже есть разделители, просто бьем по ним
    if '\x1d' in raw_string or '\u001d' in raw_string:
        return raw_string.replace('\u001d', '\x1d').split('\x1d')
        
    if len(raw_string) < 16:
        return [raw_string]
        
    parts = []
    current_part = raw_string[:16] 
    remaining = raw_string[16:]
    
    i = 0
    while i < len(remaining):
        if i + 2 <= len(remaining):
            pair = remaining[i:i+2]
            if pair in ("91", "92", "93"):
                parts.append(current_part)
                current_part = pair
                i += 2
                continue
        
        current_part += remaining[i]
        i += 1
        
    parts.append(current_part)
    return parts

def create_duplicate_label(raw_dm_string):
    parts = parse_cz_mark_to_parts(raw_dm_string)
    
    # Собираем блоки обратно через аппаратный разделитель (GS)
    payload = '\x1d'.join(parts)
    
    if zxingcpp is None:
        raise RuntimeError("Библиотека zxingcpp не установлена.")

    # Генерируем DataMatrix
    barcode = zxingcpp.create_barcode(
        payload, 
        zxingcpp.BarcodeFormat.DataMatrix
    )
    
    img_array = barcode.to_image(scale=1)
    dm_img = Image.fromarray(img_array).convert('L')
    
    img = Image.new('RGB', (TOTAL_DOTS, TOTAL_DOTS), 'white')
    PAD = 16 
    
    # Срезаем белые поля библиотеки для точного центрирования
    inverted = ImageOps.invert(dm_img)
    bbox = inverted.getbbox()
    if bbox: 
        dm_img = dm_img.crop(bbox)
        
    modules_x = dm_img.width
    max_dm_space = TOTAL_DOTS - (PAD * 2)
    scale = max_dm_space // modules_x
    if scale < 1: scale = 1
    
    final_dm_size = modules_x * scale
    final_dm = dm_img.resize((final_dm_size, final_dm_size), Image.NEAREST).convert('RGB')
    
    x_pos = (TOTAL_DOTS - final_dm_size) // 2
    y_pos = (TOTAL_DOTS - final_dm_size) // 2
    img.paste(final_dm, (x_pos, y_pos))

    return img.convert('1')

def run(plugin_info):
    with st.container(border=True):
        tabs = st.tabs(["⌨️ Ручной сканер / Текст", "📸 Камера устройства"])
        
        scanned_code = None
        
        with tabs[0]:
            text_input = st.text_input("Отсканируйте марку или вставьте код:", key="scanned_text").strip()
            if text_input:
                scanned_code = text_input
                
        with tabs[1]:
            st.info("💡 Если открылась фронтальная камера, нажмите на значок круговых стрелочек (смена камеры) в правом верхнем углу.")
            camera_photo = st.camera_input("Сделать снимок штрихкода", key="camera_scan")
            
            if camera_photo:
                if zxingcpp is None:
                    st.error("Библиотека zxingcpp не установлена. Распознавание с камеры недоступно.")
                else:
                    with st.spinner("Анализ изображения..."):
                        img = Image.open(camera_photo)
                        img_array = np.array(img)
                        
                        barcodes = zxingcpp.read_barcodes(img_array)
                        
                        if barcodes:
                            # КРИТИЧНО: берем сырые байты, а не .text, чтобы ZXing не вставлял скобки (01), (21)
                            scanned_code = barcodes[0].bytes.decode('utf-8', errors='ignore')
                            st.success("✓ Штрихкод успешно распознан!")
                        else:
                            st.error("✖ Штрихкод не найден. Попробуйте поднести камеру ближе или улучшить освещение.")

        if scanned_code:
            st.divider()
            with st.spinner("Генерация дубликата..."):
                try:
                    label_img = create_duplicate_label(scanned_code)
                    display_img = label_img.resize((label_img.width * 3, label_img.height * 3), Image.NEAREST)
                    
                    st.success("✓ Идеальный дубликат создан и готов к использованию.")
                    
                    col1, col2 = st.columns([1, 2])
                    with col1:
                        st.image(display_img, caption="Сгенерированная марка")
                        
                    buf = io.BytesIO()
                    label_img.save(buf, format="PNG")
                    
                    with col2:
                        st.text_area("Распознанные данные (внутренний код):", value=scanned_code, disabled=True, height=100)
                        st.download_button(
                            label="📥 Скачать марку (PNG)",
                            data=buf.getvalue(),
                            file_name="duplicate_label.png",
                            mime="image/png",
                            use_container_width=True
                        )
                        
                except Exception as e:
                    st.error(f"✖ Ошибка генерации: {e}")