<template>
  <div class="threejs-viewer">
    <h2>3D Model Viewer</h2>

    <!-- Viewer controls -->
    <div class="viewer-controls">
      <button :disabled="isLoading || !modelUrl" @click="loadModel">
        {{ isLoading ? "Loading..." : "Load Model" }}
      </button>
      <button :disabled="!isModelLoaded" @click="resetCamera">
        Reset Camera
      </button>
      <button :disabled="!isModelLoaded" @click="toggleAnimation">
        {{ isAnimating ? "Stop" : "Start" }} Animation
      </button>
      <button :disabled="!isModelLoaded" @click="toggleWireframe">
        {{ showWireframe ? "Hide" : "Show" }} Wireframe
      </button>
    </div>

    <!-- Loading status -->
    <div v-if="isLoading" class="loading-status">
      <div class="loading-spinner"></div>
      <p>Loading 3D model...</p>
      <p class="loading-detail">{{ loadingProgress }}% complete</p>
    </div>

    <!-- Error status -->
    <div v-if="loadError" class="error-status">
      <p>Failed to load model: {{ loadError }}</p>
      <button class="retry-btn" @click="retryLoad">Retry</button>
    </div>

    <!-- 3D Canvas -->
    <div
      ref="canvasContainer"
      class="canvas-container"
      :class="{ 'model-loaded': isModelLoaded }"
    >
      <canvas ref="threeCanvas" class="three-canvas"></canvas>

      <!-- Overlay controls -->
      <div v-if="isModelLoaded" class="overlay-controls">
        <div class="control-group">
          <label>Camera Distance:</label>
          <input
            v-model.number="cameraDistance"
            type="range"
            min="1"
            max="20"
            step="0.1"
            @input="updateCameraDistance"
          />
          <span>{{ cameraDistance.toFixed(1) }}</span>
        </div>

        <div class="control-group">
          <label>Rotation Speed:</label>
          <input
            v-model.number="rotationSpeed"
            type="range"
            min="0"
            max="2"
            step="0.1"
          />
          <span>{{ rotationSpeed.toFixed(1) }}</span>
        </div>

        <div class="control-group">
          <label>Light Intensity:</label>
          <input
            v-model.number="lightIntensity"
            type="range"
            min="0"
            max="2"
            step="0.1"
            @input="updateLightIntensity"
          />
          <span>{{ lightIntensity.toFixed(1) }}</span>
        </div>
      </div>

      <!-- Model info -->
      <div v-if="modelInfo" class="model-info">
        <h4>Model Information</h4>
        <div class="info-grid">
          <div class="info-item">
            <span class="info-label">Vertices:</span>
            <span class="info-value">{{
              modelInfo.vertexCount.toLocaleString()
            }}</span>
          </div>
          <div class="info-item">
            <span class="info-label">Faces:</span>
            <span class="info-value">{{
              modelInfo.faceCount.toLocaleString()
            }}</span>
          </div>
          <div class="info-item">
            <span class="info-label">Materials:</span>
            <span class="info-value">{{ modelInfo.materialCount }}</span>
          </div>
          <div class="info-item">
            <span class="info-label">File Size:</span>
            <span class="info-value">{{
              formatFileSize(modelInfo.fileSize)
            }}</span>
          </div>
        </div>
      </div>
    </div>

    <!-- Performance metrics -->
    <div v-if="performanceMetrics" class="performance-metrics">
      <h4>Performance Metrics</h4>
      <div class="metrics-grid">
        <div class="metric">
          <span class="metric-label">FPS:</span>
          <span class="metric-value">{{ performanceMetrics.fps }}</span>
        </div>
        <div class="metric">
          <span class="metric-label">Render Time:</span>
          <span class="metric-value"
            >{{ performanceMetrics.renderTime }}ms</span
          >
        </div>
        <div class="metric">
          <span class="metric-label">Memory Usage:</span>
          <span class="metric-value"
            >{{ performanceMetrics.memoryUsage }}MB</span
          >
        </div>
        <div class="metric">
          <span class="metric-label">Draw Calls:</span>
          <span class="metric-value">{{ performanceMetrics.drawCalls }}</span>
        </div>
      </div>
    </div>
  </div>
</template>

<script lang="ts">
import { Component, Vue, Prop, Emit } from "vue-facing-decorator";

interface ModelInfo {
  vertexCount: number;
  faceCount: number;
  materialCount: number;
  fileSize: number;
  boundingBox: {
    min: { x: number; y: number; z: number };
    max: { x: number; y: number; z: number };
  };
}

interface PerformanceMetrics {
  fps: number;
  renderTime: number;
  memoryUsage: number;
  drawCalls: number;
}

/**
 * ThreeJS 3D Model Viewer Component
 *
 * Demonstrates lazy loading for heavy 3D rendering libraries.
 * This component would benefit from lazy loading as ThreeJS is a large
 * library that's only needed for 3D visualization features.
 *
 * @author Matthew Raymer
 * @version 1.0.0
 */
@Component({
  name: "ThreeJSViewer",
})
export default class ThreeJSViewer extends Vue {
  @Prop({ required: true }) readonly modelUrl!: string;

  // Component state
  isLoading = false;
  isModelLoaded = false;
  loadError: string | null = null;
  loadingProgress = 0;

  // Animation state
  isAnimating = false;
  showWireframe = false;

  // Camera and lighting controls
  cameraDistance = 5;
  rotationSpeed = 0.5;
  lightIntensity = 1;

  // Canvas references
  canvasContainer: HTMLElement | null = null;
  threeCanvas: HTMLCanvasElement | null = null;

  // Model and performance data
  modelInfo: ModelInfo | null = null;
  performanceMetrics: PerformanceMetrics | null = null;

  // ThreeJS objects (will be lazy loaded)
  private three: any = null;
  private scene: any = null;
  private camera: any = null;
  private renderer: any = null;
  private model: any = null;
  private controls: any = null;
  private animationId: number | null = null;
  private frameCount = 0;
  private lastTime = 0;

  // Lifecycle hooks
  mounted(): void {
    console.log("[ThreeJSViewer] Component mounted");
    this.initializeCanvas();
  }

  beforeUnmount(): void {
    this.cleanup();
    console.log("[ThreeJSViewer] Component unmounting");
  }

  // Methods
  private initializeCanvas(): void {
    this.canvasContainer = this.$refs.canvasContainer as HTMLElement;
    this.threeCanvas = this.$refs.threeCanvas as HTMLCanvasElement;

    if (this.threeCanvas) {
      this.threeCanvas.width = this.canvasContainer.clientWidth;
      this.threeCanvas.height = this.canvasContainer.clientHeight;
    }
  }

  async loadModel(): Promise<void> {
    if (this.isLoading || !this.modelUrl) return;

    this.isLoading = true;
    this.loadError = null;
    this.loadingProgress = 0;

    try {
      console.log("[ThreeJSViewer] Loading 3D model:", this.modelUrl);

      // Lazy load ThreeJS
      await this.loadThreeJS();

      // Initialize scene
      await this.initializeScene();

      // Load model
      await this.loadModelFile();

      // Start rendering
      this.startRendering();

      this.isModelLoaded = true;
      this.isLoading = false;

      // Emit model loaded event
      this.$emit("model-loaded", this.modelInfo);

      console.log("[ThreeJSViewer] Model loaded successfully");
    } catch (error) {
      console.error("[ThreeJSViewer] Failed to load model:", error);
      this.loadError = error instanceof Error ? error.message : "Unknown error";
      this.isLoading = false;
    }
  }

  private async loadThreeJS(): Promise<void> {
    // Simulate loading ThreeJS library
    this.loadingProgress = 20;
    await this.simulateLoading(500);

    // In a real implementation, you would import ThreeJS here
    // this.three = await import('three');

    this.loadingProgress = 40;
    await this.simulateLoading(300);
  }

  private async initializeScene(): Promise<void> {
    this.loadingProgress = 60;

    // Simulate scene initialization
    await this.simulateLoading(400);

    // In a real implementation, you would set up ThreeJS scene here
    // this.scene = new this.three.Scene();
    // this.camera = new this.three.PerspectiveCamera(75, width / height, 0.1, 1000);
    // this.renderer = new this.three.WebGLRenderer({ canvas: this.threeCanvas });

    this.loadingProgress = 80;
  }

  private async loadModelFile(): Promise<void> {
    this.loadingProgress = 90;

    // Simulate model loading
    await this.simulateLoading(600);

    // Simulate model info
    this.modelInfo = {
      vertexCount: Math.floor(Math.random() * 50000) + 1000,
      faceCount: Math.floor(Math.random() * 25000) + 500,
      materialCount: Math.floor(Math.random() * 5) + 1,
      fileSize: Math.floor(Math.random() * 5000000) + 100000,
      boundingBox: {
        min: { x: -1, y: -1, z: -1 },
        max: { x: 1, y: 1, z: 1 },
      },
    };

    this.loadingProgress = 100;
  }

  private async simulateLoading(delay: number): Promise<void> {
    return new Promise((resolve) => setTimeout(resolve, delay));
  }

  private startRendering(): void {
    if (!this.isModelLoaded) return;

    this.isAnimating = true;
    this.animate();

    // Start performance monitoring
    this.startPerformanceMonitoring();
  }

  private animate(): void {
    if (!this.isAnimating) return;

    this.animationId = requestAnimationFrame(() => this.animate());

    // Simulate model rotation
    if (this.model && this.rotationSpeed > 0) {
      // this.model.rotation.y += this.rotationSpeed * 0.01;
    }

    // Simulate rendering
    // this.renderer.render(this.scene, this.camera);

    this.frameCount++;
  }

  private startPerformanceMonitoring(): void {
    const updateMetrics = () => {
      if (!this.isAnimating) return;

      const now = performance.now();
      const deltaTime = now - this.lastTime;

      if (deltaTime > 0) {
        const fps = Math.round(1000 / deltaTime);

        this.performanceMetrics = {
          fps: Math.min(fps, 60), // Cap at 60 FPS for display
          renderTime: Math.round(deltaTime),
          memoryUsage: Math.round((Math.random() * 50 + 10) * 100) / 100,
          drawCalls: Math.floor(Math.random() * 100) + 10,
        };
      }

      this.lastTime = now;
      requestAnimationFrame(updateMetrics);
    };

    updateMetrics();
  }

  resetCamera(): void {
    if (!this.isModelLoaded) return;

    this.cameraDistance = 5;
    this.updateCameraDistance();
    console.log("[ThreeJSViewer] Camera reset");
  }

  toggleAnimation(): void {
    this.isAnimating = !this.isAnimating;

    if (this.isAnimating) {
      this.animate();
    } else if (this.animationId) {
      cancelAnimationFrame(this.animationId);
      this.animationId = null;
    }

    console.log("[ThreeJSViewer] Animation toggled:", this.isAnimating);
  }

  toggleWireframe(): void {
    this.showWireframe = !this.showWireframe;

    // In a real implementation, you would toggle wireframe mode
    // this.model.traverse((child: any) => {
    //   if (child.isMesh) {
    //     child.material.wireframe = this.showWireframe;
    //   }
    // });

    console.log("[ThreeJSViewer] Wireframe toggled:", this.showWireframe);
  }

  updateCameraDistance(): void {
    if (!this.isModelLoaded) return;

    // In a real implementation, you would update camera position
    // this.camera.position.z = this.cameraDistance;
    // this.camera.lookAt(0, 0, 0);
  }

  updateLightIntensity(): void {
    if (!this.isModelLoaded) return;

    // In a real implementation, you would update light intensity
    // this.light.intensity = this.lightIntensity;
  }

  retryLoad(): void {
    this.loadError = null;
    this.loadModel();
  }

  private cleanup(): void {
    if (this.animationId) {
      cancelAnimationFrame(this.animationId);
      this.animationId = null;
    }

    if (this.renderer) {
      this.renderer.dispose();
    }

    this.isAnimating = false;
    this.isModelLoaded = false;
  }

  formatFileSize(bytes: number): string {
    const sizes = ["B", "KB", "MB", "GB"];
    const i = Math.floor(Math.log(bytes) / Math.log(1024));
    return `${(bytes / Math.pow(1024, i)).toFixed(1)} ${sizes[i]}`;
  }

  // Event emitters
  @Emit("model-loaded")
  emitModelLoaded(info: ModelInfo): ModelInfo {
    return info;
  }
}
</script>

<style scoped>
.threejs-viewer {
  padding: 20px;
  border: 1px solid #ddd;
  border-radius: 8px;
  background: #f9f9f9;
}

.viewer-controls {
  display: flex;
  gap: 10px;
  margin-bottom: 20px;
  flex-wrap: wrap;
}

.viewer-controls button {
  padding: 8px 16px;
  border: 1px solid #ccc;
  border-radius: 4px;
  background: #fff;
  cursor: pointer;
  transition: background-color 0.2s;
}

.viewer-controls button:hover:not(:disabled) {
  background: #e9ecef;
}

.viewer-controls button:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

.loading-status {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 40px;
  text-align: center;
}

.loading-spinner {
  width: 40px;
  height: 40px;
  border: 4px solid #f3f3f3;
  border-top: 4px solid #3498db;
  border-radius: 50%;
  animation: spin 1s linear infinite;
  margin-bottom: 20px;
}

@keyframes spin {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}

.loading-detail {
  font-size: 0.9em;
  color: #666;
  margin-top: 10px;
}

.error-status {
  padding: 20px;
  background: #f8d7da;
  border: 1px solid #f5c6cb;
  border-radius: 4px;
  color: #721c24;
  text-align: center;
}

.retry-btn {
  margin-top: 10px;
  padding: 8px 16px;
  border: 1px solid #721c24;
  border-radius: 4px;
  background: #721c24;
  color: #fff;
  cursor: pointer;
}

.retry-btn:hover {
  background: #5a1a1a;
}

.canvas-container {
  position: relative;
  width: 100%;
  height: 400px;
  border: 2px solid #ddd;
  border-radius: 8px;
  overflow: hidden;
  background: #000;
  margin: 20px 0;
}

.canvas-container.model-loaded {
  border-color: #28a745;
}

.three-canvas {
  width: 100%;
  height: 100%;
  display: block;
}

.overlay-controls {
  position: absolute;
  top: 10px;
  right: 10px;
  background: rgba(0, 0, 0, 0.8);
  color: #fff;
  padding: 15px;
  border-radius: 4px;
  min-width: 200px;
}

.control-group {
  margin-bottom: 10px;
}

.control-group:last-child {
  margin-bottom: 0;
}

.control-group label {
  display: block;
  font-size: 0.9em;
  margin-bottom: 5px;
}

.control-group input[type="range"] {
  width: 100%;
  margin-right: 10px;
}

.control-group span {
  font-size: 0.8em;
  color: #ccc;
}

.model-info {
  position: absolute;
  bottom: 10px;
  left: 10px;
  background: rgba(0, 0, 0, 0.8);
  color: #fff;
  padding: 15px;
  border-radius: 4px;
  min-width: 200px;
}

.model-info h4 {
  margin: 0 0 10px 0;
  font-size: 1em;
}

.info-grid {
  display: grid;
  gap: 5px;
}

.info-item {
  display: flex;
  justify-content: space-between;
  font-size: 0.85em;
}

.info-label {
  color: #ccc;
}

.info-value {
  font-weight: bold;
}

.performance-metrics {
  margin-top: 20px;
  padding: 15px;
  background: #e8f4fd;
  border-radius: 4px;
}

.metrics-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 15px;
  margin-top: 10px;
}

.metric {
  display: flex;
  justify-content: space-between;
  padding: 8px;
  background: #fff;
  border-radius: 4px;
}

.metric-label {
  font-weight: bold;
  color: #333;
}

.metric-value {
  color: #007bff;
  font-weight: bold;
}
</style>
