import {PANNING_ZERO_TOUCH_THRESHOLD} from "./config.js";
import {add, copy, div, mult, zero} from "./vector.js";

export class Panning {
  sim = undefined;
  touchStart = undefined;
  touchLatest = undefined;
  paused = false;
  velocity = zero;

  constructor(sim) {
    this.sim = sim;
  }

  initializeTouch({x, y}) {
    this.touchStart = {
      x,
      y,
      t: this.sim.rawTime,
      viewOrigin: copy(this.sim.display.viewOrigin),
    };
    this.touchLatest = {
      ...this.touchStart,
      dx: 0,
      dy: 0,
      dt: 0,
    };
  }

  toJSON() {
    return {
      velocity: this.velocity,
    };
  }

  fromJSON({velocity}) {
    this.velocity = copy(velocity);
    this.paused = true;
  }

  handlePointerDown({x, y}) {
    this.initializeTouch({x, y});
    if (this.paused) {
      this.paused = false;
    }
  }

  // With fast panning, panning velocity calculation happens every move;
  // With normal panning, calculation only happens at pointer up.
  handlePointerMove({x, y}) {
    if (this.touchStart) {
      this.touchLatest = {
        x,
        y,
        t: this.sim.rawTime,
        dx: x - this.touchStart.x,
        dy: x - this.touchStart.y,
        dt: this.sim.rawTime - this.touchStart.t,
      };

      // Convert pointer velocity to simulation scale
      let velocity = div(this.sim.pointer.latestVelocity, this.sim.display.scale);

      // Optional time scale compensation
      if (this.sim.getOption('compensate.timeScale')) {
        velocity = div(velocity, this.sim.timeScale);
      }

      // Additional scaling factor
      velocity = mult(velocity, this.sim.getOption('display.panningSpeed'));

      // Add pointer velocity to current panning velocity
      this.velocity = add(this.velocity, velocity);
    }
  }

  handlePointerUp() {
    if (this.touchStart && this.touchLatest) {
      if (this.touchLatest.dt < PANNING_ZERO_TOUCH_THRESHOLD) {
        this.velocity = zero;
      }
      this.touchStart = undefined;
    }
  }

  frame(elapsedTime) {
    const {display} = this.sim;

    // Apply update to viewOrigin based on panning
    if (!this.paused) {
      // elapsedTime is scaled by time scale, is that what we want?
      // Yes because if panning.velocity == obj.velocity, object should stay in view
      display.viewOrigin = add(display.viewOrigin, mult(this.velocity, elapsedTime));
    }

    if (this.sim.getOption('debug.panningInfo')) {
      const {x, y} = this.sim.panning?.velocity ?? {};
      this.sim.info['Panning Velocity'] = [`${x?.toPrecision(6)}, `, y?.toPrecision(6)];
      const {centerOfMass} = this.sim.system.computeSystemCenter();
      this.sim.info['Center of Mass'] = [`${centerOfMass.x.toPrecision(6)}, `, centerOfMass.y.toPrecision(6)];
      this.sim.info['Net Angular Momentum'] = this.sim.system.computeSystemAngularMomentum().toPrecision(6);
    }
  }

  setVelocity(velocity) {
    this.velocity = velocity;
    if (!this.sim.playing) {
      this.paused = true;
    }
  }
}
