/**
 * Universidad de La Laguna
 * Escuela Superior de Ingeniería y Tecnología
 * Grado en Ingeniería Informática
 * Programación de Aplicaciones Interactivas
 *
 * @author Pablo Pérez González
 * @since May  2022
 * @module Lissajous
 * @desc Lissajous curves
 *       The class of the lissajous curves
 * @see {@link https://academo.org/demos/lissajous-curves/}
 *
 */

'use strict';

/**
 * @class Lissajous
 * @classdesc Class that prints the Lisasajous curves in a Canvas Enviroment
 */
export class Lissajous {
  /**
   * The center x pos of the canvas
   * @type {number}
   * @private
   */
  #centerX = undefined;

  /**
   * The center y pos of the canvas
   * @type {number}
   * @private
   */
  #centerY = undefined;

  /**
   * The amplitud of the X axis
   * @type {number}
   * @private
   */
  #amplitudA = undefined;

  /**
   * The amplitud of the Y axis
   * @type {number}
   * @private
   */
  #amplitudB = undefined;

  /**
   * The number of lobes in the X axis
   * @type {number}
   * @private
   */
  #lobesAx = undefined;

  /**
   * The number of lobes in the X axis
   * @type {number}
   * @private
   */
  #lobesBy = undefined;

  /**
   * The phase of the curve
   * @type {number}
   * @private
   */
  #phase = undefined;

  /**
   * The velocity of the change of the phase
   * @type {number}
   * @private
   */
  #velocity = undefined;

  /**
   * If the animation is enabled or not
   * @type {boolean}
   * @private
   */
  #animationEnable = true;
  /**
   * The Canvas
   * @type {HTMLElement}
   * @private
   */
  #canvas = document.createElement('CANVAS');

  /**
   * The context of the canvas
   * @type {object}
   * @private
   */
  #ctx = this.#canvas.getContext('2d');

  /**
   * Create a Lissajous curve and sets the default values
   * @constructor
   * @public
   * @param {object} containerElement The HTML element that contains the curves
   */
  constructor(containerElement) {
    this.#amplitudA = 200;
    this.#amplitudB = 200;
    this.#lobesAx = 5;
    this.#lobesBy = 6;
    this.#phase = 1;
    this.#velocity = 0.0125;
    this.#canvas.width = document.documentElement.clientWidth - 200;
    this.#canvas.height = document.documentElement.clientHeight - 150;

    containerElement.querySelector('#canvasLocation').appendChild(this.#canvas);
    this.#centerX = this.#canvas.width / 2;
    this.#centerY = this.#canvas.height / 2;
    this.#ctx.translate(this.#centerX, this.#centerY);

    this.render();
  }

  /**
   * Draw the curve
   * @private
   * @param {object} ctx The Canvas context
   */
  #drawCurves(ctx) {
    ctx.beginPath();
    ctx.strokeStyle = 'black';


    for (let t = 0; t < 2 * Math.PI; t += 0.000628319) {
      const xPixel = this.#amplitudA *
          Math.sin(t * this.#lobesAx + this.#phase);
      const yPixel = this.#amplitudB * Math.sin(t * this.#lobesBy);
      ctx.lineTo(xPixel, yPixel);
    }

    ctx.stroke();
  }

  /**
   * Updates the private atribute
   * @param {number} value The new value
   */
  updateLobuleA(value) {
    this.#lobesAx = value;
  }

  /**
   * Updates the private atribute
   * @param {number} value The new value
   */
  updateLobuleB(value) {
    this.#lobesBy = value;
  }

  /**
   * Updates the private atribute
   * @param {number} value The new value
   */
  updateAmplitudeA(value) {
    this.#amplitudA = value;
  }

  /**
   * Updates the private atribute
   * @param {number} value The new value
   */
  updateAmplitudeB(value) {
    this.#amplitudB = value;
  }

  /**
   * Updates the private atribute
   * @param {number} value The new value
   */
  updatePhi(value) {
    this.#phase = value;
  }

  /**
   * Updates the private atribute
   * @param {boolean} onoff The new value
   */
  updateAnimation(onoff) {
    this.#animationEnable = Boolean(onoff);
  }

  /**
   * Render the curves
   */
  render = () => {
    this.#ctx.clearRect(
        -this.#canvas.width,
        -this.#canvas.height,
        this.#canvas.width * 2,
        this.#canvas.height * 2,
    );
    this.#drawCurves(this.#ctx);
    if (this.#animationEnable) {
      this.#phase += this.#velocity;
      requestAnimationFrame(this.render);
    } else {
      requestAnimationFrame(this.render);
    }
  };
}