// SPDX-License-Identifier: AGPL-3.0-or-later
// Copyright (C) 2026 Karim Gabriele Varano
import { useState, useEffect, useRef } from 'react'
import Icon from './components/Icon'
import { BarCombo, BarButton, BAR_H } from './components/BarControls'
import {
  onMessage, notifyReady,
  requestMassstab, setMassstab,
  zoomExtents, zoomSelection, setShowLineweights,
  setMassstabDpi, detectMassstabDpi,
  setView, setDisplayMode,
  toggleOverrides, setOverridesPreset, openOverridesPanel,
  pickLayerCombination, saveLayerCombination,
  deleteLayerCombination, openLayerCombinationsDialog,
  openDossierSettings, openKameraPanel,
  setMasseActive, openMasseSettings,
  openAbout, openCheatsheet, createText, setTextSettings,
  applyTextStyle, saveTextStyle, deleteTextStyle,
  setDarstellung,
  arrangeSelection,
  toggleReferenzlinien,
  toggleOsnap,
  setOsnapMode, toggleGridVisible,
  openProjectSettings,
} from './lib/rhinoBridge'

const PRESETS = [
  { value: 1, label: '1:1' }, { value: 5, label: '1:5' },
  { value: 10, label: '1:10' }, { value: 20, label: '1:20' },
  { value: 25, label: '1:25' }, { value: 50, label: '1:50' },
  { value: 100, label: '1:100' }, { value: 200, label: '1:200' },
  { value: 500, label: '1:500' }, { value: 1000, label: '1:1000' },
]

// Reihe 1: 3D-Ansichten (Top, Iso, Persp) + Kamera-Button
// Reihe 2: 4 Gebaeudeansichten (Norden/Osten/Sueden/Westen) — Buchstaben
//          als Symbol, rotieren mit dossier_north_angle.
const VIEWS_ROW1 = [
  { value: 'Top',         icon: 'crop_landscape', kind: 'icon' },
  { value: 'Iso',         icon: 'view_in_ar',     kind: 'icon' },
  { value: 'Perspective', icon: '3d_rotation',    kind: 'icon' },
]
const VIEWS_ROW2 = [
  { value: 'N', label: 'N', kind: 'letter' },
  { value: 'O', label: 'O', kind: 'letter' },
  { value: 'S', label: 'S', kind: 'letter' },
  { value: 'W', label: 'W', kind: 'letter' },
]

function fmtScale(s) {
  if (s == null) return '—'
  if (s >= 1) return '1:' + (s >= 10 ? s.toFixed(0) : s.toFixed(1))
  return (1 / s).toFixed(2) + ':1'
}

function snapToPreset(s, tol = 0.03) {
  if (s == null) return null
  for (const p of PRESETS) {
    if (Math.abs(s - p.value) / p.value <= tol) return p.value
  }
  return null
}

function parseScale(input) {
  if (!input) return null
  const s = String(input).trim()
  for (const sep of [':', '=', '/']) {
    if (s.includes(sep)) {
      const [a, b] = s.split(sep, 2)
      const pa = parseFloat(a), pb = parseFloat(b)
      if (pa > 0 && pb > 0) return pb / pa
      return null
    }
  }
  const n = parseFloat(s)
  return n > 0 ? n : null
}

// ---------------------------------------------------------------------------
// Vectorworks-inspirierte Bar-Widgets: Icon links, Label/Select Mitte,
// Caret rechts — alle in einem rechteckigen Container, sichtbare Trennung
// zwischen Icon-Kompartiment und Inhalt.

const PILL_H = 20  // alte Pill-Hoehe (Buttons/Chips die nicht migriert sind)

const sep = {
  width: 1, height: 20,
  background: 'var(--border)', flexShrink: 0,
  margin: '0 3px',
}
const groupLabel = {
  fontSize: 9, color: 'var(--text-muted)', textTransform: 'uppercase',
  letterSpacing: '0.08em', fontWeight: 600,
  alignSelf: 'center', whiteSpace: 'nowrap',
  padding: '0 4px',
}
const pillSelect = {
  height: PILL_H, lineHeight: PILL_H + 'px',
  padding: '0 20px 0 10px', boxSizing: 'border-box',
  fontSize: 10,
}

// BarCombo + BarButton + BAR_H jetzt zentral in ./components/BarControls.jsx —
// werden auch in Ebenen/anderen Panels verwendet.

const pillInput = {
  height: PILL_H, lineHeight: PILL_H + 'px',
  padding: '0 8px', boxSizing: 'border-box',
  borderRadius: 999,
  fontSize: 10,
}
const pillChip = {
  height: PILL_H, lineHeight: PILL_H + 'px',
  padding: '0 8px', boxSizing: 'border-box',
  display: 'inline-flex', alignItems: 'center',
  fontSize: 9,
}
const pillIconBtn = {
  width: PILL_H, height: PILL_H,
  borderRadius: '50%', boxSizing: 'border-box',
}

// Tonal button helper (filled when active, outlined when not)
function ToolButton({ active, onClick, icon, label, title, disabled }) {
  return (
    <button
      onClick={onClick}
      disabled={disabled}
      className={active ? 'btn-contained' : 'btn-outlined'}
      style={{ height: PILL_H, padding: '0 8px', boxSizing: 'border-box',
               fontSize: 9,
               opacity: disabled ? 0.4 : 1,
               cursor: disabled ? 'not-allowed' : 'pointer' }}
      title={title}
    >
      {icon && <Icon name={icon} size={12} />}
      {label && <span style={{ fontSize: 9 }}>{label}</span>}
    </button>
  )
}

// ---------------------------------------------------------------------------

export default function OberleisteApp() {
  const [state, setState] = useState({
    viewName: null, parallel: false, scale: null,
    pixelWidth: null, pixelHeight: null, unitSystem: '?',
    dpi: 96, dpiSource: 'default',
    showLineweights: false,
    viewMode: null, displayMode: null, displayModes: [],
    ortho: false, gridSnap: false, osnap: false,
    overridesEnabled: false, overridesCount: 0,
    cmdPrompt: '', cmdOptions: [],
    overridesActivePreset: null, overridesPresets: [],
    layerCombinations: [], layerCombinationActive: null,
    massePresets: [], masseActiveId: null,
    textSettings: { font: 'Helvetica', size: 0.20, bold: false, italic: false,
                     underline: false, align: 'left' },
    textSelectionSettings: null,
    textFonts: [],
    textStyles: [],
    textStyleActiveId: null,
    northAngle: 0,
    lastSetView: null,
  })
  const [appliedScale, setAppliedScale] = useState(null)
  const appliedScaleRef = useRef(null)
  const [draft, setDraft] = useState('')
  const [customMode, setCustomMode] = useState(false)  // Dropdown -> Custom-Input switch
  const customInputRef = useRef(null)
  const [textSizeCustom, setTextSizeCustom] = useState(false)

  useEffect(() => {
    onMessage('STATE', (s) => {
      setState((prev) => ({ ...prev, ...s }))
      // Dropdown spiegelt EXAKT den Backend-appliedScale fuer den aktuellen
      // Viewport. Kein Live-Skala-Fallback — das Dropdown ist statisch und
      // pro Viewport gebunden. Backend gibt null zurueck wenn der aktive
      // Viewport noch keinen gesetzten Massstab hat → Dropdown zeigt "1:?".
      const next = (typeof s?.appliedScale === 'number' && s.appliedScale > 0)
        ? s.appliedScale
        : null
      if (next !== appliedScaleRef.current) {
        setAppliedScale(next)
        appliedScaleRef.current = next
      }
    })
    notifyReady()
    setTimeout(() => requestMassstab(), 50)
  }, [])

  const isPerspective = state.parallel === false
  const scaleVal = state.scale
  const dropdownValue = appliedScale != null ? String(appliedScale) : '__none__'

  const applyDropdown = (val) => {
    if (val === '__none__') return
    if (val === '__custom__') {
      setDraft(appliedScale ? `1:${appliedScale}` : '')
      setCustomMode(true)
      // Nach dem Render: Focus + Selektion fuer schnelles Eintippen.
      setTimeout(() => {
        customInputRef.current?.focus()
        customInputRef.current?.select()
      }, 0)
      return
    }
    const r = parseFloat(val)
    if (r > 0) { setAppliedScale(r); appliedScaleRef.current = r; setMassstab(r) }
  }
  const applyDraft = () => {
    const r = parseScale(draft)
    if (r != null) {
      setAppliedScale(r); appliedScaleRef.current = r; setMassstab(r)
    }
    setDraft('')
    setCustomMode(false)
  }
  const cancelDraft = () => { setDraft(''); setCustomMode(false) }
  const apply100 = () => {
    if (appliedScale && appliedScale > 0) setMassstab(appliedScale)
  }

  // Active-Highlight aus lastSetView (vom Backend getrackt — vermeidet
  // Race-Conditions zwischen ChangeProjection und Viewport-State-Lesen).
  // Fallback wenn noch nie geklickt: Viewport-State raten.
  const matchView = (v) => {
    if (state.lastSetView) return state.lastSetView === v
    const name = (state.viewName || '').toLowerCase()
    if (v === 'Top') return name === 'top'
    if (v === 'Perspective') return state.parallel === false
    if (v === 'Iso') {
      const ortho = ['top', 'front', 'right', 'bottom', 'left', 'back']
      return state.parallel === true && !ortho.includes(name)
    }
    return false
  }

  // (Command-Bar wurde entfernt — Rhinos eigene Command-Line wird benutzt.)

  return (
    <div style={{
      width: '100%', height: '100%',
      display: 'flex', flexDirection: 'column',
      fontFamily: 'var(--font)', color: 'var(--text-primary)',
      background: 'var(--bg-panel)',
      borderBottom: '1px solid var(--border)',
      boxSizing: 'border-box',
      overflow: 'hidden',
    }}>
    {/* === Toolbar (View, Display, Massstab, Snap, Overrides) === */}
    <div style={{
      display: 'flex', alignItems: 'center', gap: 6,
      padding: '4px 10px 6px',
      overflowX: 'auto', overflowY: 'hidden',
      flexShrink: 0,
    }}>
      {/* Logo: DOSSIER. + Version darunter (Klick = Cheatsheet, Shift+Klick = About) */}
      <button
        onClick={(e) => e.shiftKey ? openAbout() : openCheatsheet()}
        title="Shortcuts (Shift+Klick = Über Dossier)"
        style={{
          display: 'flex', flexDirection: 'column',
          alignItems: 'flex-start', gap: 0,
          flexShrink: 0, userSelect: 'none',
          background: 'transparent', border: 'none', padding: 0,
          cursor: 'pointer', color: 'inherit',
        }}
      >
        <span style={{
          fontFamily: "Krungthep, 'Archivo Black', sans-serif",
          fontSize: 17,
          letterSpacing: '-0.02em',
          color: 'var(--text-primary)',
          lineHeight: 1,
        }}>
          DOSSIER<span style={{ color: 'var(--accent)' }}>.</span>
        </span>
        <span style={{
          fontFamily: 'DM Mono, monospace',
          fontSize: 8, letterSpacing: '0.05em',
          color: 'var(--text-muted)', lineHeight: 1.4, marginTop: 2,
        }}>
          v{__APP_VERSION__}
        </span>
      </button>
      {/* Settings-Icons: Launcher + Projekt — vertikal gestapelt */}
      <div style={{ display: 'flex', flexDirection: 'column', gap: 1,
                     flexShrink: 0 }}>
        <button
          onClick={() => openDossierSettings()}
          title="Dossier-Einstellungen (App-Settings, Window-Layout)"
          style={{
            background: 'transparent', border: 'none', padding: '1px 4px',
            cursor: 'pointer', color: 'var(--text-muted)',
            display: 'flex', alignItems: 'center',
          }}
          onMouseEnter={(e) => e.currentTarget.style.color = 'var(--text-primary)'}
          onMouseLeave={(e) => e.currentTarget.style.color = 'var(--text-muted)'}
        >
          <Icon name="settings" size={12} />
        </button>
        <button
          onClick={() => openProjectSettings()}
          title="Projekt-Einstellungen (Voreinstellungen Geschoss/Schnitt + Material-Library)"
          style={{
            background: 'transparent', border: 'none', padding: '1px 4px',
            cursor: 'pointer', color: 'var(--text-muted)',
            display: 'flex', alignItems: 'center',
          }}
          onMouseEnter={(e) => e.currentTarget.style.color = 'var(--text-primary)'}
          onMouseLeave={(e) => e.currentTarget.style.color = 'var(--text-muted)'}
        >
          <Icon name="tune" size={12} />
        </button>
      </div>
      <div style={sep} />
      {/* ====== VIEW 2x4 Grid ======
          Reihe 1: TOP / ISO / PERSP / 📷 (Kamera-Settings)
          Reihe 2: N / O / S / W (rotieren mit dossier_north_angle)
      */}
      {(() => {
        const VIEW_W = 140  // konsistent mit Massstab-Pills
        const CELL_W = Math.floor(VIEW_W / 4)
        const cellStyle = (isActive, isFirst) => ({
          height: BAR_H, minHeight: BAR_H, maxHeight: BAR_H,
          width: CELL_W,
          background: isActive ? 'var(--accent)' : 'var(--bg-input)',
          color: isActive ? 'var(--bg-panel)' : 'var(--text-primary)',
          border: 'none',
          borderLeft: isFirst ? 'none' : '1px solid var(--border)',
          display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
          gap: 4, fontWeight: isActive ? 600 : 500,
          cursor: 'pointer', flexShrink: 0, padding: 0,
          appearance: 'none', WebkitAppearance: 'none',
          lineHeight: 1, boxSizing: 'border-box',
          transition: 'background 0.15s, color 0.15s',
        })
        const hoverIn = (isActive) => (e) => {
          if (isActive) return
          e.currentTarget.style.background = 'var(--bg-item-hover)'
          e.currentTarget.style.color = 'var(--accent-light)'
        }
        const hoverOut = (isActive) => (e) => {
          if (isActive) return
          e.currentTarget.style.background = 'var(--bg-input)'
          e.currentTarget.style.color = 'var(--text-primary)'
        }
        // Reihe 1: 3 View-Icons + Kamera-Settings
        const row1 = [
          ...VIEWS_ROW1,
          { value: '__camera__', icon: 'videocam', kind: 'icon' },
        ]
        return (
          <div style={{
            display: 'flex', flexDirection: 'column', gap: 4, flexShrink: 0,
          }}>
            {/* Reihe 1 */}
            <div style={{
              display: 'inline-flex', width: VIEW_W,
              height: BAR_H + 2, boxSizing: 'border-box',
              border: '1px solid var(--border)', borderRadius: 999,
              overflow: 'hidden', flexShrink: 0,
            }}>
              {row1.map((v, idx) => {
                const isActive = v.value !== '__camera__' && matchView(v.value)
                const label = v.value === '__camera__' ? 'Kamera-Einstellungen'
                  : `Ansicht ${v.value}`
                return (
                  <button key={v.value}
                    onClick={() => v.value === '__camera__'
                      ? openKameraPanel() : setView(v.value)}
                    title={label}
                    onMouseEnter={hoverIn(isActive)}
                    onMouseLeave={hoverOut(isActive)}
                    style={cellStyle(isActive, idx === 0)}>
                    <Icon name={v.icon} size={11} />
                  </button>
                )
              })}
            </div>
            {/* Reihe 2: N/O/S/W als Buchstaben */}
            <div style={{
              display: 'inline-flex', width: VIEW_W,
              height: BAR_H + 2, boxSizing: 'border-box',
              border: '1px solid var(--border)', borderRadius: 999,
              overflow: 'hidden', flexShrink: 0,
            }}>
              {VIEWS_ROW2.map((v, idx) => {
                const isActive = matchView(v.value)
                return (
                  <button key={v.value}
                    onClick={() => setView(v.value)}
                    title={`Ansicht aus ${v.value} (Norden = ${(state.northAngle || 0).toFixed(0)}°)`}
                    onMouseEnter={hoverIn(isActive)}
                    onMouseLeave={hoverOut(isActive)}
                    style={cellStyle(isActive, idx === 0)}>
                    <span style={{
                      fontFamily: 'DM Mono, monospace', fontSize: 10, fontWeight: 600,
                    }}>{v.label}</span>
                  </button>
                )
              })}
            </div>
          </div>
        )
      })()}

      <div style={sep} />

      {/* ====== 2-Reihen Preset-Block ======
          Oben: Display | Kombi
          Unten: Overrides | Masse
          Gleiche Pill-Breiten, identische X-Positionen (Grid-Layout). */}
      {(() => {
        const PRESET_W = 150
        return (
          <div style={{
            display: 'grid', gridTemplateColumns: 'auto auto',
            gap: '4px 6px', flexShrink: 0,
          }}>
            {/* Reihe 1, Spalte 1: Display */}
            <BarCombo
              icon="lightbulb"
              value={state.displayMode || ''}
              onChange={(v) => setDisplayMode(v)}
              title="Display-Mode (Wireframe / Shaded / Rendered / etc.)"
              width={PRESET_W}
            >
              {!state.displayMode && <option value="">—</option>}
              {(state.displayModes || []).map(dm => (
                <option key={dm.id} value={dm.name}>{dm.name}</option>
              ))}
            </BarCombo>
            {/* Reihe 1, Spalte 2: Modelldarstellung (SIA-400 LoD) */}
            <BarCombo
              icon="tune"
              value={state.aktiveDarstellung || 'einfach'}
              onChange={(v) => setDarstellung(v)}
              title="Modelldarstellung — Default fuer Fenster/Tueren auf 'Auto'. Einzelobjekt-Override im Properties-Panel."
              width={PRESET_W}
            >
              <option value="einfach">Einfach (1:100)</option>
              <option value="standard">Standard (1:50)</option>
              <option value="detail">Detail (1:20)</option>
            </BarCombo>
            {/* Reihe 2, Spalte 1: Overrides (Toggle als Icon links) */}
            <BarCombo
              icon="auto_fix_high"
              iconClickable
              iconActive={state.overridesEnabled}
              onIconClick={() => toggleOverrides(!state.overridesEnabled)}
              iconTitle={state.overridesEnabled
                ? 'Grafische Overrides aktiv — klick zum Ausschalten'
                : 'Grafische Overrides ausgeschaltet'}
              value={state.overridesActivePreset || '__none__'}
              onChange={(v) => {
                if (v === '__configure__') { openOverridesPanel(); return }
                setOverridesPreset(v === '__none__' ? null : v)
              }}
              title={state.overridesActivePreset
                ? `Aktives Preset: ${state.overridesActivePreset} (${state.overridesCount} Regeln)`
                : `Kein Preset aktiv (${state.overridesCount} Regeln, frei editiert)`}
              width={PRESET_W}
              onGear={openOverridesPanel}
              gearTitle="Overrides-Regel-Editor öffnen"
            >
              <option value="__none__">{state.overridesCount > 0 ? `— (${state.overridesCount} Regeln)` : '—'}</option>
              {(state.overridesPresets || []).map(name => (
                <option key={name} value={name}>{name}</option>
              ))}
              <option disabled>──────────</option>
              <option value="__configure__">Konfigurieren…</option>
            </BarCombo>
            {/* Reihe 2, Spalte 2: Masse */}
            <BarCombo
              icon="straighten"
              value={state.masseActiveId || ''}
              onChange={(v) => setMasseActive(v)}
              title="Aktives Mass — Raum-Rundung + Mass-Linien-Format"
              width={PRESET_W}
              onGear={openMasseSettings}
              gearTitle="Masse bearbeiten / neues anlegen"
            >
              {(state.massePresets || []).length === 0 && <option value="">—</option>}
              {(state.massePresets || []).map(p => (
                <option key={p.id} value={p.id}>{p.name}</option>
              ))}
            </BarCombo>
          </div>
        )
      })()}

      <div style={sep} />

      {/* ====== MASSSTAB 2x2 ======
          Reihe 1: [Aktueller Massstab]    [Massstab-Dropdown (gruen wenn gesetzt)]
          Reihe 2: [Zoom-Verhaeltnis %]    [Buttons]
      */}
      {(() => {
        // Buttons-Pill: gleiche Logik wie View-Toggle (weiss default,
        // grün on hover, accent-fill wenn active)
        const PILL_W = 140  // Gleiche Breite fuer Dropdown + Buttons-Pill
        const N_BTN = 3     // ohne Lineweights — der sitzt jetzt oben neben Dropdown
        const BTN_W = Math.floor(PILL_W / N_BTN)  // jeder Button gleich breit
        const SegBtn = ({ icon, onClick, title, disabled, active, isFirst, isLast }) => (
          <button onClick={onClick} disabled={disabled} title={title}
            onMouseEnter={(e) => {
              if (disabled || active) return
              e.currentTarget.style.background = 'var(--bg-item-hover)'
              e.currentTarget.style.color = 'var(--accent-light)'
            }}
            onMouseLeave={(e) => {
              if (active) return
              e.currentTarget.style.background = 'var(--bg-input)'
              e.currentTarget.style.color = 'var(--text-primary)'
            }}
            style={{
              height: BAR_H, minHeight: BAR_H, maxHeight: BAR_H,
              width: BTN_W,
              background: active ? 'var(--accent)' : 'var(--bg-input)',
              color: active ? 'var(--bg-panel)' : 'var(--text-primary)',
              border: 'none',
              borderLeft: isFirst ? 'none' : '1px solid var(--border)',
              display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
              cursor: disabled ? 'not-allowed' : 'pointer',
              opacity: disabled ? 0.4 : 1, flexShrink: 0,
              padding: 0,
              appearance: 'none', WebkitAppearance: 'none',
              lineHeight: 1, boxSizing: 'border-box',
              transition: 'background 0.15s, color 0.15s',
            }}>
            <Icon name={icon} size={11} />
          </button>
        )
        const ratio = (!isPerspective && appliedScale && scaleVal)
          ? appliedScale / scaleVal
          : null
        const ratioText = ratio == null
          ? '—'
          : ratio >= 1
            ? Math.round(ratio * 100) + '%'
            : (ratio * 100).toFixed(ratio < 0.1 ? 1 : 0) + '%'
        const atScale = ratio != null && Math.abs(ratio - 1) < 0.005
        const STAT_W = 70   // Breite der gemeinsamen Stat-Box
        // Gesamte 2-Reihen-Hoehe: 2 × BAR_H + gap (4px) = ~48px
        return (
      <div style={{
        display: 'grid', gridTemplateColumns: 'auto auto', gap: '4px 6px',
        alignItems: 'center', flexShrink: 0,
      }}>
        {/* Spalte 1, beide Reihen: EINE Pill mit Live-Massstab oben und
            Zoom-% unten. Text-Hoehen bleiben identisch zu den
            urspruenglichen 2 Chips — der Trennstrich sitzt in der Mitte
            des 4px-Gaps zwischen ihnen. */}
        <div style={{
          gridRow: '1 / span 2',
          display: 'flex', flexDirection: 'column',
          width: STAT_W, height: BAR_H * 2 + 6,
          background: atScale ? 'var(--accent-dim)' : 'var(--bg-input)',
          color: atScale ? 'var(--accent-light)' : 'var(--text-primary)',
          border: '1px solid ' + (atScale ? 'var(--accent)' : 'var(--border)'),
          borderRadius: 14,
          overflow: 'hidden',
          fontFamily: 'DM Mono, monospace', fontSize: 11,
          fontWeight: atScale ? 600 : 500,
          flexShrink: 0,
          transition: 'border-color 0.15s, background 0.15s',
        }}>
          <div style={{
            height: BAR_H,
            display: 'flex', alignItems: 'center', justifyContent: 'center',
          }} title={isPerspective ? 'Perspektive — kein Massstab' : 'Aktueller Live-Massstab'}>
            {isPerspective ? '—' : fmtScale(scaleVal)}
          </div>
          <div style={{ height: 6, position: 'relative' }}>
            <div style={{
              position: 'absolute', left: 6, right: 6,
              top: '50%', height: 1,
              background: 'var(--border)',
            }} />
          </div>
          <div style={{
            height: BAR_H,
            display: 'flex', alignItems: 'center', justifyContent: 'center',
          }} title={ratio != null
            ? `Aktueller Zoom = ${ratioText} des gesetzten Massstabs`
            : (isPerspective ? 'Perspektive' : 'Kein Massstab gesetzt')}>
            {ratioText}
          </div>
        </div>
        {/* Reihe 1, Spalte 2: Gesetzter Massstab Dropdown — KEIN Icon, gleiche
            Breite wie Buttons-Pill darunter, exakt uebereinander */}
        {/* Dropdown + Druck-Ansicht-Toggle in einer Flex-Reihe — der
            Toggle sitzt jetzt oben statt unten im Zoom-Pill, weil er
            massstabs-nah ist (Print-View = scale-korrekte Strichstaerken). */}
        <div style={{ display: 'flex', gap: 4, alignItems: 'center' }}>
          {customMode ? (
            <input
              ref={customInputRef}
              disabled={isPerspective}
              type="text" placeholder="1:N"
              value={draft}
              onChange={(e) => setDraft(e.target.value)}
              onKeyDown={(e) => {
                if (e.key === 'Enter') applyDraft()
                else if (e.key === 'Escape') cancelDraft()
              }}
              onBlur={applyDraft}
              style={{
                height: BAR_H, width: PILL_W,
                background: 'var(--bg-input)',
                color: 'var(--text-primary)',
                border: '1px solid var(--border)',
                borderRadius: 999,
                padding: '0 12px', fontSize: 11,
                fontFamily: 'DM Mono, monospace',
                outline: 'none',
              }}
              title="Massstab eingeben (Enter = uebernehmen, Esc = abbrechen)"
            />
          ) : (
            <BarCombo
              value={dropdownValue}
              onChange={(v) => applyDropdown(v)}
              disabled={isPerspective}
              width={PILL_W}
              title="Gesetzter Massstab"
            >
              <option value="__none__">—</option>
              {PRESETS.map(p => (
                <option key={p.value} value={String(p.value)}>{p.label}</option>
              ))}
              {appliedScale != null && !PRESETS.some(p => p.value === appliedScale) && (
                <option value={String(appliedScale)}>1:{appliedScale}</option>
              )}
              <option value="__custom__">Eigener…</option>
            </BarCombo>
          )}
          <BarButton
            icon={state.showLineweights ? 'print' : 'edit'}
            active={state.showLineweights}
            onClick={() => setShowLineweights(!state.showLineweights)}
            title={state.showLineweights
              ? 'Print-View aktiv — klick zum Ausschalten'
              : 'Strichstaerken anzeigen (Print-View)'} />
        </div>
        {/* Reihe 2, Spalte 2: Zoom-Pill + Referenzlinien-Toggle.
            Symmetrisch zur Reihe 1 (Dropdown + Lineweights-Button). */}
        <div style={{ display: 'flex', gap: 4, alignItems: 'center' }}>
          <div style={{
            display: 'inline-flex', width: PILL_W,
            height: BAR_H + 2, boxSizing: 'border-box',
            border: '1px solid var(--border)', borderRadius: 999,
            overflow: 'hidden', flexShrink: 0,
          }}>
            <SegBtn icon="percent" onClick={apply100} isFirst
                    disabled={isPerspective || !appliedScale}
                    title={appliedScale ? `Zoom auf 1:${appliedScale} snappen` : 'Erst einen Massstab wählen'} />
            <SegBtn icon="fit_screen" onClick={zoomExtents}
                    title="Auf gesamten Inhalt zoomen" />
            <SegBtn icon="center_focus_strong" onClick={zoomSelection}
                    isLast
                    title="Auf Selektion zoomen" />
          </div>
          <BarButton
            icon={state.referenzlinienVisible === false ? 'visibility_off' : 'visibility'}
            active={state.referenzlinienVisible !== false}
            onClick={() => toggleReferenzlinien(state.referenzlinienVisible === false)}
            title={state.referenzlinienVisible === false
              ? 'Referenzlinien einblenden (Wandachsen, Oeffnungs-Punkte)'
              : 'Referenzlinien ausblenden (Wandachsen, Oeffnungs-Punkte)'} />
        </div>
      </div>
        )
      })()}

      <div style={sep} />

      {/* ====== ANORDNEN (2D-Z-Stack via Rhino-DisplayOrder) ======
          2x2-Grid (quadratisch): oben Aufwaerts-Aktionen, unten Abwaerts.
          Reihe 1: Vorderste | 1 hoch     (vertical_align_top, expand_less)
          Reihe 2: 1 runter | Hinterste   (expand_more, vertical_align_bottom)
          Selection-Check + Rhino-DisplayOrder im Backend, keine Z-Offsets. */}
      {(() => {
        const CELL = 26  // quadratisch: 2 * CELL Breite, ~2 * BAR_H Hoehe
        const Btn = ({ icon, dir, title, isFirst }) => (
          <button onClick={() => arrangeSelection(dir)} title={title}
            onMouseEnter={(e) => {
              e.currentTarget.style.background = 'var(--bg-item-hover)'
              e.currentTarget.style.color = 'var(--accent-light)'
            }}
            onMouseLeave={(e) => {
              e.currentTarget.style.background = 'var(--bg-input)'
              e.currentTarget.style.color = 'var(--text-primary)'
            }}
            style={{
              height: BAR_H, minHeight: BAR_H, maxHeight: BAR_H, width: CELL,
              background: 'var(--bg-input)', color: 'var(--text-primary)',
              border: 'none',
              borderLeft: isFirst ? 'none' : '1px solid var(--border)',
              display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
              cursor: 'pointer', padding: 0, flexShrink: 0,
              appearance: 'none', WebkitAppearance: 'none',
              lineHeight: 1, boxSizing: 'border-box',
              transition: 'background 0.15s, color 0.15s',
            }}>
            <Icon name={icon} size={11} />
          </button>
        )
        const rowStyle = {
          display: 'inline-flex', width: CELL * 2,
          height: BAR_H + 2, boxSizing: 'border-box',
          border: '1px solid var(--border)', borderRadius: 999,
          overflow: 'hidden', flexShrink: 0,
        }
        return (
          <div style={{
            display: 'flex', flexDirection: 'column', gap: 4, flexShrink: 0,
          }}>
            <div style={rowStyle}>
              <Btn icon="vertical_align_top" dir="front" isFirst
                    title="In den Vordergrund (Bring to Front)" />
              <Btn icon="expand_less"        dir="forward"
                    title="Eine Stufe hoch (Bring Forward)" />
            </div>
            <div style={rowStyle}>
              <Btn icon="expand_more"        dir="backward" isFirst
                    title="Eine Stufe runter (Send Backward)" />
              <Btn icon="vertical_align_bottom" dir="back"
                    title="In den Hintergrund (Send to Back)" />
            </div>
          </div>
        )
      })()}

      <div style={sep} />

      {/* ====== SNAP-BAR (Architektur-Osnaps + Grid) ======
          Layout: links zwei einzelne Toggle-Buttons (Master-O / Grid)
          stacked. Rechts zwei 3-Zellen-Pills mit den Osnap-Modi.
          Master-O und Grid sind globale Toggles → eigene Buttons.
          Die 6 Modi gehören zusammen → Pill-Gruppe.
          Ortho + Grid-Snap sind in Rhinos Footer-Bar — hier nicht. */}
      {(() => {
        const om = state.osnapModes || {}
        const osnapDisabled = !state.osnap
        // Bar-Cell: Teil einer Segment-Pill (kein eigener Border-Radius)
        const BarCell = ({ icon, active, disabled, onClick, isFirst, title }) => (
          <button onClick={onClick} disabled={disabled} title={title}
            onMouseEnter={(e) => {
              if (disabled || active) return
              e.currentTarget.style.background = 'var(--bg-item-hover)'
              e.currentTarget.style.color = 'var(--accent-light)'
            }}
            onMouseLeave={(e) => {
              if (active) return
              e.currentTarget.style.background = 'var(--bg-input)'
              e.currentTarget.style.color = 'var(--text-primary)'
            }}
            style={{
              height: BAR_H, width: BAR_H, padding: 0,
              background: active ? 'var(--accent)' : 'var(--bg-input)',
              color: active ? 'var(--bg-panel)' : 'var(--text-primary)',
              border: 'none',
              borderLeft: isFirst ? 'none' : '1px solid var(--border)',
              cursor: disabled ? 'not-allowed' : 'pointer',
              opacity: disabled ? 0.35 : 1,
              display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
              appearance: 'none', WebkitAppearance: 'none',
              lineHeight: 1, boxSizing: 'border-box', flexShrink: 0,
              transition: 'background 0.15s, color 0.15s',
            }}>
            <Icon name={icon} size={12} />
          </button>
        )
        const pillRowStyle = {
          display: 'inline-flex', width: BAR_H * 3,
          height: BAR_H + 2, boxSizing: 'border-box',
          border: '1px solid var(--border)', borderRadius: 999,
          overflow: 'hidden', flexShrink: 0,
        }
        return (
          <div style={{ display: 'flex', gap: 6, alignItems: 'flex-start',
                         flexShrink: 0 }}>
            {/* Linke Spalte: 2 einzelne Toggle-Buttons stacked */}
            <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
              <BarButton icon="gps_fixed"
                active={!!state.osnap}
                onClick={() => toggleOsnap(!state.osnap)}
                title={state.osnap ? 'Object-Snap an — Klick zum Ausschalten'
                                    : 'Object-Snap aus — Klick zum Einschalten'} />
              <BarButton
                icon={state.gridVisible === false ? 'grid_off' : 'grid_on'}
                active={state.gridVisible !== false}
                onClick={() => toggleGridVisible(state.gridVisible === false)}
                title="Konstruktions-Raster ein-/ausblenden" />
            </div>
            {/* Rechte Spalte: 2 Pills mit den 6 Osnap-Modi */}
            <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
              <div style={pillRowStyle}>
                <BarCell icon="crop_square" isFirst disabled={osnapDisabled}
                  active={!!om.end}
                  onClick={() => setOsnapMode('end', !om.end)}
                  title="Endpunkt (End)" />
                <BarCell icon="change_history" disabled={osnapDisabled}
                  active={!!om.mid}
                  onClick={() => setOsnapMode('mid', !om.mid)}
                  title="Mittelpunkt (Mid)" />
                <BarCell icon="close" disabled={osnapDisabled}
                  active={!!om.int}
                  onClick={() => setOsnapMode('int', !om.int)}
                  title="Schnittpunkt (Intersection)" />
              </div>
              <div style={pillRowStyle}>
                <BarCell icon="square_foot" isFirst disabled={osnapDisabled}
                  active={!!om.perp}
                  onClick={() => setOsnapMode('perp', !om.perp)}
                  title="Lotrecht (Perpendicular)" />
                <BarCell icon="radio_button_unchecked" disabled={osnapDisabled}
                  active={!!om.cen}
                  onClick={() => setOsnapMode('cen', !om.cen)}
                  title="Kreis-/Bogen-Mittelpunkt (Center)" />
                <BarCell icon="add" disabled={osnapDisabled}
                  active={!!om.near}
                  onClick={() => setOsnapMode('near', !om.near)}
                  title="Naechster Punkt (Near)" />
              </div>
            </div>
          </div>
        )
      })()}

      <div style={sep} />

      {/* ====== TEXT-Block (Vectorworks-Stil) ======
          Reihe 1: Style ▼ | Font ▼ | Size ▼
          Reihe 2: [B][I][U] | [L][C][R] | [+]
          Wenn TextEntity selektiert: Werte spiegeln Selektion, Aenderungen
          gehen direkt rauf und werden zusaetzlich als Default gespeichert.
      */}
      {(() => {
        const sel = state.textSelectionSettings
        const ts = sel || state.textSettings || {}
        const fonts = state.textFonts || []
        const styles = state.textStyles || []
        // Bei Selektion: Style-ID vom Text selber (falls per apply_style gesetzt),
        // sonst auf globalen Active-Style fallen
        const activeStyleId = (sel && sel.styleId) || state.textStyleActiveId
        const updateTs = (patch) => setTextSettings({ ...ts, ...patch })
        const STYLE_W = 110
        const FONT_W  = 130
        const SIZE_W  = 80
        const ACTIVE_BORDER = sel ? 'var(--accent)' : 'var(--border)'
        const SIZE_PRESETS = [0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.50, 0.70, 1.00]

        // Toggle-Button-Helper fuer B/I/U/Align
        const ToggleBtn = ({ active, onClick, title, children, borderLeft, lastInRow }) => (
          <button onClick={onClick} title={title}
            onMouseEnter={(e) => {
              if (active) return
              e.currentTarget.style.background = 'var(--bg-item-hover)'
              e.currentTarget.style.color = 'var(--accent-light)'
            }}
            onMouseLeave={(e) => {
              if (active) return
              e.currentTarget.style.background = 'var(--bg-input)'
              e.currentTarget.style.color = 'var(--text-primary)'
            }}
            style={{
              flex: 1, height: '100%',
              background: active ? 'var(--accent)' : 'var(--bg-input)',
              color: active ? 'var(--bg-panel)' : 'var(--text-primary)',
              border: 'none',
              borderLeft: borderLeft ? '1px solid var(--border)' : 'none',
              cursor: 'pointer',
              appearance: 'none', WebkitAppearance: 'none',
              lineHeight: 1, padding: 0,
              display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
              transition: 'background 0.15s, color 0.15s',
            }}>
            {children}
          </button>
        )

        return (
          <div style={{
            display: 'grid',
            gridTemplateColumns: `${STYLE_W}px ${FONT_W}px ${SIZE_W}px`,
            gap: '4px 6px', alignItems: 'center', flexShrink: 0,
          }}>
            {/* === Reihe 1 === */}
            {/* Style-Dropdown */}
            <BarCombo
              value={activeStyleId || ''}
              onChange={(v) => {
                if (v === '__save__') {
                  const n = (window.prompt('Name für neuen Text-Stil:', 'Stil') || '').trim()
                  if (n) saveTextStyle(n)
                  return
                }
                if (v === '__delete__') {
                  if (activeStyleId &&
                      window.confirm(`Stil "${styles.find(s => s.id === activeStyleId)?.name}" löschen?`))
                    deleteTextStyle(activeStyleId)
                  return
                }
                if (v) applyTextStyle(v)
              }}
              width={STYLE_W}
              title="Text-Stil — gespeicherte Settings-Presets"
            >
              <option value="">— Stil —</option>
              {styles.map(s => <option key={s.id} value={s.id}>{s.name}</option>)}
              <option disabled>──────────</option>
              <option value="__save__">+ Speichern…</option>
              {activeStyleId && <option value="__delete__">🗑 Aktiven löschen</option>}
            </BarCombo>
            {/* Font-Dropdown */}
            <BarCombo
              value={ts.font || ''}
              onChange={(v) => updateTs({ font: v })}
              width={FONT_W}
              title={sel ? 'Schriftart (auf Selektion appliziert)' : 'Schriftart'}
            >
              {fonts.length === 0 && <option value="">— Fonts laden —</option>}
              {fonts.map(f => <option key={f} value={f}>{f}</option>)}
            </BarCombo>
            {/* Size-Dropdown mit "Eigene" / Custom-Input */}
            {textSizeCustom ? (
              <div style={{
                display: 'inline-flex', alignItems: 'center', gap: 3,
                height: BAR_H + 2, padding: '0 8px', boxSizing: 'border-box',
                background: 'var(--bg-input)',
                border: '1px solid ' + ACTIVE_BORDER,
                borderRadius: 999,
                flexShrink: 0, width: SIZE_W,
              }}>
                <input
                  type="number" step="0.01" min="0.01" autoFocus
                  value={ts.size != null ? ts.size : 0.2}
                  onChange={(e) => {
                    const v = parseFloat(e.target.value)
                    if (!isNaN(v) && v > 0) updateTs({ size: v })
                  }}
                  onBlur={() => setTextSizeCustom(false)}
                  onKeyDown={(e) => {
                    if (e.key === 'Enter' || e.key === 'Escape') {
                      e.target.blur()
                    }
                  }}
                  style={{
                    flex: 1, minWidth: 0,
                    background: 'transparent', border: 'none', outline: 'none',
                    color: 'var(--text-primary)',
                    fontSize: 11, fontFamily: 'DM Mono, monospace',
                    padding: 0, textAlign: 'right', appearance: 'auto',
                  }}
                />
                <span style={{ fontSize: 9, color: 'var(--text-muted)' }}>m</span>
              </div>
            ) : (
              <BarCombo
                value={String(ts.size != null ? ts.size : 0.2)}
                onChange={(v) => {
                  if (v === '__custom__') { setTextSizeCustom(true); return }
                  const n = parseFloat(v)
                  if (!isNaN(n) && n > 0) updateTs({ size: n })
                }}
                width={SIZE_W}
                title={sel ? 'Texthoehe (auf Selektion appliziert)' : 'Texthoehe (m)'}
              >
                {SIZE_PRESETS.map(s => (
                  <option key={s} value={String(s)}>{s.toFixed(2)} m</option>
                ))}
                {ts.size != null && !SIZE_PRESETS.some(s => Math.abs(s - ts.size) < 0.001) && (
                  <option value={String(ts.size)}>{ts.size.toFixed(2)} m</option>
                )}
                <option disabled>──────────</option>
                <option value="__custom__">Eigene…</option>
              </BarCombo>
            )}

            {/* === Reihe 2 === */}
            {/* B/I/U Segmented */}
            <div style={{
              display: 'inline-flex',
              border: '1px solid ' + ACTIVE_BORDER, borderRadius: 999,
              overflow: 'hidden', flexShrink: 0, width: STYLE_W,
              height: BAR_H + 2, boxSizing: 'border-box',
              transition: 'border-color 0.15s',
            }}>
              <ToggleBtn active={!!ts.bold}
                onClick={() => updateTs({ bold: !ts.bold })}
                title={(sel ? 'Fett auf Selektion' : 'Fett') + ' (Default)'}>
                <Icon name="format_bold" size={13} />
              </ToggleBtn>
              <ToggleBtn active={!!ts.italic} borderLeft
                onClick={() => updateTs({ italic: !ts.italic })}
                title={(sel ? 'Kursiv auf Selektion' : 'Kursiv') + ' (Default)'}>
                <Icon name="format_italic" size={13} />
              </ToggleBtn>
              <ToggleBtn active={!!ts.underline} borderLeft
                onClick={() => updateTs({ underline: !ts.underline })}
                title="Unterstrichen (Settings — Rendering kommt mit RichText-Support)">
                <Icon name="format_underlined" size={13} />
              </ToggleBtn>
            </div>
            {/* L/C/R Align Segmented */}
            <div style={{
              display: 'inline-flex',
              border: '1px solid ' + ACTIVE_BORDER, borderRadius: 999,
              overflow: 'hidden', flexShrink: 0, width: FONT_W,
              height: BAR_H + 2, boxSizing: 'border-box',
              transition: 'border-color 0.15s',
            }}>
              <ToggleBtn active={(ts.align || 'left') === 'left'}
                onClick={() => updateTs({ align: 'left' })}
                title={(sel ? 'Linksbuendig auf Selektion' : 'Linksbuendig')}>
                <Icon name="format_align_left" size={13} />
              </ToggleBtn>
              <ToggleBtn active={ts.align === 'center'} borderLeft
                onClick={() => updateTs({ align: 'center' })}
                title={(sel ? 'Zentriert auf Selektion' : 'Zentriert')}>
                <Icon name="format_align_center" size={13} />
              </ToggleBtn>
              <ToggleBtn active={ts.align === 'right'} borderLeft
                onClick={() => updateTs({ align: 'right' })}
                title={(sel ? 'Rechtsbuendig auf Selektion' : 'Rechtsbuendig')}>
                <Icon name="format_align_right" size={13} />
              </ToggleBtn>
            </div>
            {/* + Neuen Text einfuegen */}
            <button
              onClick={() => createText()}
              onMouseEnter={(e) => {
                e.currentTarget.style.background = 'var(--bg-item-hover)'
                e.currentTarget.style.borderColor = 'var(--accent)'
                e.currentTarget.style.color = 'var(--accent-light)'
              }}
              onMouseLeave={(e) => {
                e.currentTarget.style.background = 'var(--bg-input)'
                e.currentTarget.style.borderColor = 'var(--border)'
                e.currentTarget.style.color = 'var(--text-primary)'
              }}
              style={{
                width: SIZE_W, height: BAR_H + 2, boxSizing: 'border-box',
                background: 'var(--bg-input)',
                color: 'var(--text-primary)',
                border: '1px solid var(--border)',
                borderRadius: 999,
                display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
                cursor: 'pointer',
                appearance: 'none', WebkitAppearance: 'none',
                lineHeight: 1, padding: 0, flexShrink: 0,
                transition: 'background 0.15s, color 0.15s, border-color 0.15s',
              }}
              title="Neuen Text einfuegen — Position picken, Text eingeben"
            >
              <Icon name="add" size={14} />
            </button>
          </div>
        )
      })()}

      {/* Snap-Toggles (Ortho/Grid/OSnap) sind in Rhinos eigener Footer-Bar
          schon vorhanden — hier rausgenommen um Doppelung zu vermeiden. */}

      {/* Spacer am rechten Rand */}
      <div style={{ flex: 1 }} />
    </div>
    </div>
  )
}
