import os
import shutil
import re
import hashlib
from PIL import Image
import numpy as np
from collections import deque

FOLDER_ORIGINAL = r'C:\fuentes\proyectos delphi\ICONOS\raul\iconos'
FOLDER_BACKUP = r'C:\fuentes\proyectos delphi\ICONOS\raul\iconos_backup_original'
FOLDER_RESOURCES = r'resources\iconos_secciones'
DFM_PATH = r'src\Data\dmg_Main.dfm'
DFM_BACKUP = r'src\Data\dmg_Main.dfm.bak_icons'

def remove_background(im, threshold=230):
    im = im.convert('RGBA')
    arr = np.array(im).astype(np.float32)
    h, w, _ = arr.shape
    
    # 1. Flood-fill from outer edges to detect the background plate
    visited = np.zeros((h, w), dtype=bool)
    is_bg = np.zeros((h, w), dtype=bool)
    
    queue = deque()
    for y in range(h):
        for x in [0, w-1]:
            queue.append((y, x))
            visited[y, x] = True
    for x in range(w):
        for y in [0, h-1]:
            if not visited[y, x]:
                queue.append((y, x))
                visited[y, x] = True
                
    while queue:
        cy, cx = queue.popleft()
        r, g, b, a = arr[cy, cx]
        
        # Pixels that are transparent or part of the light background plate
        if a < 64 or (r >= threshold and g >= threshold and b >= threshold):
            is_bg[cy, cx] = True
            for dy, dx in [(-1,0), (1,0), (0,-1), (0,1)]:
                ny, nx = cy + dy, cx + dx
                if 0 <= ny < h and 0 <= nx < w and not visited[ny, nx]:
                    visited[ny, nx] = True
                    queue.append((ny, nx))
                    
    # Remove alpha on all background pixels
    arr[is_bg, 3] = 0
    
    # 2. Defringing on adjacent boundary pixels:
    # Blend out the white antialiasing halo so the icon looks crisp on any background
    for y in range(h):
        for x in range(w):
            if arr[y, x, 3] > 0:
                has_bg_neighbor = False
                for dy, dx in [(-1,0), (1,0), (0,-1), (0,1)]:
                    ny, nx = y + dy, x + dx
                    if 0 <= ny < h and 0 <= nx < w and is_bg[ny, nx]:
                        has_bg_neighbor = True
                        break
                if has_bg_neighbor:
                    r, g, b, a = arr[y, x]
                    # If it was blending into white
                    min_c = min(r, g, b)
                    if min_c > 200:
                        whiteness = (min_c - 200.0) / 55.0 # 0.0 to 1.0
                        new_a = a * (1.0 - whiteness * 0.85)
                        arr[y, x, 3] = max(0.0, min(255.0, new_a))
                        
    return Image.fromarray(arr.astype(np.uint8))

def format_hex_dfm(raw_bytes):
    hex_str = raw_bytes.hex().upper()
    lines = []
    # First line: 64 or 66 chars
    for i in range(0, len(hex_str), 66):
        lines.append('              ' + hex_str[i:i+66])
    return '\r\n'.join(lines)

def main():
    print("=== 1. Creando Backup de Iconos y DFM ===")
    if not os.path.exists(FOLDER_BACKUP):
        shutil.copytree(FOLDER_ORIGINAL, FOLDER_BACKUP)
        print(f"Backup de carpeta de iconos creado en: {FOLDER_BACKUP}")
    else:
        print(f"Backup de carpeta de iconos ya existía en: {FOLDER_BACKUP}")
        
    shutil.copyfile(DFM_PATH, DFM_BACKUP)
    print(f"Backup de DFM creado en: {DFM_BACKUP}")

    # Map original folder files by MD5
    print("\n=== 2. Calculando MD5 original de archivos de la carpeta ===")
    orig_file_data = {}
    for f in os.listdir(FOLDER_ORIGINAL):
        if f.lower().endswith('.png'):
            p = os.path.join(FOLDER_ORIGINAL, f)
            with open(p, 'rb') as fp:
                data = fp.read()
            md5 = hashlib.md5(data).hexdigest()
            orig_file_data[f] = {
                'path': p,
                'md5': md5,
                'raw': data
            }
    print(f"Total PNGs leídos: {len(orig_file_data)}")

    # Process each icon and save cleaned version
    print("\n=== 3. Procesando remoción de fondos y defringing ===")
    cleaned_bytes_by_orig_md5 = {}
    for fname, info in orig_file_data.items():
        im = Image.open(info['path'])
        clean_im = remove_background(im)
        # Save back to FOLDER_ORIGINAL
        clean_im.save(info['path'], format='PNG')
        
        # Save to FOLDER_RESOURCES if exists
        res_path = os.path.join(FOLDER_RESOURCES, fname)
        clean_im.save(res_path, format='PNG')
        
        with open(info['path'], 'rb') as fp:
            new_data = fp.read()
        cleaned_bytes_by_orig_md5[info['md5']] = new_data
        
    print(f"Procesados y guardados {len(cleaned_bytes_by_orig_md5)} archivos PNG limpios.")

    # Update dmg_Main.dfm
    print("\n=== 4. Actualizando dmg_Main.dfm ===")
    with open(DFM_PATH, 'r', encoding='latin1') as f:
        dfm_text = f.read()

    col_match = re.search(r'object imgCollection: TImageCollection(.*?)object imgListRibbon:', dfm_text, re.DOTALL)
    assert col_match
    block = col_match.group(1)

    matches = list(re.finditer(r'item\s+Name\s*=\s*\'([^\']*)\'', block))
    print(f"Items encontrados en imgCollection: {len(matches)}")

    updated_count = 0
    # We will build replacement pieces from bottom to top to preserve character offsets
    replacements = [] # (start_idx, end_idx, new_text)

    for i in range(len(matches)):
        start = matches[i].start()
        end = matches[i+1].start() if i+1 < len(matches) else len(block)
        item_str = block[start:end]
        name = matches[i].group(1)
        
        d_start = item_str.find('{')
        d_end = item_str.find('}', d_start)
        assert d_start != -1 and d_end != -1
        
        hex_content = item_str[d_start+1:d_end]
        clean_hex = re.sub(r'\s+', '', hex_content)
        raw_data = bytes.fromhex(clean_hex)
        item_md5 = hashlib.md5(raw_data).hexdigest()
        
        if item_md5 in cleaned_bytes_by_orig_md5:
            new_png = cleaned_bytes_by_orig_md5[item_md5]
            new_hex_formatted = '\r\n' + format_hex_dfm(new_png) + '\r\n            '
            global_start = col_match.start(1) + start + d_start + 1
            global_end = col_match.start(1) + start + d_end
            replacements.append((global_start, global_end, new_hex_formatted, name))
            updated_count += 1

    print(f"Items a actualizar en dmg_Main.dfm: {updated_count}")

    # Apply replacements from end to start
    replacements.sort(key=lambda x: x[0], reverse=True)
    new_dfm_text = dfm_text
    for g_start, g_end, new_content, name in replacements:
        new_dfm_text = new_dfm_text[:g_start] + new_content + new_dfm_text[g_end:]

    with open(DFM_PATH, 'wb') as f:
        f.write(new_dfm_text.encode('latin1'))

    print("dmg_Main.dfm actualizado correctamente con codificación binaria latin1 y CRLF.")

if __name__ == '__main__':
    main()
