/**
 * @module particles-sk
 * @description <h2><code>particles-sk</code></h2>
 *
 * <p>
 *   The main application element for particles in Skia.
 * </p>
 *
 */
import { $$ } from 'common-sk/modules/dom'
import { html, render } from 'lit-html'

import 'elements-sk/spinner-sk'

const CanvasKitInit = require('../../build/canvaskit/canvaskit.js');

const ZOOM_IN_FACTOR = 1.1; // 10%
const ZOOM_OUT_FACTOR = 1/ZOOM_IN_FACTOR;

// This element might be loaded from a different site, and that means we need
// to be careful about how we construct the URL back to the canvas.wasm file.
// Start by recording the script origin.
const scriptOrigin = new URL(document.currentScript.src).origin;
const kitReady = CanvasKitInit({
  locateFile: (file) => {
    return `${scriptOrigin}/static/${file}`;
  },
}).ready();

const loadingTemplate = (ele) => html`
<div class=player-loading title="Loading particles and engine."
     style='width: ${ele._config.width}px; height: ${ele._config.height}px;'>
  <div>Loading</div>
  <spinner-sk active></spinner-sk>
</div>`;

const runningTemplate = (ele) => html`
<div class=container>
  <!-- It would be more mobile friendly to use pointermove, but Safari doesn't support it-->
  <canvas id=player
          @wheel=${ele._wheel}
          @mousemove=${ele._drag}
          width=${ele._config.width * window.devicePixelRatio}
          height=${ele._config.height * window.devicePixelRatio}
          style='width: ${ele._config.width}px; height: ${ele._config.height}px;'>
    Your browser does not support the canvas tag.
  </canvas>
</div>`;

window.customElements.define('particles-player-sk', class extends HTMLElement {
  constructor() {
    super();

    this._engine = {
      kit:       null, // CanvasKit instance
      context:   null, // CK context.
      animation: null, // Particles instance
      surface:   null, // SkSurface
      canvas:    null, // Cached SkCanvas (surface.getCanvas()).
    };

    this._state = {
      loading:        true,
      paused:         this.hasAttribute('paused'),
      time:           0, // a monotonically increasing amount of ms
      lastTs:         0, // last time stamp we had a frame
    };


    this._lastDrag = null;
    this._zoomLevel = 1.0;
  }

  connectedCallback() {
    this._config = {
      width:      this.hasAttribute('width')  ? +this.getAttribute('width')  : 256,
      height:     this.hasAttribute('height') ? +this.getAttribute('height') : 256,
      bgcolor:    this.hasAttribute('bgcolor') ? +this.getAttribute('bgcolor') : -16777216, // black
    };

    this.render();
  }

  _drag(e) {
    if (!e.buttons || !e.shiftKey) { // ignore movements unless shift is held
      this._lastDrag = null;
      return;
    }
    if (this._lastDrag) {
      const dx = e.clientX - this._lastDrag[0];
      const dy = e.clientY - this._lastDrag[1];

      const canvas = this._engine.canvas.translate(dx / this._zoomLevel,
                                                   dy / this._zoomLevel);
    }
    this._lastDrag = [e.clientX, e.clientY];

  }

  _drawFrame() {
    if (!this._engine.animation || !this._engine.canvas) {
      return;
    }
    window.requestAnimationFrame(this._drawFrame.bind(this));
    if (!this._state.lastTs) {
      this._engine.animation.start(0, true);
      this._state.lastTs = Date.now();
    }

    if (this.isPlaying()) {
      this._state.time += (Date.now() - this._state.lastTs);
    }
    this._state.lastTs = Date.now();

    this._engine.kit.setCurrentContext(this._engine.context);
    this._engine.canvas.clear(this._config.bgcolor);

    this._engine.animation.update(this._state.time / 1000.0);
    this._engine.animation.draw(this._engine.canvas);
    this._engine.surface.flush();
  }

  initialize(config) {
    this._config.width = config.width;
    this._config.height = config.height;

    this.render();
    return kitReady.then((ck) => {
      this._engine.kit = ck;
      this._initializeParticles(config.json);
      this.render();
    });
  }

  _initializeParticles(particlesJSON, assets) {
    this._state.loading = false;

    // Rebuild the surface only if needed.
    if (!this._engine.surface ||
        this._engine.surface.width  != this._config.width ||
        this._engine.surface.height != this._config.height) {

      this.render();

      this._engine.surface && this._engine.surface.delete();
      let canvasEle = $$('#player', this);
      this._engine.surface = this._engine.kit.MakeCanvasSurface(canvasEle);
      if (!this._engine.surface) {
        throw new Error('Could not make SkSurface.');
      }
      // We don't need to call .delete() on the canvas because
      // the parent surface will do that for us.
      this._engine.canvas = this._engine.surface.getCanvas();
      this._engine.context = this._engine.kit.currentContext();
    }

    this._engine.animation && this._engine.animation.delete();

    this._engine.animation = this._engine.kit.MakeParticles(
                                          JSON.stringify(particlesJSON));
    if (!this._engine.animation) {
      throw new Error('Could not parse Particles JSON.');
    }

    this._engine.canvas.clear(this._config.bgcolor);
    // Center the animation
    this.resetView();

    this.restartAnimation();

    this._drawFrame();
  }

  isPlaying() {
    return !this._state.paused;
  }

  play() {
    if (!this.isPlaying()) {
      this._state.paused = false;
    }
    this.render();
  }

  pause() {
    if (this.isPlaying()) {
      this._state.paused = true;
    }
  }

  render() {
    render(this._state.loading
               ? loadingTemplate(this)
               : runningTemplate(this),
           this, {eventContext: this});
  }

  resetView() {
    const ck = this._engine.kit;
    const canvas = this._engine.canvas;
    // Reset to identity
    const tt = canvas.getTotalMatrix();
    const itt = ck.SkMatrix.invert(tt);
    canvas.concat(itt);
    // Zoom to the middle of the animation
    canvas.translate(this._config.width/2, this._config.height/2);
    this._zoomLevel = 1.0;
  }

  restartAnimation() {
    this._state.time = 0;
    this._state.lastTs = 0;
  }

  _wheel(e) {
    e.preventDefault();
    e.stopPropagation();

    let zoom = 0;
    if (e.deltaY < 0) {
      zoom = ZOOM_IN_FACTOR;
    } else {
      zoom = ZOOM_OUT_FACTOR;
    }
    this._zoomLevel *= zoom;
    const ck = this._engine.kit;
    const canvas = this._engine.canvas;

    const tt = canvas.getTotalMatrix();
    const itt = ck.SkMatrix.invert(tt);
    const pts = [e.clientX, e.clientY];
    ck.SkMatrix.mapPoints(itt, pts); // Transform DOM pts into canvas space

    let matr = ck.SkMatrix.scaled(zoom, zoom, pts[0], pts[1]);
    canvas.concat(matr);

  }
});
@@ -2534,17 +2534,18 @@
 ) : 
--16777216
+4278190080
 , //

Fix hardcoded black color