<script>

// https://brm.io/matter-js/docs/classes/Engine.html
// import {
//   // Engine,
//   // Render,
//   // World,
//   Bodies,
//   Body,
//   Events,
//   Composite,
//   // Composites,
//   // Constraint,
//   // Vertices,
//   // Mouse,
//   // MouseConstraint,
//   // Query,
//   // Common
// } from "matter-js";

import Matter from "matter-js";
import MatterAttractors from "matter-attractors";
// Matter.use(MatterAttractors);

// import { easeInOutQuad, easeInOutQuart } from 'easing-utils';
import Tween from "@tweenjs/tween.js";

import { mapState, mapActions } from 'pinia'
import { ConcernementsStore } from '@/stores/concernements'

export default {
  inject: ['canvasMap', 'matterEngine'],
  data() {
    return {
      id: null,
      entities: null,
      // opened_entite_id: null,
      canvas: null,
      ctx: null,
      pos : {
        x: 0,
        y: 0
      },
      ray: 60,
      time: 0,
      salientPoints: [],
      scale: 1,
      opacity: 0,
      tween: null,
      body: null,
      constraint: null
    }
  },
  props: ['concernement', 'opened'],
  computed: {
    ...mapState(ConcernementsStore,['concernementsByID']),
    ...mapState(ConcernementsStore,['opened_entite_id'])
  },
  created () {
    // console.log(`ConcernementsMapItem ${this.concernement.id} created`, this.canvasMap, this.matterEngine);
    this.id = this.concernement.id
    this.entites = this.concernement.entites
    this.entites_byid = this.concernement.entites_byid

    // console.log(`ConcernementsMapItem ${this.concernement.id} $route`, this.id, this.$route);
    // if (this.$route.name === 'concernement'
    //   && parseInt(this.$route.params.id) === this.id
    //   && typeof this.$route.params.eid !== "undefined") {
    //     // console.log("we have an entity");
    //     this.opened_entite_id = parseInt(this.$route.params.eid);
    // }
    
    this.parsePoints()
    this.getSalientPoints()
    if (this.canvasMap) {
      this.initCanvasMap()
    }

  },
  // mounted() {
  //   console.log(`ConcernementsMapItem ${this.concernement.id} mounted`, this.canvasMap.canvas);
    
  // },
  watch: {
    // canvasMap (n, o) {
    //   console.log("concernementItem watch canvasMap", o, n);
    // }
    canvasMap: {
      handler (n, o){
        // console.log("concernementItem watch canvasMap.ctx", typeof this.canvas, o, n);
        if (!this.canvas) {
          this.initCanvasMap()
        }
      },
      deep: true
    },
    opened: {
      handler (n, o) {

        if(n){ // opened
          this.openClose(true);
        }else{ // closed
          this.openClose(false)
        }
      },
      deep: true
    }
  },
  methods: {
    initCanvasMap (){
      // console.log(`ConcernementsMapItem ${this.concernement.id} initCanvasMap`);
      // record canvas and ctx for rendering (drawing)
      this.canvas = this.canvasMap.canvas
      this.ctx = this.canvasMap.ctx

      // define init position of the item
      this.pos = this.getRandomPos();
      //
      this.initMatterBody()
    },
    initMatterBody (){
      
      // MATTER
      // create the matter body and add it to the engine
      if (!this.body) {
        // console.log('concernementItem creating body');

        // https://github.com/liabru/matter-attractors/issues/8        
        // https://github.com/liabru/matter-attractors/blob/master/index.js
        // MatterAttractors.Attractors.gravityConstant = -5;
        
        // Create parts of the body : main big circle & entities
        var parts = [
          Matter.Bodies.circle(0, 0, this.ray, {
            item_type: 'concernement',
            id: this.concernement.id,
          })
        ];
        for (let i = 0; i < this.entites.length; i++) {
          // parts.push(Matter.Bodies.circle(this.pos.x+this.entites[i].display.pos.x, this.pos.y+this.entites[i].display.pos.y, 15, {
          //   item_type: 'entite',
          //   id: this.entites[i].id  
          // }))
          parts.push(Matter.Bodies.circle(this.entites[i].display.pos.x, this.entites[i].display.pos.y, 0.8, {
            item_type: 'entite',
            id: this.entites[i].entite.id,
            cid: this.concernement.id, 
            isSensor: true
          }))
        }

        // create the body
        this.body = Matter.Body.create({
          parts: parts,
          item_type: 'concernement',
          id: this.concernement.id,
          frictionAir: 0,
          // mass: Math.pow(3, this.entites.length),
          mass: 10,
          restitution: 0.4,
          collisionFilter: {
            group: -1
          },
          plugin: {
            attractors: [
              // there is a built in helper function for Newtonian gravity!
              // you can find out how it works in index.js
              MatterAttractors.Attractors.gravity
            ]
          }
        });
        Matter.Body.setPosition(this.body, this.pos);

        // add init velocity
        let delta = 10;
        Matter.Body.setVelocity(this.body, {
            x: -delta + Math.random()*delta*2,
            y: -delta + Math.random()*delta*2
          });
        // console.log('concernementItem mass', this.body.mass);
        Matter.Composite.add(this.matterEngine.world, this.body);
        // console.log('concernement body', this.body);

        // // listen for animate event dispatched from parent
        // this.canvas.addEventListener('animate', this.animate)

        // listen for afterUpdate event from Matter.Engine object
        Matter.Events.on(this.matterEngine, "beforeUpdate", this.onBeforeEngineUpdate);
        Matter.Events.on(this.matterEngine, "afterUpdate", this.onAfterEngineUpdate);
      }
    },
    getRandomPos(){
      return {
        x: this.ray/2 + Math.random()*(this.canvas.width - this.ray),
        y: this.ray/2 + Math.random()*(this.canvas.height - this.ray)
      };
    },
    parsePoints (){
      // converts data (menace/maintien, actuel/future, prise) into atcual position x,y
      for (let i = 0; i < this.entites.length; i++) {
        let entite = this.entites[i]
        // console.log('entite', entite);
        
        this.entites[i].display = {
          alpha: null,
          ray: null
        }
        // RAYON
        // https://stackoverflow.com/questions/5731863/mapping-a-numeric-range-onto-another
        // slope = (output_end - output_start) / (input_end - input_start)
        // output = output_start + slope * (input - input_start)
        // from range 0 -> 100 to range 0 -> this.ray
        let init_max = 100
        let slope = this.ray / init_max
        this.entites[i].display.ray = slope * (init_max - entite.prise);
        // if (this.concernement.id === 28) {
        //   console.log(`entity prise: ${entite.prise} | ray: ${this.entites[i].display.ray}`);
        // }
        

        // ANGLE
        // -90 <= mm <= 90
        if (entite.actuelfuture) {
          // future en haut : 180 <= a <= 360
          // from -90 -> 90 to range 180 -> 360
          this.entites[i].display.alpha = entite.menacemaintien + 270
        } else {
          // actuel: en bas : O <= a <= 180
          // from -90 -> 90 to range 180 -> 0
          this.entites[i].display.alpha = -1 * entite.menacemaintien + 90
        }

        // POSITION X Y (par rapport au centre de l'entite)
        this.entites[i].display.pos = {
          x: this.entites[i].display.ray * Math.cos(this.entites[i].display.alpha * (Math.PI/180)),
          y: this.entites[i].display.ray * Math.sin(this.entites[i].display.alpha * (Math.PI/180))
        }

        this.entites_byid[entite.entite.id].display = this.entites[i].display;
      }
    },
    getSalientPoints () {
      // debugger
      // console.log(this.entites);
      let arc = 360/10;
      // loop through arcs
      for (let i = 0; i <= 360/arc; i++) {
        // loop through entities to find the farest on the arc
        let max_r = 0;
        let farest = null;
        for (let j = 0; j < this.entites.length; j++) {
          let entite = this.entites[j];
          if(arc*i <= entite.display.alpha && entite.display.alpha <= arc*i+arc) {
            // if entity is in arc
            if (entite.display.ray > max_r) { //  && entite.display.ray > this.ray/2 // and farest from minimu
              // if entity is farest from precedent one 
              max_r = entite.display.ray;
              farest = entite;
            }
          }
        }
        if (farest) {
          this.salientPoints.push(farest.display)
        }
      }
      // console.log(`this.salientPoints ${this.concernement.id}`, this.salientPoints);
    },
    // onBeforeEngineUpdate (event) {
    //   if (this.opened) {
    //     // Matter.Body.setPosition(this.body, this.pos);
    //   }
    // },
    openClose(open) {
      // console.log(`ConcernementsMapItem ${this.concernement.id} openClose: ${open}`);
      if (this.tween) {
        this.tween.stop();
      }
      if (open) {
        // opening tweening
        this.tween = new Tween.Tween({s: this.scale, x: this.pos.x, y: this.pos.y, o: 0})
          .to({
            s: 7,
            x: (this.canvas.width - 450) / 2,
            y: this.canvas.height / 2,
            o: 0.8
          }, 800)
          .onUpdate((obj) => {
            // https://github.com/liabru/matter-js/issues/986#issuecomment-812488873
            // revert to the original size (by reverting the previous scale)
            Matter.Body.scale(this.body, 1 / this.scale, 1 / this.scale)
            // then scale again to new scale
            Matter.Body.scale(this.body, obj.s, obj.s)
            // record new scale
            this.scale = obj.s;
            this.opacity = obj.o;
            
            Matter.Body.setPosition(this.body, {x:obj.x, y:obj.y});
            this.pos = {x:obj.x, y:obj.y};
          })
          .onComplete((obj) => {
            this.constraint = Matter.Constraint.create({
              pointA: this.pos,
              bodyB: this.body,
              stiffness: 1,
              damping: 0,
              length: 0
            });
            Matter.Composite.add(this.matterEngine.world, [this.body, this.constraint]);
          });
          // recreate the matter engine event to get it a the end of the events stack
          Matter.Events.off(this.matterEngine, "afterUpdate", this.onAfterEngineUpdate);
          Matter.Events.on(this.matterEngine, "afterUpdate", this.onAfterEngineUpdate);
      } else {
        // closing
        if(this.constraint){
          Matter.Composite.remove(this.matterEngine.world, this.constraint);
        }
        this.tween = new Tween.Tween({s: this.scale, o: 1})
          .to({s: 1, o: 0}, 500)
          .onUpdate((obj) => {
            // https://github.com/liabru/matter-js/issues/986#issuecomment-812488873
            // revert to the original size (by reverting the previous scale)
            Matter.Body.scale(this.body, 1 / this.scale, 1 / this.scale)
            // then scale again to new scale
            Matter.Body.scale(this.body, obj.s, obj.s)
            // record new scale
            this.scale = obj.s;
            this.opacity = obj.o;
          });
      }
      this.tween.easing(Tween.Easing.Quadratic.InOut).start();
    },
    onBeforeEngineUpdate (event) {
      if (this.tween) {
        this.tween.update();
      }
    },
    onAfterEngineUpdate (event) {
      // respawn element if outside screen
      if(this.pos.x < 0
        || this.pos.x > this.canvas.width
        || this.pos.y < 0
        || this.pos.y > this.canvas.height){
          this.pos = this.getRandomPos()
          Matter.Body.setPosition(this.body, {x:this.pos.x, y:this.pos.y});
        }
      this.draw()
    },
    draw() {

      if (!this.ctx) return;

      this.pos = this.body.position;

      if (this.opened) {
        // BOUSSOLE
        // exterieur circle
        this.ctx.beginPath();
        this.ctx.lineWidth = 2;
        this.ctx.strokeStyle = `rgba(255,255,255,${this.opacity})`;
        // external circle is %8 less than max ray = (*0.92)
        this.ctx.arc(this.pos.x, this.pos.y, this.ray*this.scale*0.92, 0, 2 * Math.PI, false);
        // this.ctx.stroke();

        // interieur circle
        this.ctx.arc(this.pos.x, this.pos.y, this.ray/2*this.scale, 0, 2 * Math.PI, false);
        // this.ctx.stroke();      

        // axes
        // vertical
        this.ctx.moveTo(this.pos.x, this.pos.y - this.ray*this.scale);
        this.ctx.lineTo(this.pos.x, this.pos.y + this.ray*this.scale);
        // horizontal
        this.ctx.moveTo(this.pos.x - this.ray*this.scale, this.pos.y);
        this.ctx.lineTo(this.pos.x + this.ray*this.scale, this.pos.y);
        // this.ctx.stroke();
        
        // fleches
        // haute
        this.ctx.moveTo(this.pos.x - (8*this.scale), this.pos.y - this.ray*this.scale*0.92 + (8*this.scale));
        this.ctx.lineTo(this.pos.x, this.pos.y - this.ray*this.scale*0.92);
        this.ctx.lineTo(this.pos.x + (8*this.scale), this.pos.y - this.ray*this.scale*0.92 + (8*this.scale));      
        // milieu
        this.ctx.moveTo(this.pos.x - (8*this.scale), this.pos.y + (8*this.scale));
        this.ctx.lineTo(this.pos.x, this.pos.y);
        this.ctx.lineTo(this.pos.x + (8*this.scale), this.pos.y + (8*this.scale));

        this.ctx.stroke();

        // MOINS - PLUS
        this.ctx.beginPath();
        this.ctx.lineWidth = 8;
        this.ctx.strokeStyle = `rgba(255,255,255,${this.opacity})`;;
        // PLUS
        // horizontal
        this.ctx.moveTo(this.pos.x + this.ray*this.scale - (5 * this.scale), this.pos.y - this.ray*this.scale);
        this.ctx.lineTo(this.pos.x + this.ray*this.scale + (5 * this.scale), this.pos.y - this.ray*this.scale);
        // vertical
        this.ctx.moveTo(this.pos.x + this.ray*this.scale, this.pos.y - this.ray*this.scale - (5 * this.scale));
        this.ctx.lineTo(this.pos.x + this.ray*this.scale, this.pos.y - this.ray*this.scale + (5 * this.scale));
        
        // MOINS
        // horizontal
        this.ctx.moveTo(this.pos.x - this.ray*this.scale - (5 * this.scale), this.pos.y - this.ray*this.scale);
        this.ctx.lineTo(this.pos.x - this.ray*this.scale + (5 * this.scale), this.pos.y - this.ray*this.scale);
        // vertical

        this.ctx.stroke();
      }

      // contours
      if (this.salientPoints.length > 3) {

        this.ctx.beginPath();
        this.ctx.lineWidth = 1;
        this.ctx.strokeStyle = "#000";
        this.ctx.fillStyle = "rgba(255,255,255,0.8)";
        this.ctx.moveTo(this.pos.x+this.salientPoints[0].pos.x*this.scale*1.15, this.pos.y+this.salientPoints[0].pos.y*this.scale*1.15)
        for (let j = 1; j < this.salientPoints.length; j++) {
          this.ctx.lineTo(this.pos.x+this.salientPoints[j].pos.x*this.scale*1.15, this.pos.y+this.salientPoints[j].pos.y*this.scale*1.15)
        }
        this.ctx.lineTo(this.pos.x+this.salientPoints[0].pos.x*this.scale*1.15, this.pos.y+this.salientPoints[0].pos.y*this.scale*1.15)
        this.ctx.fill();
        this.ctx.stroke();
      }

      if (this.opened) {
        // place all entities points
        // using entities array
        // for (let i = 0; i < this.entites.length; i++) {
        //   let entite = this.entites[i];
        //   // console.log('entite', entite);
        //   this.ctx.beginPath();
        //   this.ctx.arc(this.pos.x+entite.display.pos.x*this.scale, this.pos.y+entite.display.pos.y*this.scale, 5, 0, 2 * Math.PI, false);
        //   this.ctx.strokeStyle = "#F00";
        //   this.ctx.stroke();
        // }

        // OR using entitées matter bodies
        for (let i = 0; i < this.body.parts.length; i++) {
          if (this.body.parts[i].item_type === 'entite') {
            let part = this.body.parts[i];
            this.ctx.beginPath();
            this.ctx.arc(part.position.x, part.position.y, 0.3*this.scale, 0, 2 * Math.PI, false);
            // console.log(part.id, this.opened_entite_id);
            if (part.id === this.opened_entite_id) {
              this.ctx.fillStyle = "#F00";
            } else {
              this.ctx.fillStyle = "#000";
            }
            this.ctx.fill();
          }
        }
      }

      // // concernement id @center
      // this.ctx.beginPath();
      // // this.ctx.arc(this.pos.x, this.pos.y, 4, 0, 2 * Math.PI, false);
      // this.ctx.fillStyle = "#000";
      // this.ctx.fillText(this.concernement.id, this.pos.x, this.pos.y)
      // this.ctx.fill();

    }
  },
  render() {
    // console.log('render()', this.ctx);
  },
}

</script>
