import {Component} from '@angular/core';
import {FormsModule} from '@angular/forms';
import {MatCard, MatCardContent, MatCardHeader, MatCardTitle} from '@angular/material/card';
import {MatSliderModule} from '@angular/material/slider';
import {TranslatePipe} from '@ngx-translate/core';
import {BabylonCanvas, RenderConfig, SceneEventData} from '../../../shared/components/render-canvas/babylon-canvas.component';
import {MatButton} from '@angular/material/button';
import {ClothConfig} from './cloth.model';
import {Information} from '../information/information';
import {AlgorithmInformation} from '../information/information.models';
import {UrlConstants} from '../../../constants/UrlConstants';
import {ClothSimulationStrategy} from './strategies/cloth-simulation.strategy';
import {ClothGpuStrategy} from './strategies/cloth-gpu.strategy';
import {ClothCpuStrategy} from './strategies/cloth-cpu.strategy';

@Component({
  selector: 'app-cloth',
  imports: [
    MatCard,
    MatCardContent,
    MatCardHeader,
    MatCardTitle,
    TranslatePipe,
    BabylonCanvas,
    MatButton,
    MatSliderModule,
    FormsModule,
    Information
  ],
  templateUrl: './cloth.component.html',
  styleUrl: './cloth.component.scss',
})
export class ClothComponent {
  private currentSceneData: SceneEventData | null = null;
  private simulationTime: number = 0;
  private strategy: ClothSimulationStrategy | null = null;

  public isWindActive: boolean = false;
  public isOutlineActive: boolean = false;
  public stiffness: number = 80;
  public elongation: number = 1.0;

  public renderConfig: RenderConfig = {
    mode: '3D',
    initialViewSize: 20
  };

  algoInformation: AlgorithmInformation = {
    title: 'CLOTH.EXPLANATION.TITLE',
    entries: [
      {
        name: 'CLOTH.EXPLANATION.CLOTH_SIMULATION_EXPLANATION_TITLE',
        description: 'CLOTH.EXPLANATION.CLOTH_SIMULATION_EXPLANATION',
        link: UrlConstants.CLOTH_SIMULATION_WIKI,
        translateName: true
      },
      {
        name: 'CLOTH.EXPLANATION.XPBD_EXPLANATION_TITLE',
        description: 'CLOTH.EXPLANATION.XPBD_EXPLANATION',
        link: UrlConstants.XPBD_WIKI,
        translateName: true
      },
      {
        name: 'CLOTH.EXPLANATION.GPU_PARALLELIZATION_EXPLANATION_TITLE',
        description: 'CLOTH.EXPLANATION.GPU_PARALLELIZATION_EXPLANATION',
        link: UrlConstants.GPU_COMPUTING_WIKI,
        translateName: true
      },
      {
        name: 'CLOTH.EXPLANATION.DATA_STRUCTURES_EXPLANATION_TITLE',
        description: 'CLOTH.EXPLANATION.DATA_STRUCTURES_EXPLANATION',
        link: UrlConstants.DATA_STRUCTURE_WIKI,
        translateName: true
      }
    ],
    disclaimer: 'CLOTH.EXPLANATION.DISCLAIMER',
    disclaimerBottom: '',
    disclaimerListEntry: ['CLOTH.EXPLANATION.DISCLAIMER_1', 'CLOTH.EXPLANATION.DISCLAIMER_2', 'CLOTH.EXPLANATION.DISCLAIMER_3', 'CLOTH.EXPLANATION.DISCLAIMER_4']
  };

  public onSceneReady(event: SceneEventData): void {
    this.currentSceneData = event;
    this.createSimulation();
  }

  public toggleWind(): void {
    this.isWindActive = !this.isWindActive;
  }

  public toggleMesh(): void {
    this.isOutlineActive = !this.isOutlineActive;
    const mesh = this.strategy?.getMesh();
    if (!mesh?.material) {
      return;
    }
    mesh.material.wireframe = this.isOutlineActive;
  }

  public restartSimulation(): void {
    this.simulationTime = 0;
    this.createSimulation();
  }

  private createSimulation(): void {
    if (!this.currentSceneData) {
      return;
    }

    const {engine, scene, gpuTier} = this.currentSceneData;
    const config = this.getClothConfig();

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

    this.strategy = gpuTier === 'webgpu'
      ? new ClothGpuStrategy()
      : new ClothCpuStrategy();

    this.strategy.init(scene, engine, config);
    this.startParamUpdateLoop(scene, engine);
  }

  private startParamUpdateLoop(scene: any, engine: any): void {
    scene.onAfterRenderObservable.clear();
    scene.onAfterRenderObservable.add(() => {
      this.simulationTime += engine.getDeltaTime() / 1000.0;

      if (this.strategy) {
        this.strategy.updateParams({
          stiffness: this.stiffness,
          elongation: this.elongation,
          isWindActive: this.isWindActive,
          simulationTime: this.simulationTime,
          deltaTime: engine.getDeltaTime() / 1000.0
        });
      }
    });
  }

  private getClothConfig(): ClothConfig {
    const gridWidth = 100;
    const gridHeight = 100;
    const spacing = 0.05;
    const density = 1.0;
    const particleArea = spacing * spacing;
    const particleMass = density * particleArea;

    return {
      gridWidth,
      gridHeight,
      spacing,
      density,
      numVertices: gridWidth * gridHeight,
      particleInvMass: 1.0 / particleMass
    };
  }
}
