// ============================================================================
// Railway Layout Creator
// © Dial Solutions 2025
// ============================================================================

// ============================================================================
// GLOBAL STATE
// ============================================================================

const state = {
  // Layout data structure matching railway.js format
  layout: {
    compass: "N",
    comments: "",
    tracks: {},
    options: [],
    features: {}
  },
  
  // Canvas and view state
  canvas: null,
  ctx: null,
  viewOffsetX: 0,
  viewOffsetY: 0,
  viewScale: 10, // pixels per meter
  isDragging: false,
  isPanning: false,
  panStartX: 0,
  panStartY: 0,
  
  // Tool state
  currentTool: 'select',
  selectedTrack: null,
  hoveredTrack: null,
  connectionStart: null,
  
  // Track ID counters
  counters: {
    N: 1,
    B: 1,
    P: 1,
    S: 1
  },
  
  // Settings
  snapToGrid: true,
  gridSize: 5, // meters
  
  // Undo/redo
  history: [],
  historyIndex: -1,
  maxHistory: 50,
  
  // Insert functionality
  insertLayout: null,
  insertPosition: { x: 0, y: 0 },
  insertPreview: null,
  insertRotation: 0,
  isDraggingInsert: false,
  
  // Multi-select functionality
  selectedTracks: new Set(),
  isSelecting: false,
  selectionStart: { x: 0, y: 0 },
  selectionEnd: { x: 0, y: 0 },
  isDraggingSelection: false,
  dragOffset: { x: 0, y: 0 }
};

// ============================================================================
// INITIALIZATION
// ============================================================================

document.addEventListener('DOMContentLoaded', () => {
  initCanvas();
  initEventListeners();
  initKeyboardShortcuts();
  updateStats();
  setStatus('Ready - Click a tool and start placing tracks');
});

function initCanvas() {
  state.canvas = document.getElementById('layoutCanvas');
  state.ctx = state.canvas.getContext('2d');
  
  resizeCanvas();
  window.addEventListener('resize', resizeCanvas);
  
  // Center view
  state.viewOffsetX = state.canvas.width / 2;
  state.viewOffsetY = state.canvas.height / 2;
  
  draw();
}

function resizeCanvas() {
  const container = state.canvas.parentElement;
  const dpr = window.devicePixelRatio || 1;
  
  state.canvas.width = container.clientWidth * dpr;
  state.canvas.height = container.clientHeight * dpr;
  state.canvas.style.width = container.clientWidth + 'px';
  state.canvas.style.height = container.clientHeight + 'px';
  
  state.ctx.scale(dpr, dpr);
  
  draw();
}

function initEventListeners() {
  // Tool buttons
  document.querySelectorAll('.tool-btn').forEach(btn => {
    btn.addEventListener('click', () => {
      const tool = btn.dataset.tool;
      selectTool(tool);
    });
  });
  
  // Canvas events
  state.canvas.addEventListener('mousedown', handleMouseDown);
  state.canvas.addEventListener('mousemove', handleMouseMove);
  state.canvas.addEventListener('mouseup', handleMouseUp);
  state.canvas.addEventListener('wheel', handleWheel, { passive: false });
  state.canvas.addEventListener('contextmenu', (e) => e.preventDefault());
  
  // Drag and drop for insert
  state.canvas.addEventListener('dragover', handleDragOver);
  state.canvas.addEventListener('drop', handleDrop);
  state.canvas.addEventListener('dragenter', handleDragEnter);
  state.canvas.addEventListener('dragleave', handleDragLeave);
  
  // View controls
  document.getElementById('btn-zoom-in').addEventListener('click', () => zoomBy(1.2));
  document.getElementById('btn-zoom-out').addEventListener('click', () => zoomBy(0.8));
  document.getElementById('btn-fit').addEventListener('click', fitToView);
  
  // Settings
  document.getElementById('snap-grid').addEventListener('change', (e) => {
    state.snapToGrid = e.target.checked;
  });
  document.getElementById('grid-size').addEventListener('change', (e) => {
    state.gridSize = parseFloat(e.target.value) || 5;
    draw();
  });
  
  // File operations
  document.getElementById('btn-new').addEventListener('click', newLayout);
  document.getElementById('btn-import').addEventListener('click', () => {
    document.getElementById('file-input').click();
  });
  document.getElementById('file-input').addEventListener('change', handleFileImport);
  document.getElementById('btn-insert').addEventListener('click', () => {
    document.getElementById('insert-file-input').click();
  });
  document.getElementById('insert-file-input').addEventListener('change', handleInsertFile);
  document.getElementById('btn-export').addEventListener('click', exportJSON);
  document.getElementById('btn-validate').addEventListener('click', validateLayout);
  document.getElementById('btn-normalize').addEventListener('click', () => {
    normalizeNodeNumbering();
    updateStats();
    draw();
    setStatus('Node numbering normalized');
  });
  
  // Properties panel
  document.getElementById('btn-apply').addEventListener('click', applyProperties);
  document.getElementById('btn-delete-track').addEventListener('click', deleteSelectedTrack);
  document.getElementById('prop-type').addEventListener('change', updatePropertiesPanel);
  
  // Layout settings
  document.getElementById('layout-compass').addEventListener('change', (e) => {
    state.layout.compass = e.target.value;
  });
  document.getElementById('layout-comments').addEventListener('change', (e) => {
    state.layout.comments = e.target.value;
  });
  
  // Validation modal
  const closeValidation = document.getElementById('close-validation');
  const closeValidationBtn = document.getElementById('close-validation-btn');
  
  if (closeValidation) closeValidation.addEventListener('click', closeValidationModal);
  if (closeValidationBtn) closeValidationBtn.addEventListener('click', closeValidationModal);
  
  // Insert modal (for traditional insert method)
  const closeInsert = document.getElementById('close-insert');
  const btnInsertCancel = document.getElementById('btn-insert-cancel');
  const btnInsertApply = document.getElementById('btn-insert-apply');
  const insertX = document.getElementById('insert-x');
  const insertY = document.getElementById('insert-y');
  const insertRotation = document.getElementById('insert-rotation');
  const insertIdOffset = document.getElementById('insert-id-offset');
  
  if (closeInsert) closeInsert.addEventListener('click', closeInsertModal);
  if (btnInsertCancel) btnInsertCancel.addEventListener('click', closeInsertModal);
  if (btnInsertApply) btnInsertApply.addEventListener('click', applyInsert);
  
  // Insert modal inputs
  if (insertX) insertX.addEventListener('input', updateInsertPreview);
  if (insertY) insertY.addEventListener('input', updateInsertPreview);
  if (insertRotation) insertRotation.addEventListener('change', updateInsertPreview);
  if (insertIdOffset) insertIdOffset.addEventListener('input', updateInsertPreview);
}

function initKeyboardShortcuts() {
  document.addEventListener('keydown', (e) => {
    // Don't trigger shortcuts when typing in inputs
    if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
    
    switch(e.key.toLowerCase()) {
      case 's': selectTool('select'); break;
      case 'p': selectTool('pass'); break;
      case 'b': selectTool('buffer'); break;
      case 'w': selectTool('point'); break;
      case 'g': selectTool('signal'); break;
      case 'c': selectTool('connection'); break;
      case 'd': selectTool('delete'); break;
      case 'f': fitToView(); break;
      case 'escape':
        if (state.selectedTracks.size > 0) {
          e.preventDefault();
          clearSelection();
        }
        break;
      case 'delete': case 'backspace':
        if (state.selectedTracks.size > 0) {
          e.preventDefault();
          deleteSelectedTracks();
        } else if (state.selectedTrack) {
          e.preventDefault();
          deleteSelectedTrack();
        }
        break;
      case '+': case '=': zoomBy(1.2); break;
      case '-': case '_': zoomBy(0.8); break;
      case 'z':
        if (e.ctrlKey || e.metaKey) {
          e.preventDefault();
          if (e.shiftKey) redo();
          else undo();
        }
        break;
    }
  });
}

// ============================================================================
// TOOL SELECTION
// ============================================================================

function selectTool(tool) {
  state.currentTool = tool;
  
  // Update UI
  document.querySelectorAll('.tool-btn').forEach(btn => {
    btn.classList.toggle('active', btn.dataset.tool === tool);
  });
  
  // Update cursor
  const cursors = {
    select: 'default',
    pass: 'crosshair',
    buffer: 'crosshair',
    point: 'crosshair',
    signal: 'crosshair',
    connection: 'pointer',
    delete: 'not-allowed'
  };
  state.canvas.style.cursor = cursors[tool] || 'default';
  
  setStatus(`Tool: ${tool.charAt(0).toUpperCase() + tool.slice(1)}`);
}

// ============================================================================
// CANVAS DRAWING
// ============================================================================

function draw() {
  const ctx = state.ctx;
  const canvas = state.canvas;
  const w = canvas.width / (window.devicePixelRatio || 1);
  const h = canvas.height / (window.devicePixelRatio || 1);
  
  // Clear canvas
  ctx.clearRect(0, 0, w, h);
  
  // Draw grid
  drawGrid(ctx, w, h);
  
  // Draw origin marker
  drawOrigin(ctx);
  
  // Draw connections
  drawConnections(ctx);
  
  // Draw tracks
  drawTracks(ctx);
  
  // Draw connection preview
  if (state.currentTool === 'connection' && state.connectionStart) {
    drawConnectionPreview(ctx);
  }
  
  // Draw insert preview
  if (state.insertPreview && state.isDraggingInsert) {
    drawInsertPreview(ctx);
  }
  
  // Draw selection box
  if (state.isSelecting) {
    drawSelectionBox(ctx);
  }
  
  // Draw multi-select highlights
  if (state.selectedTracks.size > 0) {
    drawMultiSelectHighlights(ctx);
  }
}

function drawGrid(ctx, w, h) {
  ctx.save();
  ctx.strokeStyle = '#e0e0e0';
  ctx.lineWidth = 1;
  
  const gridSpacing = state.gridSize * state.viewScale;
  
  // Vertical lines
  const startX = (state.viewOffsetX % gridSpacing);
  for (let x = startX; x < w; x += gridSpacing) {
    ctx.beginPath();
    ctx.moveTo(x, 0);
    ctx.lineTo(x, h);
    ctx.stroke();
  }
  
  // Horizontal lines
  const startY = (state.viewOffsetY % gridSpacing);
  for (let y = startY; y < h; y += gridSpacing) {
    ctx.beginPath();
    ctx.moveTo(0, y);
    ctx.lineTo(w, y);
    ctx.stroke();
  }
  
  ctx.restore();
}

function drawOrigin(ctx) {
  const sx = worldToScreenX(0);
  const sy = worldToScreenY(0);
  
  ctx.save();
  ctx.strokeStyle = '#3498db';
  ctx.lineWidth = 2;
  
  // Draw crosshair at origin
  ctx.beginPath();
  ctx.moveTo(sx - 10, sy);
  ctx.lineTo(sx + 10, sy);
  ctx.moveTo(sx, sy - 10);
  ctx.lineTo(sx, sy + 10);
  ctx.stroke();
  
  // Label
  ctx.fillStyle = '#3498db';
  ctx.font = '12px sans-serif';
  ctx.fillText('(0,0)', sx + 12, sy - 5);
  
  ctx.restore();
}

function drawConnections(ctx) {
  ctx.save();
  ctx.strokeStyle = '#95a5a6';
  ctx.lineWidth = 2;
  ctx.setLineDash([5, 5]);
  
  const drawn = new Set();
  
  for (const [id, track] of Object.entries(state.layout.tracks)) {
    if (!track.connections) continue;
    
    const [x1, y1] = track.position;
    
    for (const connId of track.connections) {
      const edgeKey = [id, connId].sort().join('-');
      if (drawn.has(edgeKey)) continue;
      drawn.add(edgeKey);
      
      const connTrack = state.layout.tracks[connId];
      if (!connTrack) continue;
      
      const [x2, y2] = connTrack.position;
      
      ctx.beginPath();
      ctx.moveTo(worldToScreenX(x1), worldToScreenY(y1));
      ctx.lineTo(worldToScreenX(x2), worldToScreenY(y2));
      ctx.stroke();
    }
  }
  
  ctx.restore();
}

function drawTracks(ctx) {
  for (const [id, track] of Object.entries(state.layout.tracks)) {
    drawTrack(ctx, id, track);
  }
}

function drawTrack(ctx, id, track) {
  const [wx, wy] = track.position;
  const sx = worldToScreenX(wx);
  const sy = worldToScreenY(wy);
  
  if (id === 'N4') {
    console.log('Drawing track N4 at world:', wx, wy, 'screen:', sx, sy);
  }
  
  const isSelected = state.selectedTrack === id;
  const isHovered = state.hoveredTrack === id;
  
  // Colors by type
  const colors = {
    buffer: '#e74c3c',
    pass: '#3498db',
    point: '#9b59b6',
    signal: '#f39c12',
    pointsignal: '#e67e22'
  };
  
  const color = colors[track.type] || '#95a5a6';
  const radius = isSelected ? 10 : isHovered ? 8 : 6;
  
  // Draw circle
  ctx.save();
  ctx.fillStyle = color;
  ctx.strokeStyle = isSelected ? '#2ecc71' : isHovered ? '#27ae60' : '#2c3e50';
  ctx.lineWidth = isSelected ? 3 : isHovered ? 2 : 1;
  
  ctx.beginPath();
  ctx.arc(sx, sy, radius, 0, Math.PI * 2);
  ctx.fill();
  ctx.stroke();
  
  // Draw label
  ctx.fillStyle = '#2c3e50';
  ctx.font = 'bold 11px sans-serif';
  ctx.textAlign = 'center';
  ctx.textBaseline = 'middle';
  
  // ID above
  ctx.fillText(id, sx, sy - radius - 10);
  
  // Label below (if exists)
  if (track.label) {
    ctx.font = '10px sans-serif';
    ctx.fillText(track.label, sx, sy + radius + 10);
  }
  
  ctx.restore();
}

function drawConnectionPreview(ctx) {
  const track = state.layout.tracks[state.connectionStart];
  if (!track) return;
  
  const [wx, wy] = track.position;
  const sx = worldToScreenX(wx);
  const sy = worldToScreenY(wy);
  
  ctx.save();
  ctx.strokeStyle = '#2ecc71';
  ctx.lineWidth = 3;
  ctx.setLineDash([5, 5]);
  
  ctx.beginPath();
  ctx.moveTo(sx, sy);
  ctx.lineTo(state.mouseX || sx, state.mouseY || sy);
  ctx.stroke();
  
  ctx.restore();
}

// ============================================================================
// COORDINATE CONVERSION
// ============================================================================

function worldToScreenX(wx) {
  return wx * state.viewScale + state.viewOffsetX;
}

function worldToScreenY(wy) {
  return wy * state.viewScale + state.viewOffsetY;
}

function screenToWorldX(sx) {
  return (sx - state.viewOffsetX) / state.viewScale;
}

function screenToWorldY(sy) {
  return (sy - state.viewOffsetY) / state.viewScale;
}

function snapToGrid(value) {
  if (!state.snapToGrid) return value;
  return Math.round(value / state.gridSize) * state.gridSize;
}

// ============================================================================
// MOUSE HANDLING
// ============================================================================

function handleMouseDown(e) {
  const rect = state.canvas.getBoundingClientRect();
  const x = e.clientX - rect.left;
  const y = e.clientY - rect.top;
  
  const wx = screenToWorldX(x);
  const wy = screenToWorldY(y);
  
  // Middle mouse or space+left for panning
  if (e.button === 1 || (e.button === 0 && e.shiftKey)) {
    state.isPanning = true;
    state.panStartX = x - state.viewOffsetX;
    state.panStartY = y - state.viewOffsetY;
    state.canvas.style.cursor = 'grabbing';
    return;
  }
  
  // Ctrl+click for multi-select
  if (e.button === 0 && e.ctrlKey) {
    const clickedTrack = findTrackAtPosition(wx, wy);
    if (clickedTrack) {
      if (state.selectedTracks.has(clickedTrack)) {
        state.selectedTracks.delete(clickedTrack);
      } else {
        state.selectedTracks.add(clickedTrack);
      }
      updateMultiSelectProperties();
      draw();
    }
    return;
  }
  
  if (e.button !== 0) {
    // Handle right-click for insert rotation
    if (state.insertPreview && state.isDraggingInsert) {
      e.preventDefault();
      state.insertRotation = (state.insertRotation + 90) % 360;
      const snapText = state.snapToGrid ? ' (snapped to grid)' : '';
      setStatus(`Rotation: ${state.insertRotation}° - Drag to position, right-click to rotate, left-click to place${snapText}`);
      draw();
    }
    return;
  }
  
  // Handle insert preview placement (check this first)
  if (state.insertPreview && state.isDraggingInsert) {
    // Snap to grid if enabled
    const snappedX = state.snapToGrid ? snapToGrid(wx) : wx;
    const snappedY = state.snapToGrid ? snapToGrid(wy) : wy;
    applyInsertAtPosition(snappedX, snappedY);
    return;
  }
  
  // Handle select tool interactions
  if (state.currentTool === 'select' && !state.isDraggingInsert) {
    const clickedTrack = findTrackAtPosition(wx, wy);
    if (clickedTrack) {
      // Track clicked - handle selection and dragging
      if (!state.selectedTracks.has(clickedTrack)) {
        // New track selected - clear others and select this one
        state.selectedTracks.clear();
        state.selectedTracks.add(clickedTrack);
        state.selectedTrack = clickedTrack;
      }
      
      // Start dragging - check if we have multiple tracks selected
      if (state.selectedTracks.size > 1) {
        console.log('Starting multi-select drag for', state.selectedTracks.size, 'tracks at', wx, wy);
        state.isDraggingSelection = true;
        state.dragOffset = { x: wx, y: wy };
        // Store original positions for all selected tracks
        state.originalPositions = {};
        for (const trackId of state.selectedTracks) {
          state.originalPositions[trackId] = [...state.layout.tracks[trackId].position];
        }
      } else {
        console.log('Starting single drag for track:', clickedTrack, 'at', wx, wy);
        state.isDragging = true;
        state.dragOffset = { x: wx, y: wy };
        state.originalPosition = [...state.layout.tracks[clickedTrack].position];
      }
      updateMultiSelectProperties();
    } else {
      // No track clicked - start selection box
      state.isSelecting = true;
      state.selectionStart = { x: wx, y: wy };
      state.selectionEnd = { x: wx, y: wy };
      state.selectedTracks.clear();
      state.selectedTrack = null;
      updateMultiSelectProperties();
    }
    draw();
    return;
  }
  
  const clickedTrack = findTrackAtPosition(wx, wy);
  
  switch (state.currentTool) {
    case 'select':
      // This case is already handled above in the select tool logic
      break;
      
    case 'pass':
    case 'buffer':
    case 'point':
    case 'signal':
      if (!clickedTrack) {
        const snappedX = snapToGrid(wx);
        const snappedY = snapToGrid(wy);
        createTrack(state.currentTool, snappedX, snappedY);
      }
      break;
      
    case 'connection':
      if (clickedTrack) {
        if (!state.connectionStart) {
          state.connectionStart = clickedTrack;
          setStatus(`Connection: Selected ${clickedTrack}, click another track to connect`);
        } else {
          if (state.connectionStart !== clickedTrack) {
            addConnection(state.connectionStart, clickedTrack);
          }
          state.connectionStart = null;
          setStatus('Connection created');
        }
      }
      break;
      
    case 'delete':
      if (clickedTrack) {
        deleteTrack(clickedTrack);
      }
      break;
  }
  
  draw();
}

function handleMouseMove(e) {
  const rect = state.canvas.getBoundingClientRect();
  const x = e.clientX - rect.left;
  const y = e.clientY - rect.top;
  
  state.mouseX = x;
  state.mouseY = y;
  
  const wx = screenToWorldX(x);
  const wy = screenToWorldY(y);
  
  // Update coordinates display
  document.getElementById('mouse-coords').textContent = 
    `x: ${wx.toFixed(1)}, y: ${wy.toFixed(1)}`;
  
  if (state.isPanning) {
    state.viewOffsetX = x - state.panStartX;
    state.viewOffsetY = y - state.panStartY;
    draw();
    return;
  }
  
  // Update single track dragging
  if (state.isDragging && state.selectedTrack) {
    console.log('Dragging track:', state.selectedTrack, 'from', state.dragOffset, 'to', wx, wy);
    const track = state.layout.tracks[state.selectedTrack];
    if (track) {
      // Use the original position as the base, not the current position
      const deltaX = wx - state.dragOffset.x;
      const deltaY = wy - state.dragOffset.y;
      
      const newX = state.originalPosition[0] + deltaX;
      const newY = state.originalPosition[1] + deltaY;
      
      track.position[0] = state.snapToGrid ? snapToGrid(newX) : newX;
      track.position[1] = state.snapToGrid ? snapToGrid(newY) : newY;
      
      updatePropertyInputs();
      draw();
    }
    return;
  }
  
  // Update multi-select dragging
  if (state.isDraggingSelection && state.selectedTracks.size > 0) {
    console.log('Dragging', state.selectedTracks.size, 'tracks from', state.dragOffset, 'to', wx, wy);
    const deltaX = wx - state.dragOffset.x;
    const deltaY = wy - state.dragOffset.y;
    
    // Update all selected tracks
    for (const trackId of state.selectedTracks) {
      const track = state.layout.tracks[trackId];
      if (track && state.originalPositions[trackId]) {
        const newX = state.originalPositions[trackId][0] + deltaX;
        const newY = state.originalPositions[trackId][1] + deltaY;
        
        track.position[0] = state.snapToGrid ? snapToGrid(newX) : newX;
        track.position[1] = state.snapToGrid ? snapToGrid(newY) : newY;
      }
    }
    
    updateMultiSelectProperties();
    draw();
    return;
  }
  
  // Update selection box
  if (state.isSelecting) {
    state.selectionEnd = { x: wx, y: wy };
    updateSelectionBox();
    draw();
    return;
  }
  
  // Update insert preview
  if (state.insertPreview && state.isDraggingInsert) {
    state.insertPosition = { x: wx, y: wy };
    draw();
    return;
  }
  
  // Update hover state
  const hoveredTrack = findTrackAtPosition(wx, wy);
  if (hoveredTrack !== state.hoveredTrack) {
    state.hoveredTrack = hoveredTrack;
    draw();
  }
  
  // Draw connection preview
  if (state.currentTool === 'connection' && state.connectionStart) {
    draw();
  }
}

function handleMouseUp(e) {
  if (state.isPanning) {
    state.isPanning = false;
    state.canvas.style.cursor = state.currentTool === 'select' ? 'default' : 'crosshair';
  }
  
  if (state.isDragging) {
    state.isDragging = false;
    saveHistory();
  }
  
  if (state.isDraggingSelection) {
    state.isDraggingSelection = false;
    state.originalPositions = null; // Clean up original positions
    saveHistory();
  }
  
  if (state.isSelecting) {
    state.isSelecting = false;
    updateSelectionBox();
    draw();
  }
}

function handleWheel(e) {
  e.preventDefault();
  
  const rect = state.canvas.getBoundingClientRect();
  const x = e.clientX - rect.left;
  const y = e.clientY - rect.top;
  
  const oldScale = state.viewScale;
  const zoomFactor = e.deltaY < 0 ? 1.1 : 0.9;
  state.viewScale *= zoomFactor;
  state.viewScale = Math.max(1, Math.min(100, state.viewScale));
  
  // Zoom towards mouse position
  const wx = (x - state.viewOffsetX) / oldScale;
  const wy = (y - state.viewOffsetY) / oldScale;
  
  state.viewOffsetX = x - wx * state.viewScale;
  state.viewOffsetY = y - wy * state.viewScale;
  
  updateZoomDisplay();
  draw();
}

function findTrackAtPosition(wx, wy, tolerance = 1.5) {
  const tol = tolerance / state.viewScale * 20; // Adjust for screen space
  
  for (const [id, track] of Object.entries(state.layout.tracks)) {
    const [tx, ty] = track.position;
    const dist = Math.sqrt((wx - tx) ** 2 + (wy - ty) ** 2);
    if (dist < tol) return id;
  }
  
  return null;
}

// ============================================================================
// TRACK OPERATIONS
// ============================================================================

function createTrack(type, x, y) {
  const prefix = type === 'pass' ? 'N' : 
                 type === 'buffer' ? 'B' : 
                 type === 'point' ? 'P' : 'S';
  
  const id = `${prefix}${state.counters[prefix]}`;
  state.counters[prefix]++;
  
  const track = {
    type: type,
    position: [x, y],
    connections: []
  };
  
  // Add type-specific properties
  if (type === 'buffer' || type === 'signal') {
    track.label = '';
  }
  
  if (type === 'point') {
    track.label = '';
  }
  
  state.layout.tracks[id] = track;
  
  saveHistory();
  updateStats();
  setStatus(`Created ${type} track: ${id}`);
  
  // Auto-select new track
  state.selectedTrack = id;
  showProperties(id);
  
  draw();
}

function deleteTrack(id) {
  const track = state.layout.tracks[id];
  if (!track) return;
  
  // Remove connections from other tracks
  for (const [otherId, otherTrack] of Object.entries(state.layout.tracks)) {
    if (otherTrack.connections) {
      otherTrack.connections = otherTrack.connections.filter(c => c !== id);
    }
  }
  
  delete state.layout.tracks[id];
  
  if (state.selectedTrack === id) {
    state.selectedTrack = null;
    hideProperties();
  }
  
  saveHistory();
  updateStats();
  setStatus(`Deleted track: ${id}`);
  draw();
}

function deleteSelectedTrack() {
  if (state.selectedTrack) {
    deleteTrack(state.selectedTrack);
  }
}

function canAddConnection(track, newConnectionId) {
  if (!track || !track.connections) return false;
  
  // Check if connection already exists
  if (track.connections.includes(newConnectionId)) return false;
  
  // Enforce connection rules based on track type
  switch (track.type) {
    case 'buffer':
      // Buffers can have exactly 1 connection
      return track.connections.length < 1;
      
    case 'pass':
      // Pass nodes can have 0 or 2 connections (stored bidirectionally in UI)
      return track.connections.length < 2;
      
    case 'point':
      // Points can have exactly 3 connections
      return track.connections.length < 3;
      
    case 'signal':
      // Signals can have 0 or 1 connection (0 if they have a triggernode)
      return track.connections.length < 1;
      
    case 'pointsignal':
      // Point signals can have 0 or 1 connection (0 if they have a triggernode)
      return track.connections.length < 1;
      
    default:
      return false;
  }
}

function addConnection(id1, id2) {
  const track1 = state.layout.tracks[id1];
  const track2 = state.layout.tracks[id2];
  
  if (!track1 || !track2) return;
  
  // Initialize connections arrays if needed
  if (!track1.connections) track1.connections = [];
  if (!track2.connections) track2.connections = [];
  
  // Check connection limits before adding
  if (!canAddConnection(track1, id2)) {
    console.warn(`Cannot add connection: ${id1} (${track1.type}) already has maximum connections`);
    return;
  }
  if (!canAddConnection(track2, id1)) {
    console.warn(`Cannot add connection: ${id2} (${track2.type}) already has maximum connections`);
    return;
  }
  
  // Add bidirectional connection for UI (both directions for visual feedback)
  if (!track1.connections.includes(id2)) {
    track1.connections.push(id2);
  }
  if (!track2.connections.includes(id1)) {
    track2.connections.push(id1);
  }
  
  // Auto-order point connections after adding
  if (track1.type === 'point') {
    autoOrderPointConnections(id1);
  }
  if (track2.type === 'point') {
    autoOrderPointConnections(id2);
  }
  
  saveHistory();
  draw();
  
  if (state.selectedTrack === id1 || state.selectedTrack === id2) {
    updatePropertyInputs();
  }
}

// ============================================================================
// POINT CONNECTION ORDERING
// ============================================================================

/**
 * Auto-order point connections based on geometry:
 * - First connection: "always live" leg (the through route)
 * - Second & third: switchable legs (forming acute angle)
 */
function autoOrderPointConnections(pointId) {
  const point = state.layout.tracks[pointId];
  if (!point || point.type !== 'point' || !point.connections || point.connections.length !== 3) {
    return;
  }
  
  const [px, py] = point.position;
  
  // Calculate angles to each connected track
  const connections = point.connections.map(connId => {
    const track = state.layout.tracks[connId];
    if (!track) return null;
    
    const [cx, cy] = track.position;
    const dx = cx - px;
    const dy = cy - py;
    const angle = Math.atan2(dy, dx); // radians, -π to π
    
    return { id: connId, angle: angle };
  }).filter(c => c !== null);
  
  if (connections.length !== 3) return;
  
  // Sort by angle
  connections.sort((a, b) => a.angle - b.angle);
  
  // Find the pair with the smallest angle difference (acute angle)
  // These are the switchable legs
  let minDiff = Infinity;
  let acutePairIndex = 0;
  
  for (let i = 0; i < 3; i++) {
    const next = (i + 1) % 3;
    let diff = connections[next].angle - connections[i].angle;
    
    // Normalize to [0, 2π]
    if (diff < 0) diff += Math.PI * 2;
    
    // Take the smaller of the two possible angles
    const actualDiff = Math.min(diff, Math.PI * 2 - diff);
    
    if (actualDiff < minDiff) {
      minDiff = actualDiff;
      acutePairIndex = i;
    }
  }
  
  // The "always live" leg is the one NOT in the acute pair
  const alwaysLiveIndex = (acutePairIndex + 2) % 3; // The one opposite the acute pair start
  
  // Reorder: always-live first, then the two switchable legs
  const switchable1 = acutePairIndex;
  const switchable2 = (acutePairIndex + 1) % 3;
  
  point.connections = [
    connections[alwaysLiveIndex].id,
    connections[switchable1].id,
    connections[switchable2].id
  ];
}

function removeConnection(id1, id2) {
  const track1 = state.layout.tracks[id1];
  const track2 = state.layout.tracks[id2];
  
  if (!track1 || !track2) return;
  
  if (track1.connections) {
    track1.connections = track1.connections.filter(c => c !== id2);
  }
  if (track2.connections) {
    track2.connections = track2.connections.filter(c => c !== id1);
  }
  
  saveHistory();
  draw();
}

// ============================================================================
// PROPERTIES PANEL
// ============================================================================

function showProperties(trackId) {
  const track = state.layout.tracks[trackId];
  if (!track) return;
  
  document.getElementById('no-selection').style.display = 'none';
  document.getElementById('track-properties').style.display = 'block';
  
  updatePropertyInputs();
  updatePropertiesPanel();
}

function hideProperties() {
  document.getElementById('no-selection').style.display = 'block';
  document.getElementById('track-properties').style.display = 'none';
}

function updatePropertyInputs() {
  if (!state.selectedTrack) return;
  
  const track = state.layout.tracks[state.selectedTrack];
  if (!track) return;
  
  document.getElementById('prop-id').value = state.selectedTrack;
  document.getElementById('prop-type').value = track.type;
  document.getElementById('prop-label').value = track.label || '';
  document.getElementById('prop-x').value = track.position[0].toFixed(1);
  document.getElementById('prop-y').value = track.position[1].toFixed(1);
  
  // Update connections list
  const connectionsList = document.getElementById('connections-list');
  connectionsList.innerHTML = '';
  
  if (track.connections && track.connections.length > 0) {
    // Show ordering hint for points
    if (track.type === 'point' && track.connections.length === 3) {
      const hint = document.createElement('div');
      hint.style.cssText = 'font-size: 11px; color: #7f8c8d; margin-bottom: 8px; padding: 6px; background: #ecf0f1; border-radius: 3px;';
      hint.innerHTML = '<strong>Order:</strong> 1=Always live, 2&3=Switchable legs<br><button onclick="autoOrderCurrentPoint()" style="margin-top: 4px; padding: 4px 8px; font-size: 11px;">♻ Auto-order by Angle</button>';
      connectionsList.appendChild(hint);
    }
    
    track.connections.forEach((connId, index) => {
      const item = document.createElement('div');
      item.className = 'connection-item';
      
      // Add ordering controls for points
      let orderControls = '';
      if (track.type === 'point' && track.connections.length === 3) {
        const canMoveUp = index > 0;
        const canMoveDown = index < track.connections.length - 1;
        orderControls = `
          <div style="display: flex; gap: 2px; margin-right: 4px;">
            <button onclick="moveConnectionUp(${index})" ${!canMoveUp ? 'disabled' : ''} 
                    style="padding: 2px 4px; font-size: 10px;" title="Move up">▲</button>
            <button onclick="moveConnectionDown(${index})" ${!canMoveDown ? 'disabled' : ''} 
                    style="padding: 2px 4px; font-size: 10px;" title="Move down">▼</button>
          </div>
        `;
      }
      
      const orderLabel = track.type === 'point' ? `<strong>${index + 1}.</strong> ` : '';
      
      item.innerHTML = `
        ${orderControls}
        <span>${orderLabel}${connId}</span>
        <button onclick="removeConnectionFromUI('${state.selectedTrack}', '${connId}')">✕</button>
      `;
      connectionsList.appendChild(item);
    });
  } else {
    connectionsList.innerHTML = '<div style="color: #95a5a6; font-size: 12px;">No connections</div>';
  }
  
  // Type-specific properties
  if (track.type === 'point') {
    document.getElementById('prop-coacting').value = 
      (track.coacting || []).join(', ');
  }
  
  if (track.type === 'signal') {
    document.getElementById('prop-triggernode').value = track.triggernode || '';
    document.getElementById('prop-directionvector').value = track.directionvector || '';
  }
  
  if (track.type === 'pointsignal') {
    document.getElementById('prop-point').value = track.point || '';
    document.getElementById('prop-pointleg').value = track.pointleg || '';
    document.getElementById('prop-directionvector-ps').value = track.directionvector || '';
    document.getElementById('prop-triggernode-ps').value = track.triggernode || '';
    document.getElementById('prop-leglabels').value = 
      track.leglabels ? JSON.stringify(track.leglabels) : '';
  }
}

function updatePropertiesPanel() {
  const type = document.getElementById('prop-type').value;
  
  document.getElementById('point-props').style.display = 
    type === 'point' ? 'block' : 'none';
  document.getElementById('signal-props').style.display = 
    type === 'signal' ? 'block' : 'none';
  document.getElementById('pointsignal-props').style.display = 
    type === 'pointsignal' ? 'block' : 'none';
}

function applyProperties() {
  if (!state.selectedTrack) return;
  
  const track = state.layout.tracks[state.selectedTrack];
  if (!track) return;
  
  const newId = document.getElementById('prop-id').value.trim();
  const newType = document.getElementById('prop-type').value;
  const newLabel = document.getElementById('prop-label').value.trim();
  const newX = parseFloat(document.getElementById('prop-x').value);
  const newY = parseFloat(document.getElementById('prop-y').value);
  
  // Validate ID
  if (!newId) {
    showError('id-error', 'ID cannot be empty');
    return;
  }
  
  if (newId !== state.selectedTrack && state.layout.tracks[newId]) {
    showError('id-error', 'ID already exists');
    return;
  }
  
  document.getElementById('id-error').textContent = '';
  
  // Update ID if changed
  if (newId !== state.selectedTrack) {
    // Update connections in all tracks
    for (const [id, t] of Object.entries(state.layout.tracks)) {
      if (t.connections) {
        t.connections = t.connections.map(c => c === state.selectedTrack ? newId : c);
      }
    }
    
    state.layout.tracks[newId] = track;
    delete state.layout.tracks[state.selectedTrack];
    state.selectedTrack = newId;
  }
  
  // Update properties
  track.type = newType;
  track.label = newLabel;
  track.position = [newX, newY];
  
  // Type-specific properties
  if (newType === 'point') {
    const coactingStr = document.getElementById('prop-coacting').value.trim();
    track.coacting = coactingStr ? coactingStr.split(',').map(s => s.trim()) : [];
  } else {
    delete track.coacting;
  }
  
  if (newType === 'signal') {
    track.triggernode = document.getElementById('prop-triggernode').value.trim();
    track.directionvector = document.getElementById('prop-directionvector').value.trim();
    // Clean up pointsignal-specific properties
    delete track.point;
    delete track.pointleg;
    delete track.leglabels;
  } else if (newType === 'pointsignal') {
    track.point = document.getElementById('prop-point').value.trim();
    track.pointleg = document.getElementById('prop-pointleg').value.trim();
    track.directionvector = document.getElementById('prop-directionvector-ps').value.trim();
    track.triggernode = document.getElementById('prop-triggernode-ps').value.trim();
    
    // Handle leglabels JSON
    const leglabelsStr = document.getElementById('prop-leglabels').value.trim();
    if (leglabelsStr) {
      try {
        track.leglabels = JSON.parse(leglabelsStr);
      } catch (e) {
        showError('prop-leglabels', 'Invalid JSON format');
        return;
      }
    } else {
      delete track.leglabels;
    }
  } else {
    // Clean up all signal properties for non-signal types
    delete track.point;
    delete track.pointleg;
    delete track.directionvector;
    delete track.triggernode;
    delete track.leglabels;
  }
  
  saveHistory();
  updateStats();
  setStatus('Properties updated');
  draw();
}

function showError(elementId, message) {
  const el = document.getElementById(elementId);
  if (el) {
    el.textContent = message;
  }
}

// Helper functions for connection management (called from HTML)
window.removeConnectionFromUI = function(id1, id2) {
  removeConnection(id1, id2);
  updatePropertyInputs();
};

window.moveConnectionUp = function(index) {
  if (!state.selectedTrack || index <= 0) return;
  
  const track = state.layout.tracks[state.selectedTrack];
  if (!track || !track.connections) return;
  
  // Swap with previous
  const temp = track.connections[index];
  track.connections[index] = track.connections[index - 1];
  track.connections[index - 1] = temp;
  
  saveHistory();
  updatePropertyInputs();
  setStatus('Connection order updated');
};

window.moveConnectionDown = function(index) {
  if (!state.selectedTrack) return;
  
  const track = state.layout.tracks[state.selectedTrack];
  if (!track || !track.connections || index >= track.connections.length - 1) return;
  
  // Swap with next
  const temp = track.connections[index];
  track.connections[index] = track.connections[index + 1];
  track.connections[index + 1] = temp;
  
  saveHistory();
  updatePropertyInputs();
  setStatus('Connection order updated');
};

window.autoOrderCurrentPoint = function() {
  if (!state.selectedTrack) return;
  
  const track = state.layout.tracks[state.selectedTrack];
  if (!track || track.type !== 'point') return;
  
  autoOrderPointConnections(state.selectedTrack);
  saveHistory();
  updatePropertyInputs();
  draw();
  setStatus('Point connections auto-ordered by angle');
};

// ============================================================================
// VIEW CONTROLS
// ============================================================================

function zoomBy(factor) {
  const centerX = state.canvas.width / (window.devicePixelRatio || 1) / 2;
  const centerY = state.canvas.height / (window.devicePixelRatio || 1) / 2;
  
  const oldScale = state.viewScale;
  state.viewScale *= factor;
  state.viewScale = Math.max(1, Math.min(100, state.viewScale));
  
  const wx = (centerX - state.viewOffsetX) / oldScale;
  const wy = (centerY - state.viewOffsetY) / oldScale;
  
  state.viewOffsetX = centerX - wx * state.viewScale;
  state.viewOffsetY = centerY - wy * state.viewScale;
  
  updateZoomDisplay();
  draw();
}

function fitToView() {
  const tracks = Object.values(state.layout.tracks);
  if (tracks.length === 0) {
    state.viewScale = 10;
    state.viewOffsetX = state.canvas.width / (window.devicePixelRatio || 1) / 2;
    state.viewOffsetY = state.canvas.height / (window.devicePixelRatio || 1) / 2;
    updateZoomDisplay();
    draw();
    return;
  }
  
  let minX = Infinity, maxX = -Infinity;
  let minY = Infinity, maxY = -Infinity;
  
  tracks.forEach(track => {
    const [x, y] = track.position;
    minX = Math.min(minX, x);
    maxX = Math.max(maxX, x);
    minY = Math.min(minY, y);
    maxY = Math.max(maxY, y);
  });
  
  const padding = 20; // meters
  const rangeX = (maxX - minX) + padding * 2;
  const rangeY = (maxY - minY) + padding * 2;
  
  const w = state.canvas.width / (window.devicePixelRatio || 1);
  const h = state.canvas.height / (window.devicePixelRatio || 1);
  
  const scaleX = w / rangeX;
  const scaleY = h / rangeY;
  state.viewScale = Math.min(scaleX, scaleY) * 0.9;
  
  const centerX = (minX + maxX) / 2;
  const centerY = (minY + maxY) / 2;
  
  state.viewOffsetX = w / 2 - centerX * state.viewScale;
  state.viewOffsetY = h / 2 - centerY * state.viewScale;
  
  updateZoomDisplay();
  draw();
}

function updateZoomDisplay() {
  const percentage = Math.round((state.viewScale / 10) * 100);
  document.getElementById('zoom-level').textContent = `Zoom: ${percentage}%`;
}

// ============================================================================
// FILE OPERATIONS
// ============================================================================

function newLayout() {
  if (Object.keys(state.layout.tracks).length > 0) {
    if (!confirm('Create new layout? This will clear the current layout.')) {
      return;
    }
  }
  
  state.layout = {
    compass: "N",
    comments: "",
    tracks: {},
    options: [],
    features: {}
  };
  
  state.selectedTrack = null;
  state.counters = { N: 1, B: 1, P: 1, S: 1 };
  state.history = [];
  state.historyIndex = -1;
  
  hideProperties();
  updateStats();
  draw();
  setStatus('New layout created');
}

function handleFileImport(e) {
  const file = e.target.files[0];
  if (!file) return;
  
  const reader = new FileReader();
  reader.onload = (event) => {
    try {
      const json = JSON.parse(event.target.result);
      importLayout(json);
      setStatus(`Imported: ${file.name}`);
    } catch (err) {
      alert('Error parsing JSON: ' + err.message);
      setStatus('Import failed', true);
    }
  };
  reader.readAsText(file);
  
  // Reset file input
  e.target.value = '';
}

function importLayout(json) {
  // Validate basic structure
  if (!json.tracks || typeof json.tracks !== 'object') {
    alert('Invalid layout JSON: missing tracks object');
    return;
  }
  
  state.layout = {
    compass: json.compass || 'N',
    comments: json.comments || json.comment || '',
    tracks: json.tracks,
    options: json.options || [],
    features: json.features || {}
  };
  
  // Update counters based on existing IDs
  for (const id of Object.keys(state.layout.tracks)) {
    const prefix = id[0];
    const num = parseInt(id.substring(1)) || 0;
    if (state.counters[prefix] !== undefined) {
      state.counters[prefix] = Math.max(state.counters[prefix], num + 1);
    }
  }
  
  // Update UI
  document.getElementById('layout-compass').value = state.layout.compass;
  document.getElementById('layout-comments').value = state.layout.comments;
  
  state.selectedTrack = null;
  hideProperties();
  updateStats();
  fitToView();
  
  saveHistory();
}

function convertToSingleDirectionConnections(layout) {
  // Create a copy of the layout with single-direction connections
  const singleDirectionLayout = JSON.parse(JSON.stringify(layout));
  
  // For each track, keep only one connection (the first one)
  // EXCEPT for points which need all 3 connections
  for (const [id, track] of Object.entries(singleDirectionLayout.tracks)) {
    if (track.connections && track.connections.length > 0) {
      if (track.type === 'point') {
        // Points need all 3 connections - keep them all
        // (railway.js doesn't infer point connections)
      } else {
        // For other track types, keep only the first connection
        track.connections = [track.connections[0]];
      }
    }
  }
  
  return singleDirectionLayout;
}

function exportJSON() {
  // Clean up the layout before export
  const cleanLayout = {
    compass: state.layout.compass,
    tracks: {}
  };
  
  if (state.layout.comments) {
    cleanLayout.comments = state.layout.comments;
  }
  
  // Copy tracks, removing empty properties
  for (const [id, track] of Object.entries(state.layout.tracks)) {
    const cleanTrack = {
      type: track.type,
      position: track.position
    };
    
    if (track.label) cleanTrack.label = track.label;
    if (track.connections && track.connections.length > 0) {
      cleanTrack.connections = track.connections;
    }
    if (track.coacting && track.coacting.length > 0) {
      cleanTrack.coacting = track.coacting;
    }
    if (track.point) cleanTrack.point = track.point;
    if (track.pointleg) cleanTrack.pointleg = track.pointleg;
    if (track.directionvector) cleanTrack.directionvector = track.directionvector;
    if (track.triggernode) cleanTrack.triggernode = track.triggernode;
    
    cleanLayout.tracks[id] = cleanTrack;
  }
  
  if (state.layout.options && state.layout.options.length > 0) {
    cleanLayout.options = state.layout.options;
  }
  
  if (state.layout.features && Object.keys(state.layout.features).length > 0) {
    cleanLayout.features = state.layout.features;
  }
  
  // Convert to single-direction connections for railway.js compatibility
  const singleDirectionLayout = convertToSingleDirectionConnections(cleanLayout);
  
  const json = JSON.stringify(singleDirectionLayout, null, 2);
  const blob = new Blob([json], { type: 'application/json' });
  const url = URL.createObjectURL(blob);
  
  const a = document.createElement('a');
  a.href = url;
  a.download = 'layout.json';
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
  URL.revokeObjectURL(url);
  
  setStatus('Layout exported to layout.json');
}

// ============================================================================
// VALIDATION
// ============================================================================

function validateLayout() {
  const errors = [];
  const warnings = [];
  
  const trackIds = new Set(Object.keys(state.layout.tracks));
  
  for (const [id, track] of Object.entries(state.layout.tracks)) {
    // Check position
    if (!Array.isArray(track.position) || track.position.length !== 2) {
      errors.push(`${id}: Invalid position format`);
    }
    
    // Check connections
    if (track.connections) {
      for (const connId of track.connections) {
        if (!trackIds.has(connId)) {
          errors.push(`${id}: References non-existent track "${connId}"`);
        } else {
          // Check bidirectional
          const connTrack = state.layout.tracks[connId];
          if (!connTrack.connections || !connTrack.connections.includes(id)) {
            warnings.push(`${id}: Connection to "${connId}" is not bidirectional`);
          }
        }
      }
      
      // Check connection count by type
      if (track.type === 'buffer' && track.connections.length !== 1) {
        warnings.push(`${id}: Buffer should have exactly 1 connection (has ${track.connections.length})`);
      }
      if (track.type === 'pass' && track.connections.length > 2) {
        warnings.push(`${id}: Pass node should have 0 or 2 connections (has ${track.connections.length})`);
      }
      if (track.type === 'point' && track.connections.length !== 3) {
        warnings.push(`${id}: Point should have exactly 3 connections (has ${track.connections.length})`);
      }
      if (track.type === 'signal' && track.connections.length > 1) {
        warnings.push(`${id}: Signal should have 0 or 1 connection (has ${track.connections.length})`);
      }
      if (track.type === 'pointsignal' && track.connections.length > 1) {
        warnings.push(`${id}: Point signal should have 0 or 1 connection (has ${track.connections.length})`);
      }
    } else if (track.type !== 'signal' && track.type !== 'pointsignal') {
      warnings.push(`${id}: Has no connections`);
    }
    
    // Check type-specific properties
    if (track.type === 'point' && track.coacting) {
      for (const coactId of track.coacting) {
        if (!trackIds.has(coactId)) {
          errors.push(`${id}: Coacting point "${coactId}" does not exist`);
        }
      }
    }
    
    if (track.type === 'pointsignal') {
      if (track.point && !trackIds.has(track.point)) {
        errors.push(`${id}: References non-existent point "${track.point}"`);
      }
      if (track.pointleg && !trackIds.has(track.pointleg)) {
        errors.push(`${id}: References non-existent pointleg "${track.pointleg}"`);
      }
      if (track.triggernode && !trackIds.has(track.triggernode)) {
        errors.push(`${id}: References non-existent triggernode "${track.triggernode}"`);
      }
    }
  }
  
  // Show results
  showValidationResults(errors, warnings);
}

function showValidationResults(errors, warnings) {
  const modal = document.getElementById('validation-modal');
  const resultsDiv = document.getElementById('validation-results');
  
  resultsDiv.innerHTML = '';
  
  if (errors.length === 0 && warnings.length === 0) {
    resultsDiv.innerHTML = '<div class="validation-item success">✓ No issues found - layout is valid!</div>';
    document.getElementById('validation-status').textContent = '✓ Valid';
    document.getElementById('validation-status').className = '';
  } else {
    document.getElementById('validation-status').textContent = `⚠ ${errors.length} errors, ${warnings.length} warnings`;
    document.getElementById('validation-status').className = 'error';
    
    if (errors.length > 0) {
      resultsDiv.innerHTML += '<h4 style="color: #e74c3c; margin-bottom: 10px;">Errors:</h4>';
      errors.forEach(err => {
        resultsDiv.innerHTML += `<div class="validation-item error">✕ ${err}</div>`;
      });
    }
    
    if (warnings.length > 0) {
      resultsDiv.innerHTML += '<h4 style="color: #f39c12; margin: 15px 0 10px;">Warnings:</h4>';
      warnings.forEach(warn => {
        resultsDiv.innerHTML += `<div class="validation-item warning">⚠ ${warn}</div>`;
      });
    }
  }
  
  modal.style.display = 'flex';
}

function closeValidationModal() {
  document.getElementById('validation-modal').style.display = 'none';
}

// ============================================================================
// STATISTICS
// ============================================================================

function updateStats() {
  const tracks = Object.values(state.layout.tracks);
  const stats = {
    total: tracks.length,
    buffer: tracks.filter(t => t.type === 'buffer').length,
    pass: tracks.filter(t => t.type === 'pass').length,
    point: tracks.filter(t => t.type === 'point').length,
    signal: tracks.filter(t => t.type === 'signal' || t.type === 'pointsignal').length
  };
  
  document.getElementById('stat-total').textContent = stats.total;
  document.getElementById('stat-buffers').textContent = stats.buffer;
  document.getElementById('stat-pass').textContent = stats.pass;
  document.getElementById('stat-points').textContent = stats.point;
  document.getElementById('stat-signals').textContent = stats.signal;
}

// ============================================================================
// HISTORY (UNDO/REDO)
// ============================================================================

function saveHistory() {
  // Remove any redo history
  state.history = state.history.slice(0, state.historyIndex + 1);
  
  // Add current state
  const snapshot = JSON.parse(JSON.stringify(state.layout));
  state.history.push(snapshot);
  
  // Limit history size
  if (state.history.length > state.maxHistory) {
    state.history.shift();
  } else {
    state.historyIndex++;
  }
}

function undo() {
  if (state.historyIndex > 0) {
    state.historyIndex--;
    state.layout = JSON.parse(JSON.stringify(state.history[state.historyIndex]));
    updateAfterHistoryChange();
    setStatus('Undo');
  }
}

function redo() {
  if (state.historyIndex < state.history.length - 1) {
    state.historyIndex++;
    state.layout = JSON.parse(JSON.stringify(state.history[state.historyIndex]));
    updateAfterHistoryChange();
    setStatus('Redo');
  }
}

function updateAfterHistoryChange() {
  if (state.selectedTrack && !state.layout.tracks[state.selectedTrack]) {
    state.selectedTrack = null;
    hideProperties();
  } else if (state.selectedTrack) {
    updatePropertyInputs();
  }
  
  updateStats();
  draw();
}

// ============================================================================
// STATUS
// ============================================================================

function setStatus(message, isError = false) {
  const el = document.getElementById('status-message');
  el.textContent = message;
  el.style.color = isError ? '#e74c3c' : '#ecf0f1';
}

// ============================================================================
// INSERT LAYOUT FUNCTIONALITY
// ============================================================================

function handleInsertFile(e) {
  const file = e.target.files[0];
  if (!file) return;
  
  const reader = new FileReader();
  reader.onload = (event) => {
    try {
      const json = JSON.parse(event.target.result);
      if (!json.tracks || typeof json.tracks !== 'object') {
        alert('Invalid layout JSON: missing tracks object');
        return;
      }
      
      state.insertLayout = json;
      showInsertModal();
      updateInsertPreview();
    } catch (err) {
      alert('Error parsing JSON: ' + err.message);
    }
  };
  reader.readAsText(file);
  
  // Reset file input
  e.target.value = '';
}

function showInsertModal() {
  const modal = document.getElementById('insert-modal');
  modal.style.display = 'flex';
  
  // Set default position to cursor position
  const wx = screenToWorldX(state.mouseX || state.canvas.width / 2);
  const wy = screenToWorldY(state.mouseY || state.canvas.height / 2);
  
  document.getElementById('insert-x').value = snapToGrid(wx).toFixed(1);
  document.getElementById('insert-y').value = snapToGrid(wy).toFixed(1);
  document.getElementById('insert-rotation').value = '0';
  
  // Auto-calculate ID offset
  const autoOffset = calculateAutoIdOffset();
  document.getElementById('insert-id-offset').value = autoOffset;
  
  updateInsertPreview();
}

function closeInsertModal() {
  document.getElementById('insert-modal').style.display = 'none';
  state.insertLayout = null;
}


function updateInsertPreview() {
  if (!state.insertLayout) return;
  
  const tracks = Object.keys(state.insertLayout.tracks);
  const x = parseFloat(document.getElementById('insert-x').value) || 0;
  const y = parseFloat(document.getElementById('insert-y').value) || 0;
  const rotation = parseInt(document.getElementById('insert-rotation').value) || 0;
  const idOffset = parseInt(document.getElementById('insert-id-offset').value) || 0;
  
  let preview = `Will insert ${tracks.length} tracks at position (${x}, ${y})`;
  if (rotation !== 0) preview += `, rotated ${rotation}°`;
  if (idOffset > 0) preview += `, with ID offset +${idOffset}`;
  
  preview += '\n\nTracks: ';
  preview += tracks.slice(0, 10).join(', ');
  if (tracks.length > 10) preview += `... and ${tracks.length - 10} more`;
  
  document.getElementById('insert-preview').textContent = preview;
}

function applyInsert() {
  if (!state.insertLayout) return;
  
  const x = parseFloat(document.getElementById('insert-x').value) || 0;
  const y = parseFloat(document.getElementById('insert-y').value) || 0;
  const rotation = parseInt(document.getElementById('insert-rotation').value) || 0;
  const idOffset = parseInt(document.getElementById('insert-id-offset').value) || 0;
  
  // Calculate next available ID numbers
  const nextIds = calculateNextAvailableIds(idOffset);
  
  // Store track count before insertion
  const trackCount = Object.keys(state.insertLayout.tracks).length;
  
  // Insert the layout
  insertLayoutAtPosition(state.insertLayout, x, y, rotation, nextIds);
  
  closeInsertModal();
  updateStats();
  draw();
  setStatus(`Inserted layout with ${trackCount} tracks`);
}

function calculateAutoIdOffset() {
  const existingIds = Object.keys(state.layout.tracks);
  let maxNumber = 0;
  
  // Find the highest number across all track IDs
  for (const id of existingIds) {
    const match = id.match(/^([A-Z]+)(\d+)$/);
    if (match) {
      const num = parseInt(match[2]);
      maxNumber = Math.max(maxNumber, num);
    }
  }
  
  // Return 1 more than the highest number found
  return maxNumber + 1;
}

function calculateNextAvailableIds(offset) {
  const existingIds = new Set(Object.keys(state.layout.tracks));
  const counters = { N: 1, B: 1, P: 1, S: 1 };
  
  // Find highest numbers for each prefix
  for (const id of existingIds) {
    const match = id.match(/^([A-Z]+)(\d+)$/);
    if (match) {
      const prefix = match[1];
      const num = parseInt(match[2]);
      if (counters[prefix] !== undefined) {
        counters[prefix] = Math.max(counters[prefix], num + 1);
      }
    }
  }
  
  // Add offset
  for (const prefix in counters) {
    counters[prefix] += offset;
  }
  
  return counters;
}

function insertLayoutAtPosition(layout, offsetX, offsetY, rotation, nextIds) {
  const cos = Math.cos(rotation * Math.PI / 180);
  const sin = Math.sin(rotation * Math.PI / 180);
  
  const idMap = new Map();
  const newTracks = {};
  
  // First pass: create new tracks with transformed positions and new IDs
  for (const [oldId, track] of Object.entries(layout.tracks)) {
    const newId = generateNewId(oldId, nextIds);
    idMap.set(oldId, newId);
    
    const [oldX, oldY] = track.position;
    
    // Apply rotation around origin, then translate
    const rotatedX = oldX * cos - oldY * sin;
    const rotatedY = oldX * sin + oldY * cos;
    
    const newX = rotatedX + offsetX;
    const newY = rotatedY + offsetY;
    
    newTracks[newId] = {
      ...track,
      position: [newX, newY]
    };
  }
  
  // Second pass: update connections to use new IDs
  for (const [newId, track] of Object.entries(newTracks)) {
    if (track.connections) {
      track.connections = track.connections.map(oldConnId => {
        const newConnId = idMap.get(oldConnId);
        return newConnId || oldConnId; // fallback if mapping not found
      });
    }
    
    // Update point-specific properties
    if (track.type === 'point' && track.coacting) {
      track.coacting = track.coacting.map(oldId => idMap.get(oldId) || oldId);
    }
    
    // Update signal-specific properties
    if (track.type === 'pointsignal') {
      if (track.point) track.point = idMap.get(track.point) || track.point;
      if (track.pointleg) track.pointleg = idMap.get(track.pointleg) || track.pointleg;
      if (track.triggernode) track.triggernode = idMap.get(track.triggernode) || track.triggernode;
    }
    
    if (track.type === 'signal' && track.triggernode) {
      track.triggernode = idMap.get(track.triggernode) || track.triggernode;
    }
  }
  
  // Add all new tracks to the layout
  for (const [id, track] of Object.entries(newTracks)) {
    state.layout.tracks[id] = track;
  }
  
  saveHistory();
}

function generateNewId(oldId, counters) {
  const match = oldId.match(/^([A-Z]+)(\d+)$/);
  if (!match) return oldId;
  
  const prefix = match[1];
  const num = parseInt(match[2]);
  
  if (counters[prefix] !== undefined) {
    const newNum = counters[prefix]++;
    return `${prefix}${newNum}`;
  }
  
  return oldId;
}

// ============================================================================
// DRAG AND DROP FOR INSERT
// ============================================================================

function handleDragOver(e) {
  e.preventDefault();
  e.dataTransfer.dropEffect = 'copy';
}

function handleDragEnter(e) {
  e.preventDefault();
  state.canvas.style.border = '2px dashed #3498db';
}

function handleDragLeave(e) {
  e.preventDefault();
  state.canvas.style.border = '1px solid #bdc3c7';
}

function handleDrop(e) {
  e.preventDefault();
  state.canvas.style.border = '1px solid #bdc3c7';
  
  const files = e.dataTransfer.files;
  if (files.length === 0) return;
  
  const file = files[0];
  if (!file.name.endsWith('.json')) {
    alert('Please drop a JSON file');
    return;
  }
  
  const reader = new FileReader();
  reader.onload = (event) => {
    try {
      const json = JSON.parse(event.target.result);
      if (!json.tracks || typeof json.tracks !== 'object') {
        alert('Invalid layout JSON: missing tracks object');
        return;
      }
      
      // Start insert preview mode
      state.insertLayout = json;
      state.insertPreview = json;
      state.isDraggingInsert = true;
      
      // Set auto-calculated ID offset
      const autoOffset = calculateAutoIdOffset();
      state.insertIdOffset = autoOffset;
      
      // Set initial position to drop location
      const rect = state.canvas.getBoundingClientRect();
      const x = e.clientX - rect.left;
      const y = e.clientY - rect.top;
      const wx = screenToWorldX(x);
      const wy = screenToWorldY(y);
      
      // Snap to grid if enabled
      const snappedX = state.snapToGrid ? snapToGrid(wx) : wx;
      const snappedY = state.snapToGrid ? snapToGrid(wy) : wy;
      state.insertPosition = { x: snappedX, y: snappedY };
      state.insertRotation = 0;
      
      const snapText = state.snapToGrid ? ' (snapped to grid)' : '';
      setStatus(`Drag to position, right-click to rotate, left-click to place${snapText}`);
      draw();
      
    } catch (err) {
      alert('Error parsing JSON: ' + err.message);
    }
  };
  reader.readAsText(file);
}


function handleMouseUp(e) {
  if (state.isPanning) {
    state.isPanning = false;
    state.canvas.style.cursor = 'grab';
    return;
  }
  
  if (state.isDragging) {
    state.isDragging = false;
    saveHistory();
    updateStats();
  }
  
  if (state.isSelecting) {
    state.isSelecting = false;
    draw();
  }
  
  if (state.isDraggingSelection) {
    state.isDraggingSelection = false;
    saveHistory();
    updateStats();
  }
}

// Handle right-click rotation for insert preview
function handleContextMenu(e) {
  if (state.insertPreview && state.isDraggingInsert) {
    e.preventDefault();
    
    // Cycle through rotations: 0° -> 90° -> 180° -> 270° -> 0°
    state.insertRotation = (state.insertRotation + 90) % 360;
    
    setStatus(`Rotation: ${state.insertRotation}° - Drag to position, right-click to rotate, left-click to place`);
    draw();
  }
}

function applyInsertAtPosition(x, y) {
  if (!state.insertLayout) return;
  
  const rotation = state.insertRotation;
  const idOffset = state.insertIdOffset || 0;
  
  // Calculate next available ID numbers
  const nextIds = calculateNextAvailableIds(idOffset);
  
  // Insert the layout
  insertLayoutAtPosition(state.insertLayout, x, y, rotation, nextIds);
  
  // Clean up
  const insertedCount = Object.keys(state.insertLayout?.tracks || {}).length;
  state.insertLayout = null;
  state.insertPreview = null;
  state.isDraggingInsert = false;
  
  // Normalize node numbering
  normalizeNodeNumbering();
  
  updateStats();
  draw();
  setStatus(`Inserted layout with ${insertedCount} tracks and normalized numbering`);
}

function drawInsertPreview(ctx) {
  if (!state.insertPreview || !state.insertLayout) return;
  
  const offsetX = state.insertPosition.x;
  const offsetY = state.insertPosition.y;
  const rotation = state.insertRotation;
  
  ctx.save();
  
  // Apply rotation and translation
  const cos = Math.cos(rotation * Math.PI / 180);
  const sin = Math.sin(rotation * Math.PI / 180);
  
  // Draw grid snap indicator if enabled
  if (state.snapToGrid) {
    const snapSx = worldToScreenX(offsetX);
    const snapSy = worldToScreenY(offsetY);
    
    ctx.save();
    ctx.strokeStyle = '#e74c3c';
    ctx.lineWidth = 2;
    ctx.setLineDash([3, 3]);
    ctx.beginPath();
    ctx.arc(snapSx, snapSy, 12, 0, 2 * Math.PI);
    ctx.stroke();
    ctx.restore();
  }
  
  // Draw preview tracks with semi-transparent styling
  ctx.globalAlpha = 0.7;
  ctx.strokeStyle = '#3498db';
  ctx.lineWidth = 3;
  ctx.setLineDash([10, 5]);
  
  for (const [id, track] of Object.entries(state.insertLayout.tracks)) {
    const [oldX, oldY] = track.position;
    
    // Apply rotation around origin, then translate
    const rotatedX = oldX * cos - oldY * sin;
    const rotatedY = oldX * sin + oldY * cos;
    
    const newX = rotatedX + offsetX;
    const newY = rotatedY + offsetY;
    
    const sx = worldToScreenX(newX);
    const sy = worldToScreenY(newY);
    
    // Draw track as circle
    ctx.beginPath();
    ctx.arc(sx, sy, 8, 0, 2 * Math.PI);
    ctx.stroke();
    
    // Draw track label
    ctx.fillStyle = '#3498db';
    ctx.font = '12px Arial';
    ctx.textAlign = 'center';
    ctx.fillText(id, sx, sy - 12);
  }
  
  // Draw connections
  ctx.strokeStyle = '#3498db';
  ctx.lineWidth = 2;
  ctx.setLineDash([5, 5]);
  
  for (const [id, track] of Object.entries(state.insertLayout.tracks)) {
    if (!track.connections) continue;
    
    const [oldX, oldY] = track.position;
    const rotatedX = oldX * cos - oldY * sin;
    const rotatedY = oldX * sin + oldY * cos;
    const newX = rotatedX + offsetX;
    const newY = rotatedY + offsetY;
    
    const startSx = worldToScreenX(newX);
    const startSy = worldToScreenY(newY);
    
    for (const connId of track.connections) {
      const connTrack = state.insertLayout.tracks[connId];
      if (!connTrack) continue;
      
      const [connOldX, connOldY] = connTrack.position;
      const connRotatedX = connOldX * cos - connOldY * sin;
      const connRotatedY = connOldX * sin + connOldY * cos;
      const connNewX = connRotatedX + offsetX;
      const connNewY = connRotatedY + offsetY;
      
      const endSx = worldToScreenX(connNewX);
      const endSy = worldToScreenY(connNewY);
      
      ctx.beginPath();
      ctx.moveTo(startSx, startSy);
      ctx.lineTo(endSx, endSy);
      ctx.stroke();
    }
  }
  
  ctx.restore();
}

// ============================================================================
// COMPREHENSIVE FUNCTION BACKUP FOR LAYOUT CREATOR
// ============================================================================

// MULTI-SELECT FUNCTIONALITY
function updateMultiSelectProperties() {
  if (state.selectedTracks.size === 0) {
    hideProperties();
    return;
  }
  
  if (state.selectedTracks.size === 1) {
    const trackId = Array.from(state.selectedTracks)[0];
    state.selectedTrack = trackId;
    showProperties(trackId);
    return;
  }
  
  // Multi-select properties
  showMultiSelectProperties();
}

function showMultiSelectProperties() {
  const panel = document.getElementById('properties-panel');
  if (!panel) return;
  
  panel.style.display = 'block';
  
  const title = document.getElementById('properties-title');
  if (title) {
    title.textContent = `Multi-Select (${state.selectedTracks.size} tracks)`;
  }
  
  const content = document.getElementById('properties-content');
  if (!content) return;
  
  // Hide other content
  const noSelection = document.getElementById('no-selection');
  const trackProperties = document.getElementById('track-properties');
  if (noSelection) noSelection.style.display = 'none';
  if (trackProperties) trackProperties.style.display = 'none';
  
  // Show multi-select content
  content.style.display = 'block';
  content.innerHTML = `
    <div class="prop-group">
      <label>Selected Tracks:</label>
      <div style="max-height: 150px; overflow-y: auto; background: #f8f9fa; padding: 8px; border-radius: 4px;">
        ${Array.from(state.selectedTracks).map(id => {
          const track = state.layout.tracks[id];
          return `<div style="display: flex; justify-content: space-between; align-items: center; padding: 2px 0;">
            <span>${id} (${track.type})</span>
            <button onclick="removeFromSelection('${id}')" style="background: #e74c3c; color: white; border: none; border-radius: 3px; padding: 2px 6px; cursor: pointer;">✕</button>
          </div>`;
        }).join('')}
      </div>
    </div>
    
    <div class="prop-group">
      <label>Actions:</label>
      <div style="display: flex; gap: 8px; flex-wrap: wrap;">
        <button onclick="deleteSelectedTracks()" class="danger-btn" style="font-size: 12px;">Delete All</button>
        <button onclick="clearSelection()" class="secondary-btn" style="font-size: 12px;">Clear Selection</button>
        <button onclick="normalizeSelectedTracks()" class="secondary-btn" style="font-size: 12px;">Normalize IDs</button>
      </div>
    </div>
  `;
}

function removeFromSelection(trackId) {
  state.selectedTracks.delete(trackId);
  if (state.selectedTracks.size === 0) {
    hideProperties();
  } else {
    updateMultiSelectProperties();
  }
  draw();
}

function deleteSelectedTracks() {
  if (state.selectedTracks.size === 0) return;
  
  if (confirm(`Delete ${state.selectedTracks.size} selected tracks?`)) {
    for (const trackId of state.selectedTracks) {
      delete state.layout.tracks[trackId];
    }
    state.selectedTracks.clear();
    state.selectedTrack = null;
    hideProperties();
    saveHistory();
    updateStats();
    draw();
    setStatus(`Deleted ${state.selectedTracks.size} tracks`);
  }
}

function clearSelection() {
  state.selectedTracks.clear();
  state.selectedTrack = null;
  hideProperties();
  draw();
}

function normalizeSelectedTracks() {
  if (state.selectedTracks.size === 0) return;
  
  // Get selected tracks and normalize their IDs
  const selectedTracks = Array.from(state.selectedTracks);
  const newTracks = {};
  const idMap = new Map();
  
  // Create new sequential IDs for selected tracks
  let counters = { N: 1, B: 1, P: 1, S: 1 };
  
  for (const oldId of selectedTracks) {
    const track = state.layout.tracks[oldId];
    const match = oldId.match(/^([A-Z]+)(\d+)$/);
    if (match) {
      const prefix = match[1];
      const newId = `${prefix}${counters[prefix]}`;
      counters[prefix]++;
      idMap.set(oldId, newId);
      newTracks[newId] = { ...track };
    }
  }
  
  // Update connections and references
  for (const [newId, track] of Object.entries(newTracks)) {
    if (track.connections) {
      track.connections = track.connections.map(oldConnId => {
        const newConnId = idMap.get(oldConnId);
        return newConnId || oldConnId;
      });
    }
    
    if (track.type === 'point' && track.coacting) {
      track.coacting = track.coacting.map(oldId => idMap.get(oldId) || oldId);
    }
    
    if (track.type === 'pointsignal') {
      if (track.point) track.point = idMap.get(track.point) || track.point;
      if (track.pointleg) track.pointleg = idMap.get(track.pointleg) || track.pointleg;
      if (track.triggernode) track.triggernode = idMap.get(track.triggernode) || track.triggernode;
    }
    
    if (track.type === 'signal' && track.triggernode) {
      track.triggernode = idMap.get(track.triggernode) || track.triggernode;
    }
  }
  
  // Replace selected tracks
  for (const oldId of selectedTracks) {
    delete state.layout.tracks[oldId];
  }
  
  for (const [newId, track] of Object.entries(newTracks)) {
    state.layout.tracks[newId] = track;
  }
  
  // Update selection with new IDs
  state.selectedTracks.clear();
  for (const newId of Object.keys(newTracks)) {
    state.selectedTracks.add(newId);
  }
  
  saveHistory();
  updateMultiSelectProperties();
  draw();
  setStatus(`Normalized ${selectedTracks.length} selected tracks`);
}

// SELECTION BOX FUNCTIONALITY
function updateSelectionBox() {
  const startX = Math.min(state.selectionStart.x, state.selectionEnd.x);
  const startY = Math.min(state.selectionStart.y, state.selectionEnd.y);
  const endX = Math.max(state.selectionStart.x, state.selectionEnd.x);
  const endY = Math.max(state.selectionStart.y, state.selectionEnd.y);
  
  // Clear current selection
  state.selectedTracks.clear();
  
  // Find tracks within selection box
  for (const [id, track] of Object.entries(state.layout.tracks)) {
    const [x, y] = track.position;
    if (x >= startX && x <= endX && y >= startY && y <= endY) {
      state.selectedTracks.add(id);
    }
  }
  
  updateMultiSelectProperties();
}

function drawSelectionBox(ctx) {
  const startX = worldToScreenX(Math.min(state.selectionStart.x, state.selectionEnd.x));
  const startY = worldToScreenY(Math.min(state.selectionStart.y, state.selectionEnd.y));
  const endX = worldToScreenX(Math.max(state.selectionStart.x, state.selectionEnd.x));
  const endY = worldToScreenY(Math.max(state.selectionStart.y, state.selectionEnd.y));
  
  ctx.save();
  ctx.strokeStyle = '#3498db';
  ctx.lineWidth = 2;
  ctx.setLineDash([5, 5]);
  ctx.fillStyle = 'rgba(52, 152, 219, 0.1)';
  
  ctx.beginPath();
  ctx.rect(startX, startY, endX - startX, endY - startY);
  ctx.fill();
  ctx.stroke();
  
  ctx.restore();
}

function drawMultiSelectHighlights(ctx) {
  ctx.save();
  ctx.strokeStyle = '#e74c3c';
  ctx.lineWidth = 3;
  ctx.setLineDash([]);
  
  for (const trackId of state.selectedTracks) {
    const track = state.layout.tracks[trackId];
    if (!track) continue;
    
    const [x, y] = track.position;
    const sx = worldToScreenX(x);
    const sy = worldToScreenY(y);
    
    // Draw highlight circle
    ctx.beginPath();
    ctx.arc(sx, sy, 12, 0, 2 * Math.PI);
    ctx.stroke();
    
    // Draw track ID
    ctx.fillStyle = '#e74c3c';
    ctx.font = 'bold 12px Arial';
    ctx.textAlign = 'center';
    ctx.fillText(trackId, sx, sy - 16);
  }
  
  ctx.restore();
}

// NODE NUMBERING NORMALIZATION
function normalizeNodeNumbering() {
  const oldTracks = state.layout.tracks;
  const newTracks = {};
  const idMap = new Map();
  const counters = { N: 1, B: 1, P: 1, S: 1 }; // Starting counters for each prefix

  // Group tracks by prefix and sort them by their current number
  const groupedTracks = {};
  for (const id in oldTracks) {
    const match = id.match(/^([A-Z]+)(\d+)$/);
    if (match) {
      const prefix = match[1];
      const num = parseInt(match[2]);
      if (!groupedTracks[prefix]) {
        groupedTracks[prefix] = [];
      }
      groupedTracks[prefix].push({ id, num, track: oldTracks[id] });
    } else {
      // Keep non-standard IDs as is
      newTracks[id] = oldTracks[id];
    }
  }

  // Sort each group and assign new sequential IDs
  for (const prefix in groupedTracks) {
    groupedTracks[prefix].sort((a, b) => a.num - b.num);
    for (const item of groupedTracks[prefix]) {
      const newId = `${prefix}${counters[prefix]++}`;
      idMap.set(item.id, newId);
      newTracks[newId] = { ...item.track }; // Copy track data
    }
  }

  // Update all connections and references to use new IDs
  for (const newId in newTracks) {
    const track = newTracks[newId];

    // Update connections
    if (track.connections) {
      track.connections = track.connections.map(connId => idMap.get(connId) || connId);
    }

    // Update point-specific properties
    if (track.type === 'point' && track.coacting) {
      track.coacting = track.coacting.map(coId => idMap.get(coId) || coId);
    }

    // Update signal-specific properties
    if (track.type === 'pointsignal') {
      if (track.point) track.point = idMap.get(track.point) || track.point;
      if (track.pointleg) track.pointleg = idMap.get(track.pointleg) || track.pointleg;
      if (track.triggernode) track.triggernode = idMap.get(track.triggernode) || track.triggernode;
      if (track.leglabels) {
        const newLegLabels = {};
        for (const oldLegId in track.leglabels) {
          newLegLabels[idMap.get(oldLegId) || oldLegId] = track.leglabels[oldLegId];
        }
        track.leglabels = newLegLabels;
      }
    } else if (track.type === 'signal' && track.triggernode) {
      track.triggernode = idMap.get(track.triggernode) || track.triggernode;
    }
  }

  state.layout.tracks = newTracks;

  // Update selected track if it was renumbered
  if (state.selectedTrack && idMap.has(state.selectedTrack)) {
    state.selectedTrack = idMap.get(state.selectedTrack);
    showProperties(state.selectedTrack);
  } else if (state.selectedTrack) {
    // If selected track was not renumbered (e.g., non-standard ID), just refresh properties
    showProperties(state.selectedTrack);
  } else {
    hideProperties();
  }

  // Update multi-selected tracks
  const newSelectedTracks = new Set();
  for (const oldId of state.selectedTracks) {
    newSelectedTracks.add(idMap.get(oldId) || oldId);
  }
  state.selectedTracks = newSelectedTracks;
  updateMultiSelectProperties();

  saveHistory();
  draw();
}


// GLOBAL FUNCTION EXPORTS
window.removeFromSelection = removeFromSelection;
window.deleteSelectedTracks = deleteSelectedTracks;
window.clearSelection = clearSelection;
window.normalizeSelectedTracks = normalizeSelectedTracks;
window.normalizeNodeNumbering = normalizeNodeNumbering;
