console.log('[viewport-manager.js] Script loading...');

/**
 * ViewportManager - Manages multiple DICOM viewports in a grid layout
 *
 * Handles viewport creation/destruction, layout switching, active viewport tracking,
 * and synchronized scrolling across viewports.
 */
class ViewportManager {
  constructor(gridContainer, projectId) {
    this.gridContainer = gridContainer;
    this.projectId = projectId;

    this.viewports = []; // Array of Viewport instances
    this.activeViewportId = 0; // Currently focused viewport
    this.layoutMode = '1x1'; // '1x1', '2x2', '3x3'
    this.linkedMode = false; // Synchronized scrolling

    // Event callbacks
    this.onActiveViewportChangeCallback = null;
    this.onLayoutChangeCallback = null;
    this.onViewportUpdateCallback = null;

    // Don't initialize layout here - let caller do it asynchronously
    // The viewer.js setupUI() will call setLayout('1x1') after construction
  }

  /**
   * Set layout mode (1x1, 2x2, 3x3)
   * @param {string} mode - Layout mode
   */
  async setLayout(mode) {
    if (!['1x1', '2x2', '3x3'].includes(mode)) {
      console.error('[ViewportManager] Invalid layout mode:', mode);
      return;
    }

    console.log('[ViewportManager] Setting layout:', mode);

    // Save current viewport states
    const savedStates = this.viewports
      .filter(vp => vp.hasContent())
      .map(vp => vp.serialize());

    // Destroy existing viewports
    this.destroyAllViewports();

    // Update layout mode
    this.layoutMode = mode;

    // Update grid classes
    this.gridContainer.className = `viewport-grid layout-${mode}`;
    this.gridContainer.setAttribute('data-layout', mode);

    // Clear grid
    this.gridContainer.innerHTML = '';

    // Calculate number of viewports
    const viewportCount = this.getViewportCountForLayout(mode);

    console.log('[ViewportManager] Creating', viewportCount, 'viewports...');

    // Create new viewports
    for (let i = 0; i < viewportCount; i++) {
      await this.createViewport(i);
    }

    // Restore viewport states if possible
    for (const state of savedStates) {
      if (state.id < this.viewports.length) {
        try {
          await this.viewports[state.id].deserialize(state);
        } catch (error) {
          console.error('[ViewportManager] Failed to restore viewport', state.id, ':', error);
        }
      }
    }

    // Set first viewport as active
    this.setActiveViewport(0);

    console.log('[ViewportManager] ✓ Layout set to', mode);

    // Notify listeners
    if (this.onLayoutChangeCallback) {
      this.onLayoutChangeCallback(mode, this.viewports.length);
    }
  }

  /**
   * Get viewport count for layout mode
   */
  getViewportCountForLayout(mode) {
    const counts = {
      '1x1': 1,
      '2x2': 4,
      '3x3': 9
    };
    return counts[mode] || 1;
  }

  /**
   * Create a viewport at the specified index
   * @param {number} index - Viewport index
   */
  async createViewport(index) {
    console.log('[ViewportManager] Creating viewport', index);

    // Create viewport cell HTML
    const cell = document.createElement('div');
    cell.className = 'viewport-cell';
    cell.dataset.viewportId = index;

    // Create canvas
    const canvas = document.createElement('canvas');
    canvas.className = 'dicom-canvas';
    canvas.id = `canvas-${index}`;
    cell.appendChild(canvas);

    // Create viewport selector button (clickable number badge)
    const selectorBtn = document.createElement('div');
    selectorBtn.className = 'viewport-selector-btn';
    selectorBtn.textContent = index + 1;
    selectorBtn.title = `Click to activate Viewport ${index + 1} (or press ${index + 1})`;
    cell.appendChild(selectorBtn);

    // Create overlay
    const overlay = document.createElement('div');
    overlay.className = 'viewport-overlay';
    overlay.innerHTML = `
      <div class="viewport-series-info">
        <span class="series-name">-</span>
      </div>
      <div class="viewport-image-info">
        <span class="image-counter">-</span>
      </div>
    `;
    cell.appendChild(overlay);

    // Create empty state
    const emptyState = document.createElement('div');
    emptyState.className = 'viewport-empty-state';
    emptyState.innerHTML = `
      <div class="empty-icon">📂</div>
      <div class="empty-text">Click a series to load</div>
    `;
    cell.appendChild(emptyState);

    // Add to grid
    this.gridContainer.appendChild(cell);

    // Force DOM reflow to ensure canvas.parentElement is available
    // This prevents rare "Cannot read properties of undefined (reading 'parentElement')" errors
    void cell.offsetHeight;

    // Verify canvas is properly in DOM before creating viewport
    if (!canvas.parentElement) {
      throw new Error(`Canvas element for viewport ${index} not properly attached to DOM after appendChild`);
    }

    // Create Viewport instance
    const viewport = new Viewport(index, canvas, this.projectId);

    // Set up event listeners
    viewport.onImageLoadCallback = (vpId, imageIndex, totalImages) => {
      this.handleViewportImageLoad(vpId, imageIndex, totalImages);
    };

    viewport.onScrollCallback = (vpId, imageIndex) => {
      this.handleViewportScroll(vpId, imageIndex);
    };

    // Enable Cornerstone
    await viewport.enable();

    // Add click handler to selector button for explicit activation
    selectorBtn.addEventListener('click', (e) => {
      e.stopPropagation();
      this.setActiveViewport(index);
      console.log('[ViewportManager] Viewport', index, 'activated via selector button');
    });

    // Also activate when clicking the empty state or overlay areas
    cell.addEventListener('click', (e) => {
      // Activate if clicking empty state, overlay, or cell background (not canvas)
      if (e.target === canvas || e.target.classList.contains('cornerstone-canvas')) return;
      this.setActiveViewport(index);
    });

    // Store viewport
    this.viewports[index] = viewport;

    console.log('[ViewportManager] ✓ Viewport', index, 'created');

    return viewport;
  }

  /**
   * Destroy a viewport
   * @param {number} index - Viewport index
   */
  destroyViewport(index) {
    if (!this.viewports[index]) {
      return;
    }

    console.log('[ViewportManager] Destroying viewport', index);

    const viewport = this.viewports[index];
    viewport.disable();

    // Remove from array
    this.viewports[index] = null;

    // Remove cell from DOM
    const cell = this.gridContainer.querySelector(`[data-viewport-id="${index}"]`);
    if (cell) {
      cell.remove();
    }

    console.log('[ViewportManager] ✓ Viewport', index, 'destroyed');
  }

  /**
   * Destroy all viewports
   */
  destroyAllViewports() {
    console.log('[ViewportManager] Destroying all viewports...');

    for (let i = 0; i < this.viewports.length; i++) {
      if (this.viewports[i]) {
        this.destroyViewport(i);
      }
    }

    this.viewports = [];
    console.log('[ViewportManager] ✓ All viewports destroyed');
  }

  /**
   * Set active viewport
   * @param {number} index - Viewport index
   */
  setActiveViewport(index) {
    if (index < 0 || index >= this.viewports.length) {
      console.warn('[ViewportManager] Invalid viewport index:', index);
      return;
    }

    if (this.activeViewportId === index) {
      return; // Already active
    }

    console.log('[ViewportManager] Setting active viewport:', index);

    // Remove active class from previous viewport
    const prevCell = this.gridContainer.querySelector(`[data-viewport-id="${this.activeViewportId}"]`);
    if (prevCell) {
      prevCell.classList.remove('active');
    }

    // Add active class to new viewport
    const newCell = this.gridContainer.querySelector(`[data-viewport-id="${index}"]`);
    if (newCell) {
      newCell.classList.add('active');
    }

    this.activeViewportId = index;

    // Notify listeners
    if (this.onActiveViewportChangeCallback) {
      this.onActiveViewportChangeCallback(index, this.viewports[index]);
    }
  }

  /**
   * Get active viewport
   */
  getActiveViewport() {
    return this.viewports[this.activeViewportId];
  }

  /**
   * Get viewport by index
   */
  getViewport(index) {
    return this.viewports[index];
  }

  /**
   * Load series into specific viewport
   * @param {number} viewportId - Viewport index
   * @param {string} seriesId - Series ID
   * @param {Array<string>} docIds - Document IDs
   * @param {Object} metadata - Series metadata
   */
  async loadSeriesIntoViewport(viewportId, seriesId, docIds, metadata) {
    const viewport = this.viewports[viewportId];
    if (!viewport) {
      console.error('[ViewportManager] Viewport not found:', viewportId);
      return;
    }

    console.log('[ViewportManager] Loading series into viewport', viewportId);

    try {
      await viewport.loadSeries(seriesId, docIds, metadata);

      // Notify listeners
      if (this.onViewportUpdateCallback) {
        this.onViewportUpdateCallback(viewportId, viewport);
      }

      console.log('[ViewportManager] ✓ Series loaded into viewport', viewportId);
    } catch (error) {
      console.error('[ViewportManager] Failed to load series into viewport', viewportId, ':', error);
      throw error;
    }
  }

  /**
   * Load series into active viewport
   */
  async loadSeriesIntoActiveViewport(seriesId, docIds, metadata) {
    await this.loadSeriesIntoViewport(this.activeViewportId, seriesId, docIds, metadata);
  }

  /**
   * Clear viewport
   * @param {number} viewportId - Viewport index
   */
  clearViewport(viewportId) {
    const viewport = this.viewports[viewportId];
    if (!viewport) {
      console.error('[ViewportManager] Viewport not found:', viewportId);
      return;
    }

    console.log('[ViewportManager] Clearing viewport', viewportId);
    viewport.clear();

    // Notify listeners
    if (this.onViewportUpdateCallback) {
      this.onViewportUpdateCallback(viewportId, viewport);
    }
  }

  /**
   * Clear active viewport
   */
  clearActiveViewport() {
    this.clearViewport(this.activeViewportId);
  }

  /**
   * Swap two viewports
   * @param {number} id1 - First viewport ID
   * @param {number} id2 - Second viewport ID
   */
  async swapViewports(id1, id2) {
    if (id1 === id2) return;

    const vp1 = this.viewports[id1];
    const vp2 = this.viewports[id2];

    if (!vp1 || !vp2) {
      console.error('[ViewportManager] Invalid viewport IDs for swap:', id1, id2);
      return;
    }

    console.log('[ViewportManager] Swapping viewports', id1, 'and', id2);

    // Serialize states
    const state1 = vp1.serialize();
    const state2 = vp2.serialize();

    // Clear both
    vp1.clear();
    vp2.clear();

    // Swap states (update IDs to match new positions)
    state1.id = id2;
    state2.id = id1;

    // Restore swapped states
    try {
      if (state2.seriesId) {
        await vp1.deserialize(state2);
      }
      if (state1.seriesId) {
        await vp2.deserialize(state1);
      }

      console.log('[ViewportManager] ✓ Viewports swapped');
    } catch (error) {
      console.error('[ViewportManager] Failed to swap viewports:', error);
    }
  }

  /**
   * Toggle linked mode (synchronized scrolling)
   */
  toggleLinkedMode() {
    this.linkedMode = !this.linkedMode;
    console.log('[ViewportManager] Linked mode:', this.linkedMode ? 'ON' : 'OFF');

    // Update all viewports
    this.viewports.forEach(vp => {
      if (vp) {
        vp.linked = this.linkedMode;
      }
    });

    return this.linkedMode;
  }

  /**
   * Handle viewport image load event
   */
  handleViewportImageLoad(viewportId, imageIndex, totalImages) {
    console.log('[ViewportManager] Viewport', viewportId, 'loaded image', imageIndex + 1, '/', totalImages);

    // Notify listeners
    if (this.onViewportUpdateCallback) {
      this.onViewportUpdateCallback(viewportId, this.viewports[viewportId]);
    }
  }

  /**
   * Handle viewport scroll event (for synchronized scrolling)
   */
  handleViewportScroll(sourceViewportId, imageIndex) {
    if (!this.linkedMode) {
      return; // Linked mode disabled
    }

    console.log('[ViewportManager] Syncing scroll from viewport', sourceViewportId, '- image index:', imageIndex);

    const sourceViewport = this.viewports[sourceViewportId];
    if (!sourceViewport || sourceViewport.seriesDocIds.length === 0) {
      return;
    }

    // Calculate progress through source series (0.0 to 1.0)
    const progress = imageIndex / (sourceViewport.seriesDocIds.length - 1);

    // Sync other viewports
    this.viewports.forEach((targetViewport, targetId) => {
      if (!targetViewport || targetId === sourceViewportId) {
        return; // Skip source viewport
      }

      if (!targetViewport.hasContent() || !targetViewport.linked) {
        return; // Skip empty or unlinked viewports
      }

      // Calculate target image index based on progress
      const targetIndex = Math.round(progress * (targetViewport.seriesDocIds.length - 1));

      if (targetIndex !== targetViewport.currentImageIndex) {
        // Load image without triggering another scroll event
        const originalCallback = targetViewport.onScrollCallback;
        targetViewport.onScrollCallback = null; // Temporarily disable to prevent infinite loop

        targetViewport.loadImage(targetIndex).then(() => {
          targetViewport.onScrollCallback = originalCallback; // Re-enable
        }).catch(error => {
          console.error('[ViewportManager] Failed to sync viewport', targetId, ':', error);
          targetViewport.onScrollCallback = originalCallback; // Re-enable even on error
        });
      }
    });
  }

  /**
   * Get all viewports with content
   */
  getLoadedViewports() {
    return this.viewports.filter(vp => vp && vp.hasContent());
  }

  /**
   * Get viewport count
   */
  getViewportCount() {
    return this.viewports.length;
  }

  /**
   * Serialize all viewport states
   */
  serialize() {
    return {
      layout: this.layoutMode,
      linkedMode: this.linkedMode,
      activeViewportId: this.activeViewportId,
      viewports: this.viewports
        .filter(vp => vp && vp.hasContent())
        .map(vp => vp.serialize())
    };
  }

  /**
   * Restore from serialized state
   */
  async deserialize(state) {
    // Set layout first
    await this.setLayout(state.layout || '1x1');

    // Restore linked mode
    this.linkedMode = state.linkedMode || false;

    // Restore viewport states
    if (state.viewports) {
      for (const vpState of state.viewports) {
        if (vpState.id < this.viewports.length) {
          try {
            await this.viewports[vpState.id].deserialize(vpState);
          } catch (error) {
            console.error('[ViewportManager] Failed to restore viewport', vpState.id, ':', error);
          }
        }
      }
    }

    // Restore active viewport
    this.setActiveViewport(state.activeViewportId || 0);
  }

  /**
   * Apply action to active viewport
   */
  applyToActiveViewport(action) {
    const viewport = this.getActiveViewport();
    if (!viewport) {
      console.warn('[ViewportManager] No active viewport');
      return;
    }

    if (typeof viewport[action] === 'function') {
      viewport[action]();
    }
  }

  /**
   * Navigate active viewport
   */
  nextImage() {
    this.applyToActiveViewport('nextImage');
  }

  previousImage() {
    this.applyToActiveViewport('previousImage');
  }

  zoomIn() {
    this.applyToActiveViewport('zoomIn');
  }

  zoomOut() {
    this.applyToActiveViewport('zoomOut');
  }

  reset() {
    this.applyToActiveViewport('reset');
  }

  toggleInvert() {
    this.applyToActiveViewport('toggleInvert');
  }

  rotate() {
    this.applyToActiveViewport('rotate');
  }
}

// Export for use in other modules
if (typeof module !== 'undefined' && module.exports) {
  module.exports = ViewportManager;
}

console.log('[viewport-manager.js] ✓ ViewportManager class defined and available globally');
