import io
import re
import base64
import numpy as np
import streamlit as st
import streamlit.components.v1 as components
from PIL import Image, ImageOps

# Надежный C++ движок на сервере
try:
    import zxingcpp
except ImportError:
    zxingcpp = None

# Оригинальный генератор
try:
    from pystrich.datamatrix import DataMatrixEncoder, DataMatrixData, FNC1
except ImportError:
    st.error("Папка pystrich не найдена в корне проекта.")
    DataMatrixEncoder, DataMatrixData, FNC1 = None, None, None

DPI = 203
LABEL_SIZE_MM = 20
TOTAL_DOTS = int((LABEL_SIZE_MM / 25.4) * DPI)

def parse_cz_mark_to_parts(raw_string):
    if "(01)" in raw_string and "(21)" in raw_string:
        raw_string = raw_string.replace("(01)", "01", 1).replace("(21)", "21", 1)
        raw_string = raw_string.replace("(91)", "\x1d91", 1).replace("(92)", "\x1d92", 1).replace("(93)", "\x1d93", 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)
    args = [FNC1]
    for i, part in enumerate(parts):
        args.append(part)
        if i < len(parts) - 1:
            args.append(FNC1)
            
    payload = DataMatrixData(*args, encoding='ascii')
    encoder = DataMatrixEncoder(payload)
    dm_img = encoder.get_pilimage()
    
    img = Image.new('RGB', (TOTAL_DOTS, TOTAL_DOTS), 'white')
    PAD = 16 
    
    gray = dm_img.convert('L')
    inverted = ImageOps.invert(gray)
    bbox = inverted.getbbox()
    if bbox: 
        dm_img = dm_img.crop(bbox)
        
    pixels = dm_img.convert('L').load()
    module_size = next((x for x in range(1, dm_img.width) if pixels[x, 0] != pixels[0, 0]), 1)
            
    modules_x = dm_img.width // module_size
    modules_y = dm_img.height // module_size
    small_dm = dm_img.resize((modules_x, modules_y), Image.NEAREST)
    
    scale = (TOTAL_DOTS - (PAD * 2)) // modules_x
    if scale < 1: scale = 1
    
    final_dm_size = modules_x * scale
    final_dm = small_dm.resize((final_dm_size, final_dm_size), Image.NEAREST)
    
    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):
    st.markdown("""
        <style>
            div[data-testid="stTextInput"]:has(input[aria-label="scanned_hidden"]) {
                display: none; height: 0; margin: 0; padding: 0;
            }
        </style>
    """, unsafe_allow_html=True)

    with st.container(border=True):
        tabs = st.tabs(["⌨️ Текст / Ручной сканер", "📸 Live-Камера (Tap to Scan)"])
        scanned_code = None
        
        # Поле для получения данных (текста марки или Base64 картинки из браузера)
        scanned_raw = st.text_input("scanned_hidden", key="scanned_hidden", label_visibility="collapsed")
        
        with tabs[0]:
            text_input = st.text_input("Отсканируйте марку или вставьте код:", key="scanned_text").strip()
            if text_input:
                scanned_code = text_input
                
        with tabs[1]:
            if scanned_raw:
                # Если пришла картинка с камеры в формате Base64
                if scanned_raw.startswith("data:image"):
                    if zxingcpp is None:
                        st.error("Библиотека zxing-cpp не установлена на сервере.")
                    else:
                        with st.spinner("Анализ снимка сервером..."):
                            try:
                                header, encoded = scanned_raw.split(",", 1)
                                image_data = base64.b64decode(encoded)
                                img = Image.open(io.BytesIO(image_data))
                                img_array = np.array(img)
                                
                                barcodes = zxingcpp.read_barcodes(img_array)
                                if barcodes:
                                    scanned_code = barcodes[0].bytes.decode('utf-8', errors='ignore')
                                    st.success("✓ Штрихкод мгновенно распознан сервером!")
                                else:
                                    st.error("✖ Марка не найдена на снимке. Попробуйте еще раз.")
                            except Exception as e:
                                st.error(f"Ошибка чтения снимка: {e}")
                else:
                    # Если пришел просто текст (на всякий случай)
                    scanned_code = scanned_raw
                    
                if st.button("📷 Сканировать заново", use_container_width=True):
                    st.session_state["scanned_hidden"] = ""
                    st.rerun()
            else:
                st.info("Дождитесь автофокуса и **НАЖМИТЕ НА ВИДЕО**, чтобы сделать снимок.")
                
                # HTML видоискателя: только захват потока и отправка кадра в Python по клику
                components.html("""
                    <div id="video-container" style="width: 100%; height: 400px; background: #000; border-radius: 8px; position: relative; overflow: hidden; cursor: pointer;">
                        <video id="camera-stream" autoplay playsinline muted style="width: 100%; height: 100%; object-fit: cover;"></video>
                        <div id="flash" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: white; opacity: 0; pointer-events: none; transition: opacity 0.1s;"></div>
                        <div id="status" style="position: absolute; top: 5px; left: 5px; color: white; background: rgba(0,0,0,0.5); padding: 4px 8px; border-radius: 4px; font-family: sans-serif; font-size: 12px;">Запуск камеры...</div>
                        <div style="position: absolute; bottom: 15px; left: 50%; transform: translateX(-50%); color: white; background: rgba(0,0,0,0.7); padding: 8px 16px; border-radius: 20px; font-family: sans-serif; text-align: center; pointer-events: none;">👆 ТАПНИТЕ ДЛЯ СКАНИРОВАНИЯ</div>
                    </div>
                    
                    <script type="text/javascript">
                        window.addEventListener('load', function () {
                            const video = document.getElementById('camera-stream');
                            const status = document.getElementById('status');
                            const flash = document.getElementById('flash');
                            const container = document.getElementById('video-container');
                            
                            if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
                                status.innerText = 'WebRTC не поддерживается';
                                return;
                            }

                            navigator.mediaDevices.getUserMedia({
                                video: { 
                                    facingMode: "environment",
                                    width: { ideal: 1920 },
                                    height: { ideal: 1080 },
                                    advanced: [{ focusMode: "continuous" }]
                                },
                                audio: false
                            }).then(function(stream) {
                                video.srcObject = stream;
                                video.onloadedmetadata = function() {
                                    video.play();
                                    status.style.display = 'none';
                                };
                            }).catch(function(err) {
                                navigator.mediaDevices.getUserMedia({ video: { facingMode: "environment" } }).then(function(s) {
                                    video.srcObject = s;
                                    video.play();
                                    status.style.display = 'none';
                                }).catch(e => status.innerText = 'Ошибка камеры');
                            });

                            // По клику на видео делаем снимок в полном разрешении и шлем в Python
                            container.addEventListener('click', () => {
                                if (!video.videoWidth) return;
                                
                                // Эффект вспышки
                                flash.style.opacity = '1';
                                setTimeout(() => flash.style.opacity = '0', 150);
                                status.style.display = 'block';
                                status.innerText = 'Отправка...';
                                
                                const canvas = document.createElement('canvas');
                                canvas.width = video.videoWidth;
                                canvas.height = video.videoHeight;
                                canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);
                                
                                // Получаем Base64 (качество 0.9 для скорости передачи без сильных потерь)
                                const dataUrl = canvas.toDataURL('image/jpeg', 0.9);
                                
                                // Отправляем в скрытое поле Streamlit
                                const parentDoc = window.parent.document;
                                const inputElement = parentDoc.querySelector('input[aria-label="scanned_hidden"]');
                                if (inputElement) {
                                    let nativeSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set;
                                    nativeSetter.call(inputElement, dataUrl);
                                    inputElement.dispatchEvent(new Event('input', { bubbles: true }));
                                }
                            });
                        });
                    </script>
                """, height=420)

        if scanned_code:
            st.divider()
            with st.spinner("Генерация дубликата..."):
                if DataMatrixEncoder is None:
                    st.error("Генерация невозможна: отсутствует локальная папка pystrich.")
                else:
                    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("✓ Создан 100% оригинальный дубликат GS1 DataMatrix.")
                        
                        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}")