import {AfterViewInit, Component, ElementRef, inject, OnDestroy, ViewChild} from '@angular/core';
import {MatMiniFabButton} from '@angular/material/button';

import {IntersectionPlaneService, PlaneOrientation} from '../../services/intersection-plane.service';
import {MatIcon} from '@angular/material/icon';
import {MatTooltip} from '@angular/material/tooltip';
import {RendererService} from '../../services/renderer.service';
import {PlayerModelService} from '../../services/player-model.service';
import {InputHandlerService} from '../../services/input-handler.service';
import {FormsModule} from '@angular/forms';
import {MatFormField, MatInput, MatLabel} from '@angular/material/input';
import {ParticleManagerService} from '../../services/particle-manager.service';

@Component({
  selector: 'app-render-container',
  imports: [
    MatIcon,
    MatMiniFabButton,
    MatTooltip,
    FormsModule,
    MatInput,
    MatFormField,
    MatLabel
  ],
  templateUrl: './render-container.component.html',
  styleUrl: './render-container.component.scss'
})
export class RenderContainerComponent implements AfterViewInit, OnDestroy {
  @ViewChild('rendererContainer') rendererContainer!: ElementRef;

  private readonly intersectionPlaneService = inject(IntersectionPlaneService);
  private readonly playerModelService = inject(PlayerModelService);
  private readonly inputHandlerService = inject(InputHandlerService);
  private readonly rendererService = inject(RendererService);
  private readonly particleManagerService = inject(ParticleManagerService);

  ngAfterViewInit(): void {
    this.initializeScene();
    this.animate();
  }

  /**
   * Clean up resources when component is destroyed
   */
  ngOnDestroy(): void {
    if (this.rendererService.renderer) {
      this.inputHandlerService.cleanup(this.rendererService.renderer.domElement);
    }
  }

  /**
   * Initialize the 3D scene and all related components
   */
  private initializeScene(): void {
    this.rendererService.initializeRenderer(this.rendererContainer);
    this.playerModelService.loadSkinTexture('/public/img/skins/steve.png')
      .then(() => {
        // Then create the player model with the texture applied
        this.playerModelService.createPlayerModel();
      });
    this.intersectionPlaneService.createIntersectionPlane();
    this.inputHandlerService.initializeInputHandlers(this.rendererService.renderer.domElement);
  }

  /**
   * Animation loop
   */
  private animate(): void {
    requestAnimationFrame(this.animate.bind(this));
    this.intersectionPlaneService.updatePlaneOrientation(this.rendererService.camera);
    this.rendererService.render();
  }

  /**
   * Get whether the plane is locked
   */
  public get isPlaneLocked(): boolean {
    return this.intersectionPlaneService.isPlaneLocked();
  }

  public set opacity(opacity: number) {
    this.intersectionPlaneService.currentOpacity = opacity;
  }

  public get opacity(): number {
    return this.intersectionPlaneService.currentOpacity;
  }

  // Grid proxies
  public get gridVisible(): boolean {
    return this.intersectionPlaneService.getGridVisible();
  }

  public set gridVisible(v: boolean) {
    this.intersectionPlaneService.setGridVisible(v);
  }

  public get gridDensity(): number {
    return this.intersectionPlaneService.getGridDensity();
  }

  public set gridDensity(d: number) {
    this.intersectionPlaneService.setGridDensity(d);
  }

  // Grid snap proxies
  public get gridSnapEnabled(): boolean {
    return this.intersectionPlaneService.getGridSnapEnabled();
  }

  public set gridSnapEnabled(v: boolean) {
    this.intersectionPlaneService.setGridSnapEnabled(v);
  }

  /**
   * Toggle the plane locked state
   */
  public togglePlaneLock(): void {
    const newLockedState = !this.isPlaneLocked;
    this.intersectionPlaneService.setPlaneLocked(newLockedState);
  }

  public resetCamera(): void {
    this.rendererService.resetCamera();
  }

  public toggleShowParticlesWhenIntersectingPlane(): void {
    this.particleManagerService.onlyIntersectingParticles = !this.particleManagerService.onlyIntersectingParticles;
  }

  public toggleShowCharacter(): void {
    this.playerModelService.showCharacter = !this.playerModelService.showCharacter;
  }

  /**
   * Get the current plane orientation
   */
  public get currentPlaneOrientation(): PlaneOrientation {
    return this.intersectionPlaneService.getCurrentOrientation();
  }

  /**
   * Retrieves the value indicating whether only intersecting particles are being considered.
   */
  public get onlyIntersecting(): boolean {
    return this.particleManagerService.onlyIntersectingParticles;
  }

  /**
   * Retrieves the value indicating whether the character is being rendered.
   */
  public get showCharacter(): boolean {
    return this.playerModelService.showCharacter;
  }

  /**
   * Set the plane orientation
   */
  public setPlaneOrientation(orientation: PlaneOrientation): void {
    this.intersectionPlaneService.setPlaneOrientation(orientation);
  }

  /**
   * Get all available plane orientations
   */
  public get planeOrientations(): typeof PlaneOrientation {
    return PlaneOrientation;
  }
}
