'use strict'

import { Container } from '@pixi/display';

import { Effect } from './effect';
import { Vector } from '../vector';

/**
 * A rotating effect that involves rotating the game a specified number of degrees.
 */
export class Rotate extends Effect {
  /**
   * A reference to the initial angle.
   * 
   * @private
   * 
   * @property {number}
   */
  private _initialAngle: number;

  /**
   * The angle to rotate to, from 0 to 360 with 0 being the default state and 360 being all the way around back to the default state.
   * 
   * @private
   * 
   * @property {number}
   */
  private _desiredAngle: number;

  /**
   * The initial pivot of the container.
   * 
   * @private
   * 
   * @property {Vector}
   */
  private _initialPivot: Vector;

  /**
   * A reference to the easing function to use for this effect.
   * 
   * @private
   * 
   * @property {Function}
   */
  private _easing: Function;

  /**
   * @param {Container} container A reference to the container to apply the rotate effect to.
   * @param {number} angle The angle to rotate to, from 0 to 360 with 0 being the default state and 360 being all the way around back to the default state.
   * @param {number} duration The amount of time, in milliseconds, that the effect should take.
   * @param {Function} [easing] The easing function that should be used.
   */
  constructor(container: Container, angle: number, duration: number, easing?: Function) {
    super(container);

    this._initialAngle = container.angle;
    this._desiredAngle = angle
    this.duration = duration;
    this._easing = easing || this.easeLinear;
    this._initialPivot = { x: this.container.pivot.x, y: this.container.pivot.y };

    if (this._initialPivot.x == 0) this.container.pivot.x = this.container.width / 2;
    if (this._initialPivot.y == 0) this.container.pivot.y = this.container.height / 2;
  }

  /**
   * Updates the status of this effect on a frame by frame basis.
   */
  update() {
    if (this.criteriaMet()) {
      this.finished.dispatch();
      return;
    }

    this.current = performance.now();

    const timeDiffPercentage = (this.current - this.started) / this.duration;
    const percentageThroughAnimation = this._easing(timeDiffPercentage);

    const angleAmount = this._desiredAngle * percentageThroughAnimation;
    this.container.angle = this._initialAngle + angleAmount;

    if (this.useRAF) this.id = requestAnimationFrame(() => this.update());
  }

  /**
   * Checks to see if the container's current angle is very close to the desired angle.
   * 
   * We can't use container angle == desired angle because with the game loop we might miss that exact moment so we check a very small window.
   * 
   * @private
   * 
   * @returns {boolean} Returns true if the angle criteria is met or false otherwise.
   */
  criteriaMet(): boolean {
    if (this.container.angle > this._desiredAngle) return true;
    return false;
  }
}