import {Component} from '@angular/core';
import {BabylonCanvas, RenderConfig, SceneEventData} from '../../../shared/components/render-canvas/babylon-canvas.component';
import {MatCard, MatCardContent, MatCardHeader, MatCardTitle} from '@angular/material/card';

import {FormsModule} from '@angular/forms';
import {NgxSliderModule, Options} from '@angular-slider/ngx-slider';
import {DEFAULT_DAMPING, DEFAULT_G, DEFAULT_L1_LENGTH, DEFAULT_M1_MASS, DEFAULT_L2_LENGTH, DEFAULT_M2_MASS, DEFAULT_TRAIL_DECAY, MAX_DAMPING, MAX_G, MAX_LENGTH, MAX_MASS, MAX_TRAIL_DECAY, MIN_DAMPING, MIN_G, MIN_LENGTH, MIN_MASS, MIN_TRAIL_DECAY, IMPULSE_M2, IMPULSE_M1} from './pendulum.model';
import {TranslatePipe} from '@ngx-translate/core';
import {MatButton} from '@angular/material/button';
import {Information} from '../information/information';
import {AlgorithmInformation} from '../information/information.models';
import {UrlConstants} from '../../../constants/UrlConstants';
import {PendulumSimulationStrategy} from './strategies/pendulum-simulation.strategy';
import {PendulumGpuStrategy} from './strategies/pendulum-gpu.strategy';
import {PendulumCpuStrategy} from './strategies/pendulum-cpu.strategy';

@Component({
  selector: 'app-pendulum',
  imports: [
    BabylonCanvas,
    MatCard,
    MatCardContent,
    MatCardHeader,
    MatCardTitle,
    FormsModule,
    NgxSliderModule,
    TranslatePipe,
    MatButton,
    Information,
  ],
  templateUrl: './pendulum.component.html',
  styleUrl: './pendulum.component.scss',
})
class PendulumComponent {

  algoInformation: AlgorithmInformation = {
    title: 'PENDULUM.EXPLANATION.TITLE',
    entries: [
      {
        name: '',
        description: 'PENDULUM.EXPLANATION.EXPLANATION',
        link: UrlConstants.DOUBLE_PENDULUM_WIKI
      }
    ],
    disclaimer: 'PENDULUM.EXPLANATION.DISCLAIMER',
    disclaimerBottom: 'PENDULUM.EXPLANATION.DISCLAIMER_BOTTOM',
    disclaimerListEntry: ['PENDULUM.EXPLANATION.DISCLAIMER_1', 'PENDULUM.EXPLANATION.DISCLAIMER_2', 'PENDULUM.EXPLANATION.DISCLAIMER_3', 'PENDULUM.EXPLANATION.DISCLAIMER_4']
  };

  renderConfig: RenderConfig = {
    mode: '2D',
    initialViewSize: 2
  };

  trailDecayOptions: Options = {
    floor: MIN_TRAIL_DECAY,
    ceil: MAX_TRAIL_DECAY,
    logScale: false,
    step: 0.001,
    showTicks: false,
    hideLimitLabels: false,
    hidePointerLabels: false
  };

  gravityOptions: Options = {
    floor: MIN_G,
    ceil: MAX_G,
    logScale: false,
    step: 0.01,
    showTicks: false,
    hideLimitLabels: false,
    hidePointerLabels: false
  };

  dampingOptions: Options = {
    floor: MAX_DAMPING,
    ceil: MIN_DAMPING,
    logScale: false,
    step: 0.001,
    showTicks: false,
    hideLimitLabels: false,
    hidePointerLabels: false
  };

  lengthOptions: Options = {
    floor: MIN_LENGTH,
    ceil: MAX_LENGTH,
    logScale: false,
    step: 0.1,
    showTicks: false,
    hideLimitLabels: false,
    hidePointerLabels: false
  };

  massOptions: Options = {
    floor: MIN_MASS,
    ceil: MAX_MASS,
    logScale: false,
    step: 0.1,
    showTicks: false,
    hideLimitLabels: false,
    hidePointerLabels: false
  };

  readonly simParams = {
    time: 0,
    dt: 0.015,
    g: DEFAULT_G,
    m1: DEFAULT_M1_MASS,
    m2: DEFAULT_M2_MASS,
    l1: DEFAULT_L1_LENGTH,
    l2: DEFAULT_L2_LENGTH,
    damping: DEFAULT_DAMPING,
    trailDecay: DEFAULT_TRAIL_DECAY,
    impulseM1: 0.0,
    impulseM2: 0.0,
  };

  private currentSceneData: SceneEventData | null = null;
  private strategy: PendulumSimulationStrategy | null = null;

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

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

    const {engine, scene, gpuTier} = this.currentSceneData;

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

    this.strategy = gpuTier === 'webgpu'
      ? new PendulumGpuStrategy()
      : new PendulumCpuStrategy();

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

  private startParamUpdateLoop(scene: any): void {
    scene.onAfterRenderObservable.clear();
    scene.onAfterRenderObservable.add(() => {
      this.simParams.time += this.simParams.dt;

      if (this.strategy) {
        this.strategy.updateParams({...this.simParams});
      }

      this.resetImpulses();
    });
  }

  private resetImpulses() {
    if (this.simParams.impulseM1 !== 0.0) {
      this.simParams.impulseM1 = 0;
    }

    if (this.simParams.impulseM2 !== 0.0) {
      this.simParams.impulseM2 = 0;
    }
  }

  pushPendulum(m1: boolean) {
    if (m1) {
      this.simParams.impulseM1 = IMPULSE_M1;
      return;
    }

    this.simParams.impulseM2 = IMPULSE_M2;
  }

  resetPendulum() {
    this.createSimulation();
  }
}

export default PendulumComponent;
