"""
Gemeinsame Hilfsfunktionen fuer DXF-Makros.

Enthaelt die Reihen-Gruppierung fuer Omniflo-Elemente und Layout-Funktionen
fuer die Uebersichts-DXF Erzeugung.
"""

import json
import os

import ezdxf
from ezdxf import bbox as ezdxf_bbox
from ezdxf.addons.importer import Importer


# ---------------------------------------------------------------------------
# Reihen-Gruppen: (Label, Quell-Key, Filter-Funktion)
# ---------------------------------------------------------------------------

ROW_GROUPS = [
    ("Boegen 22.5", "boegen",
     lambda b: b["KurvenWinkel"] == 22.5),
    ("Boegen 45", "boegen",
     lambda b: b["KurvenWinkel"] == 45),
    ("Boegen 67.5", "boegen",
     lambda b: b["KurvenWinkel"] == 67.5),
    ("Boegen 90", "boegen",
     lambda b: b["KurvenWinkel"] == 90),
    ("Boegen 180", "boegen",
     lambda b: b["KurvenWinkel"] == 180),
    ("Weichenkoerper Einzel", "weichen",
     lambda w: w["WeichenTyp"] == "Einzelweiche" and w["KurvenWinkel"] == 22.5),
    ("Weichenkoerper Doppel", "weichen",
     lambda w: w["WeichenTyp"] == "Doppelweiche" and w["KurvenWinkel"] == 22.5),
    ("Weichenkoerper Dreiwege", "weichen",
     lambda w: w["WeichenTyp"] == "Dreiwegeweiche" and w["KurvenWinkel"] == 22.5),
    ("Einzelweiche 45", "weichen",
     lambda w: w["WeichenTyp"] == "Einzelweiche" and w["KurvenWinkel"] == 45),
    ("Einzelweiche 90", "weichen",
     lambda w: w["WeichenTyp"] == "Einzelweiche" and w["KurvenWinkel"] == 90),
    ("Einzelweiche Parallel", "weichen",
     lambda w: w["WeichenTyp"] == "Einzelweiche" and w["KurvenWinkel"] == 0),
    ("Doppelweiche 45", "weichen",
     lambda w: w["WeichenTyp"] == "Doppelweiche" and w["KurvenWinkel"] == 45),
    ("Doppelweiche 90", "weichen",
     lambda w: w["WeichenTyp"] == "Doppelweiche" and w["KurvenWinkel"] == 90),
    ("Doppelweiche Parallel", "weichen",
     lambda w: w["WeichenTyp"] == "Doppelweiche" and w["KurvenWinkel"] == 0),
    ("Dreiwegeweiche 45", "weichen",
     lambda w: w["WeichenTyp"] == "Dreiwegeweiche" and w["KurvenWinkel"] == 45),
    ("Dreiwegeweiche 90", "weichen",
     lambda w: w["WeichenTyp"] == "Dreiwegeweiche" and w["KurvenWinkel"] == 90),
    ("Dreiwegeweiche Parallel", "weichen",
     lambda w: w["WeichenTyp"] == "Dreiwegeweiche" and w["KurvenWinkel"] == 0),
    ("Dreifachweiche", "weichen",
     lambda w: w["WeichenTyp"] == "Dreifachweiche"),
    ("Deltaweiche", "weichen",
     lambda w: w["WeichenTyp"] == "Deltaweiche"),
    ("Sternweiche", "weichen",
     lambda w: w["WeichenTyp"] == "Sternweiche"),
]

# ---------------------------------------------------------------------------
# Schalter-Filter: (Label, JSON-Datei, Filter-Funktion)
# Gemeinsam genutzt von set_einfuegepkt.py und set_koords.py.
# ---------------------------------------------------------------------------

SWITCH_FILTERS = {
    "boegen": (
        "Boegen",
        "omniflo_boegen.json",
        lambda item: True,
    ),
    "weichen45": (
        "Weichen 45 Grad",
        "omniflo_weichen.json",
        lambda item: item["KurvenWinkel"] == 45
        and item["WeichenTyp"] not in ("Dreifachweiche", "Deltaweiche"),
    ),
    "weichen90": (
        "Weichen 90 Grad",
        "omniflo_weichen.json",
        lambda item: item["KurvenWinkel"] == 90
        and item["WeichenTyp"] not in ("Dreifachweiche", "Deltaweiche"),
    ),
    "weichenkoerper": (
        "Weichenkoerper",
        "omniflo_weichen.json",
        lambda item: item["KurvenWinkel"] == 22.5,
    ),
    "weichen_parallel": (
        "Weichen Parallel",
        "omniflo_weichen.json",
        lambda item: item["KurvenWinkel"] == 0 and item["WeichenTyp"] != "Sternweiche",
    ),
    "sternweiche": (
        "Sternweiche",
        "omniflo_weichen.json",
        lambda item: item["WeichenTyp"] == "Sternweiche",
    ),
    "delta": (
        "Deltaweichen",
        "omniflo_weichen.json",
        lambda item: item["WeichenTyp"] == "Deltaweiche",
    ),
    "dreifachweiche": (
        "Dreifachweichen",
        "omniflo_weichen.json",
        lambda item: item["WeichenTyp"] == "Dreifachweiche",
    ),
}


PADDING_X = 200
PADDING_Y = 400
TEXT_HEIGHT = 30
CROSS_SIZE = 40
TEXT_MARGIN = 20
ROW_LABEL_WIDTH = 600


def load_omniflo_data(data_dir):
    """Laedt Boegen- und Weichen-JSON und gibt ein Dict {boegen, weichen} zurueck."""
    boegen_path = os.path.join(data_dir, "json", "omniflo_boegen.json")
    weichen_path = os.path.join(data_dir, "json", "omniflo_weichen.json")

    with open(boegen_path, "r", encoding="utf-8") as f:
        boegen_data = json.load(f)
    with open(weichen_path, "r", encoding="utf-8") as f:
        weichen_data = json.load(f)

    return {"boegen": boegen_data, "weichen": weichen_data}


def is_bogen(sivasnr, sources):
    """Prueft ob eine Sivasnr ein Bogen ist."""
    return any(str(b["Sivasnr"]) == str(sivasnr) for b in sources["boegen"])


def build_row_layout(data_dir, results_dir):
    """
    Berechnet das Layout aller Omniflo-Elemente in Reihen.

    Returns:
        (sources, rows) wobei rows eine Liste von Dicts ist:
        [{
            'label': str,
            'source_key': str,
            'elements': [{'sivasnr', 'source', 'extmin', 'extmax',
                          'width', 'height', 'offset_x', 'offset_y'}],
            'cursor_y': float,
            'max_height': float,
        }]
    """
    sources = load_omniflo_data(data_dir)
    omniflo_dir = os.path.join(data_dir, "omniflo")

    rows = []
    cursor_y = 0.0

    for label, source_key, filter_func in ROW_GROUPS:
        items = [item for item in sources[source_key] if filter_func(item)]
        if not items:
            continue

        row_elements = []
        for item in items:
            sivasnr = str(item["Sivasnr"])
            dxf_path_result = os.path.join(results_dir, f"{sivasnr}.dxf")
            dxf_path_orig = os.path.join(omniflo_dir, f"{sivasnr}.dxf")
            dxf_path = dxf_path_result if os.path.exists(dxf_path_result) else dxf_path_orig

            if not os.path.exists(dxf_path):
                continue

            source_doc = ezdxf.readfile(dxf_path)
            bb = ezdxf_bbox.extents(source_doc.modelspace())
            if not bb.has_data:
                continue

            row_elements.append({
                'sivasnr': sivasnr,
                'source': source_doc,
                'extmin': bb.extmin,
                'extmax': bb.extmax,
                'width': bb.extmax[0] - bb.extmin[0],
                'height': bb.extmax[1] - bb.extmin[1],
            })

        if not row_elements:
            continue

        max_height = max(e['height'] for e in row_elements)

        cursor_x = 0.0
        for elem in row_elements:
            elem['offset_x'] = cursor_x - elem['extmin'][0]
            elem['offset_y'] = cursor_y - elem['extmin'][1]
            cursor_x += elem['width'] + PADDING_X

        rows.append({
            'label': label,
            'source_key': source_key,
            'elements': row_elements,
            'cursor_y': cursor_y,
            'max_height': max_height,
        })

        cursor_y -= max_height + TEXT_HEIGHT + TEXT_MARGIN * 2 + PADDING_Y

    return sources, rows


def import_element_as_block(source_doc, target_doc, block_name):
    """Importiert alle Modelspace-Entities eines Quell-Dokuments als Block."""
    importer = Importer(source_doc, target_doc)
    importer.import_tables()
    blk = target_doc.blocks.new(name=block_name)
    for entity in source_doc.modelspace():
        importer.import_entity(entity, blk)
    importer.finalize()
    return block_name


def draw_cross(msp, x, y, size, color, layer):
    """Zeichnet ein Kreuz an (x, y)."""
    msp.add_line((x - size, y), (x + size, y),
                 dxfattribs={'layer': layer, 'color': color})
    msp.add_line((x, y - size), (x, y + size),
                 dxfattribs={'layer': layer, 'color': color})
