"""
ui.py
-----
All Blender UI classes for knot_animation:

  KNOT_UL_List                — playlist UIList
  KNOT_UL_AllowedMaterialsList— generator filter UIList
  draw_knot_properties()      — shared property layout helper (collapsible sections)
  KNOT_PT_Panel               — main N-panel (VIEW_3D > UI > Pr3tz)

Every major block is collapsible via per-item or per-settings ui_show_* flags.
KnotItem flags are per-item so each playlist entry remembers its own state.
"""
from __future__ import annotations

import bpy

from .handler import compute_playlist_duration


# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------

def _section(layout, item, flag: str, label: str, icon: str = 'NONE'):
    """Draw a collapsible box section. Returns (box, is_open)."""
    box = layout.box()
    row = box.row()
    row.prop(item, flag,
             icon='TRIA_DOWN' if getattr(item, flag) else 'TRIA_RIGHT',
             icon_only=True, emboss=False)
    row.label(text=label, icon=icon)
    return box, getattr(item, flag)


def _subsection(layout, item, flag: str, label: str, icon: str = 'NONE'):
    """Like _section but without an outer box — for nesting inside existing boxes."""
    row = layout.row()
    row.prop(item, flag,
             icon='TRIA_DOWN' if getattr(item, flag) else 'TRIA_RIGHT',
             icon_only=True, emboss=False)
    row.label(text=label, icon=icon)
    return getattr(item, flag)


def draw_prop_with_mod(layout, item, prop: str, mod_prop: str):
    """Draw a split row with the base property and its attenuverter (Mod)."""
    split = layout.split(factor=0.6)
    split.prop(item, prop)
    split.prop(item, mod_prop)


# ---------------------------------------------------------------------------
# UILists
# ---------------------------------------------------------------------------

class KNOT_UL_List(bpy.types.UIList):
    def draw_item(self, context, layout, data, item, icon,
                  active_data, active_propname, index):
        layout.label(text=item.name, icon='CURVE_PATH')


class KNOT_UL_AllowedMaterialsList(bpy.types.UIList):
    def draw_item(self, context, layout, data, item, icon,
                  active_data, active_propname, index):
        row = layout.row(align=True)
        row.prop(item, "enabled", text="")
        if item.is_preset:
            row.label(text=f"Preset: {item.name}",   icon='SHADING_RENDERED')
        else:
            row.label(text=f"Material: {item.name}", icon='MATERIAL')


# ---------------------------------------------------------------------------
# Shared property layout helper
# ---------------------------------------------------------------------------

def draw_knot_properties(layout, item) -> None:
    """Draw all KnotItem properties using collapsible per-item sections."""

    # ── Shape ──────────────────────────────────────────────────────────────
    box, open_ = _section(layout, item, "ui_show_shape", "Shape", 'CURVE_PATH')
    if open_:
        box.prop(item, "shape_type")

        if item.shape_type == 'TORUS_KNOT':
            draw_prop_with_mod(box, item, "torus_p", "mod_torus_p")
            draw_prop_with_mod(box, item, "torus_q", "mod_torus_q")
            row = box.row(align=True)
            row.prop(item, "flip_p", toggle=True, icon='ARROW_LEFTRIGHT')
            row.prop(item, "flip_q", toggle=True, icon='ARROW_LEFTRIGHT')
            box.prop(item, "mode")
            if item.mode == 'MAJOR_MINOR':
                draw_prop_with_mod(box, item, "torus_R", "mod_torus_R")
                draw_prop_with_mod(box, item, "torus_r", "mod_torus_r")
            else:
                draw_prop_with_mod(box, item, "torus_eR", "mod_torus_eR")
                draw_prop_with_mod(box, item, "torus_iR", "mod_torus_iR")

        elif item.shape_type == 'MOBIUS':
            draw_prop_with_mod(box, item, "mobius_twists", "mod_mobius_twists")
            draw_prop_with_mod(box, item, "mobius_width", "mod_mobius_width")

        elif item.shape_type == 'LISSAJOUS':
            draw_prop_with_mod(box, item, "liss_kx", "mod_liss_kx")
            draw_prop_with_mod(box, item, "liss_ky", "mod_liss_ky")
            draw_prop_with_mod(box, item, "liss_kz", "mod_liss_kz")
            draw_prop_with_mod(box, item, "liss_amp", "mod_liss_amp")

        elif item.shape_type == 'SPIRAL':
            draw_prop_with_mod(box, item, "spiral_turns", "mod_spiral_turns")
            draw_prop_with_mod(box, item, "spiral_R", "mod_spiral_R")

    # ── Geometry ────────────────────────────────────────────────────────────
    box, open_ = _section(layout, item, "ui_show_geometry", "Geometry", 'MESH_DATA')
    if open_:
        draw_prop_with_mod(box, item, "geo_extrude", "mod_geo_extrude")
        draw_prop_with_mod(box, item, "geo_offset", "mod_geo_offset")
        draw_prop_with_mod(box, item, "geo_bDepth", "mod_geo_bDepth")
        draw_prop_with_mod(box, item, "torus_h", "mod_torus_h")

    # ── Links & Phases ───────────────────────────────────────────────────────
    box, open_ = _section(layout, item, "ui_show_links", "Links & Phases", 'LINKED')
    if open_:
        box.prop(item, "multiple_links")
        col = box.column(align=True)
        row = col.row(align=True)
        row.prop(item, "torus_u")
        row.prop(item, "torus_v")
        row = col.row(align=True)
        row.prop(item, "torus_rP")
        row.prop(item, "torus_sP")

    # ── Animation Rates ──────────────────────────────────────────────────────
    box, open_ = _section(layout, item, "ui_show_anim", "Animation Rates", 'ANIM')
    if open_:
        box.prop(item, "cycle_rate")
        col = box.column(align=True)
        row = col.row(align=True)
        row.prop(item, "spin_phase_rate")
        row.prop(item, "rev_phase_rate")
        row = col.row(align=True)
        row.prop(item, "height_rate")
        row = col.row(align=True)
        row.prop(item, "scale_rate")
        row.prop(item, "scale_amplitude")

    # ── Material ─────────────────────────────────────────────────────────────
    box, open_ = _section(layout, item, "ui_show_material", "Material", 'MATERIAL')
    if open_:
        box.prop(item, "material_mode", expand=True)
        if item.material_mode == 'PRESET':
            box.prop(item, "shader_id")
            col = box.column(align=True)
            col.prop(item, "preset_color")
            row = col.row(align=True)
            row.prop(item, "preset_roughness", slider=True)
            row.prop(item, "preset_metallic",  slider=True)
            col.prop(item, "preset_emission_strength")
        else:
            box.prop(item, "project_material", text="Material")

    # ── Colors ───────────────────────────────────────────────────────────────
    box, open_ = _section(layout, item, "ui_show_colors", "Colors", 'COLOR')
    if open_:
        box.prop(item, "use_colors")
        if item.use_colors:
            row = box.row(align=True)
            row.prop(item, "colorSet")
            row.prop(item, "random_colors")

    # ── Transition ───────────────────────────────────────────────────────────
    box, open_ = _section(layout, item, "ui_show_trans", "Transition", 'MOD_TIME')
    if open_:
        box.prop(item, "transition_frames")
        if item.transition_frames > 0:
            box.prop(item, "transition_easing")


# ---------------------------------------------------------------------------
# Main panel
# ---------------------------------------------------------------------------

class KNOT_PT_Panel(bpy.types.Panel):
    bl_label      = "Pr3tz"
    bl_idname     = "KNOT_PT_panel"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category   = 'Pr3tz'

    def draw(self, context):
        layout = self.layout
        scene  = context.scene
        glob   = scene.knot_globals
        gen    = scene.knot_generator

        # ── Playlist ─────────────────────────────────────────────────────────
        pl_box = layout.box()
        pl_row = pl_box.row()
        pl_row.prop(glob, "ui_show_playlist",
                    icon='TRIA_DOWN' if glob.ui_show_playlist else 'TRIA_RIGHT',
                    icon_only=True, emboss=False)
        pl_row.label(text="Playlist", icon='NLA')
        if glob.ui_show_playlist:
            row = pl_box.row()
            row.template_list("KNOT_UL_List", "", scene, "knot_list",
                              scene, "knot_list_index")
            col = row.column(align=True)
            col.operator("knot.add_item",    text="", icon='ADD')
            col.operator("knot.remove_item", text="", icon='REMOVE')
            col.separator()
            col.operator("knot.move_item", text="", icon='TRIA_UP'
                         ).direction = 'UP'
            col.operator("knot.move_item", text="", icon='TRIA_DOWN'
                         ).direction = 'DOWN'
            pl_box.operator("knot.populate", icon='FILE_REFRESH')

        # ── Selected Knot ─────────────────────────────────────────────────────
        if 0 <= scene.knot_list_index < len(scene.knot_list):
            item = scene.knot_list[scene.knot_list_index]
            ke_box = layout.box()
            ke_row = ke_box.row()
            ke_row.prop(glob, "ui_show_knot_edit",
                        icon='TRIA_DOWN' if glob.ui_show_knot_edit else 'TRIA_RIGHT',
                        icon_only=True, emboss=False)
            ke_row.label(text=f"Knot: {item.name}", icon='CURVE_PATH')
            if glob.ui_show_knot_edit:
                ke_box.prop(item, "name", text="",)
                draw_knot_properties(ke_box, item)
                ke_box.operator("knot.bake_preview", icon='FILE_TICK')

        # ── Global Settings ───────────────────────────────────────────────────
        gb = layout.box()
        gb_row = gb.row()
        gb_row.prop(glob, "ui_show_global",
                    icon='TRIA_DOWN' if glob.ui_show_global else 'TRIA_RIGHT',
                    icon_only=True, emboss=False)
        gb_row.label(text="Global Settings", icon='WORLD')

        if glob.ui_show_global:
            gb.prop(glob, "knot_scale")
            gb.prop(glob, "frames_per_knot")
            
            # Resolutions
            res_box = gb.box()
            res_box.label(text="Resolutions (Preview / Render)", icon='RESTRICT_VIEW_OFF')
            row = res_box.row(align=True)
            row.prop(glob, "preview_resolution", text="Curve")
            row.prop(glob, "render_resolution", text="")
            row = res_box.row(align=True)
            row.prop(glob, "preview_bevel_resolution", text="Bevel")
            row.prop(glob, "render_bevel_resolution", text="")

            # Visual Effects & Mods
            fx_box = gb.box()
            fx_box.label(text="Visual Effects & Tweaks", icon='MODIFIER')
            fx_box.prop(glob, "global_master_thickness", slider=True)
            fx_box.prop(glob, "global_emission_multiplier", slider=True)
            fx_box.prop(glob, "global_hue_shift", slider=True)
            fx_box.prop(glob, "global_turbulence", slider=True)
            fx_box.prop(glob, "camera_shake_amplitude", slider=True)
            fx_box.prop(glob, "auto_turntable_speed")
            
            row = fx_box.row(align=True)
            row.prop(glob, "smooth_shading", toggle=True, icon='SHADING_RENDERED')
            row.prop(glob, "viewport_wireframe", toggle=True, icon='SHADING_WIRE')

            n_knots = len(scene.knot_list)
            if n_knots > 0:
                total_frames = compute_playlist_duration(scene)
                fps  = max(1, scene.render.fps)
                secs = total_frames / fps
                gb.label(
                    text=f"Playlist: {total_frames} fr / {secs:.1f}s  ({n_knots} knots)",
                    icon='TIME')
                row = gb.row(align=True)
                row.operator("knot.fit_timeline", icon='PREVIEW_RANGE', text="Fit Timeline")
                row.operator("knot.fit_playlist",  icon='NLA_PUSHDOWN',  text="Fit Playlist")

            # · Playback sub-section ·
            gb.separator()
            pb_open = _subsection(gb, glob, "ui_show_playback", "Playback", 'PLAY')
            if pb_open:
                pb = gb.column(align=True)
                pb.prop(glob, "global_speed")
                pb.prop(glob, "animation_phase")
                pb.prop(glob, "reactivity_factor", slider=True)
                gb.label(text="↑ Keyframe or drive for reactivity", icon='INFO')
                row = gb.row(align=True)
                row.prop(scene, "frame_end",    text="Total Frames")
                row.prop(scene.render, "fps",   text="FPS")

        # ── Random Generator ──────────────────────────────────────────────────
        gen_box = layout.box()
        gen_hdr = gen_box.row()
        gen_hdr.prop(gen, "ui_show_generator",
                     icon='TRIA_DOWN' if gen.ui_show_generator else 'TRIA_RIGHT',
                     icon_only=True, emboss=False)
        gen_hdr.label(text="Random Generator", icon='GROUP')

        if gen.ui_show_generator:
            gen_box.prop(gen, "num_knots")

            # · Base Knot Defaults ·
            base_box = gen_box.box()
            base_row = base_box.row()
            base_row.prop(gen, "ui_show_base",
                          icon='TRIA_DOWN' if gen.ui_show_base else 'TRIA_RIGHT',
                          icon_only=True, emboss=False)
            base_row.label(text="Base Defaults", icon='PRESET')
            if gen.ui_show_base:
                draw_knot_properties(base_box, gen.base_knot)

            # · Randomise Toggles (collapsible container) ·
            rand_box = gen_box.box()
            rand_hdr = rand_box.row()
            rand_hdr.prop(gen, "ui_show_rand_toggles",
                          icon='TRIA_DOWN' if gen.ui_show_rand_toggles else 'TRIA_RIGHT',
                          icon_only=True, emboss=False)
            rand_hdr.label(text="Randomise Toggles", icon='MODIFIER')

            if gen.ui_show_rand_toggles:

                def draw_rand(prop):
                    row = rand_box.row(align=True)
                    row.prop(gen, f"r_{prop}")
                    if getattr(gen, f"r_{prop}"):
                        row.prop(gen, f"min_{prop}")
                        row.prop(gen, f"max_{prop}")

                def draw_rand_bool(prop):
                    row = rand_box.row(align=True)
                    row.prop(gen, f"r_{prop}")
                    if getattr(gen, f"r_{prop}") and hasattr(gen, f"prob_{prop}"):
                        row.prop(gen, f"prob_{prop}")

                # ·· Shape ··
                s_box = rand_box.box()
                s_row = s_box.row()
                s_row.prop(gen, "ui_show_rand_shape",
                           icon='TRIA_DOWN' if gen.ui_show_rand_shape else 'TRIA_RIGHT',
                           icon_only=True, emboss=False)
                s_row.label(text="Shape", icon='CURVE_PATH')
                if gen.ui_show_rand_shape:
                    s_box.prop(gen, "r_shape_type")
                    draw_rand("torus_p");  draw_rand("torus_q")
                    draw_rand_bool("flip_p"); draw_rand_bool("flip_q")
                    draw_rand_bool("mode")
                    draw_rand("torus_R");  draw_rand("torus_r")
                    draw_rand("torus_eR"); draw_rand("torus_iR")
                    draw_rand("mobius_twists"); draw_rand("mobius_width")
                    draw_rand("liss_kx"); draw_rand("liss_ky")
                    draw_rand("liss_kz"); draw_rand("liss_amp")
                    draw_rand("spiral_turns"); draw_rand("spiral_R")

                # ·· Geometry & Links ··
                g_box = rand_box.box()
                g_row = g_box.row()
                g_row.prop(gen, "ui_show_rand_geo",
                           icon='TRIA_DOWN' if gen.ui_show_rand_geo else 'TRIA_RIGHT',
                           icon_only=True, emboss=False)
                g_row.label(text="Geometry & Links", icon='MESH_DATA')
                if gen.ui_show_rand_geo:
                    draw_rand("geo_extrude"); draw_rand("geo_offset")
                    draw_rand("geo_bDepth"); draw_rand("torus_h")
                    draw_rand_bool("multiple_links")
                    draw_rand("torus_u");  draw_rand("torus_v")
                    draw_rand("torus_rP"); draw_rand("torus_sP")
                    draw_rand("transition_frames")
                    draw_rand_bool("transition_easing")

                # ·· Animation Rates ··
                a_box = rand_box.box()
                a_row = a_box.row()
                a_row.prop(gen, "ui_show_rand_anim",
                           icon='TRIA_DOWN' if gen.ui_show_rand_anim else 'TRIA_RIGHT',
                           icon_only=True, emboss=False)
                a_row.label(text="Animation Rates", icon='ANIM')
                if gen.ui_show_rand_anim:
                    draw_rand("cycle_rate")
                    draw_rand("spin_phase_rate"); draw_rand("rev_phase_rate")
                    draw_rand("height_rate")
                    draw_rand("scale_rate");   draw_rand("scale_amplitude")

                # ·· Material ··
                m_box = rand_box.box()
                m_row = m_box.row()
                m_row.prop(gen, "ui_show_rand_mat",
                           icon='TRIA_DOWN' if gen.ui_show_rand_mat else 'TRIA_RIGHT',
                           icon_only=True, emboss=False)
                m_row.label(text="Material", icon='MATERIAL')
                if gen.ui_show_rand_mat:
                    row = m_box.row(align=True)
                    row.prop(gen, "r_material")
                    if gen.r_material:
                        row.prop(gen, "r_preset_params", text="Rnd Params")
                        filter_box = m_box.box()
                        filter_box.label(text="Allowed Materials & Presets:")
                        row_sync = filter_box.row()
                        row_sync.template_list(
                            "KNOT_UL_AllowedMaterialsList", "",
                            gen, "allowed_materials",
                            gen, "allowed_materials_index",
                            rows=5)
                        sync_col = row_sync.column(align=True)
                        sync_col.operator(
                            "knot.sync_generator_materials",
                            icon='FILE_REFRESH', text=""
                        )
                        if len(gen.allowed_materials) == 0:
                            filter_box.label(
                                text="Click Sync to load project materials & presets",
                                icon='INFO')

            gen_box.operator("knot.generate_random", icon='PLAY')

        # ── Export ────────────────────────────────────────────────────────────
        ex_box = layout.box()
        ex_box.label(text="Export", icon='EXPORT')
        ex_box.label(text="Bake all frames to a standalone .blend file",
                     icon='INFO')
        row = ex_box.row()
        row.scale_y = 1.4
        row.operator("knot.bake_export", icon='RENDER_ANIMATION')
