(function ($, Drupal, once) {
  var _boussole;
  // behaviour is attached by preporcess ouatminimal_preprocess_field_multiple_value_form__field_entite__node_concernement() on ouatminimal.theme file
  Drupal.behaviors.boussole = {
    attach: function (context, settings) {
      console.log("Drupal Boussole behavior", context);
      let $boussole_wrapper = document.querySelector('#boussole-layout');
      once('boussole-class-behaviour', '#boussole-layout', context).forEach(element => {
        // let tabs = 
        // div.field-group-tabs-wrapper
        // input.horizontal-tabs-active-tab
        _boussole = new Boussole($boussole_wrapper);
      });
      
      let $form = $boussole_wrapper.querySelector(':scope>.form-item');
      console.log($form);
      let $tabledrag = $form.querySelector('table.field-multiple-table');
      console.log($tabledrag);
      let $form_entities = $tabledrag.querySelectorAll(':scope>tbody>tr>td:not(.field-multiple-drag):not(.delta-order)>div');
      console.log($form_entities);

      let entities = {};
      [...$form_entities].forEach($entity => {
        console.log($entity);
        let ajax_new_content = $entity.classList.contains('ajax-new-content'); 
        if(ajax_new_content){
          // its a new paragraph just added by ajax
          $entity = $entity.querySelector(':scope>div');
        }

        let id = $entity.getAttribute('id');
        let $af = $entity.querySelector('.paragraphs-subform .field--name-field-actuel-future');
        let $p = $entity.querySelector('.paragraphs-subform .field--name-field-prise input');
        let $mm = $entity.querySelector('.paragraphs-subform .field--name-field-menace-maintien-degres input');
        let e = {
          form: $form,
          wrapper:{
            $dom: $entity,
            id: id
          },
          title: {
            $dom: null,
            value: null
          },
          af: {
            $dom: $af,
            value: parseInt($af.querySelector('input:checked').value)
          },
          p: {
            $dom: $p,
            value: parseFloat($p.value)
          },
          mm: {
            $dom: $mm,
            value: parseFloat($mm.value)
          }
        }

        // title does not exists when we add an "entité intégrée" as we also have to create the referenced node 
        let $title = $entity.querySelector('.paragraphs-subform>.field--type-entity-reference.field--name-field-entite td.inline-entity-form-node-label');
        if ($title) {
          e.title= {
            $dom: $title,
            value: $title.textContent
          };
        }


        entities[id] = e;
      });
      console.log(entities);
      _boussole.updateCreateEntities(entities);
    }
  };

  class Boussole {
    constructor($boussole_wrapper) {
      this.$boussole_wrapper = $boussole_wrapper;
      this.$boussole = document.querySelector('#boussole-layout>.boussole-wrapper>.boussole');
      this.entities = {}

      this.initTabChecking();
      this.enableDragArea()
    }

    initTabChecking(){
      // debugger
      this.$field_group_tab = this.$boussole_wrapper.closest('details.field-group-tab');
      // watch for tabs opening and closing
      // hidden input do not triggers events
      // so we have to use mutationObserver tomake it trigger the change event
      this.$tabInput = this.$field_group_tab.parentElement.querySelector(':scope>input[type="hidden"]');
      let observer = new MutationObserver(function(mutations, observer) {
        if(mutations[0].attributeName == "value") {		
          // console.log('mutation observed', mutations, observer);
          let event = new Event('change');
          mutations[0].target.dispatchEvent(event);
        }
      });
      observer.observe(this.$tabInput, {attributes: true});
      // then we listen to the event
      this.$tabInput.addEventListener('change', event => {
        // console.log('tab input changed', event, this);
        this.wrapperTabChanged(event);
      });
      this.checkTabIsOpen();
    }

    checkTabIsOpen(){
      this.tabisopen = this.$field_group_tab.getAttribute('id') === this.$tabInput.value;
    }

    wrapperTabChanged(event){
      this.checkTabIsOpen();
      if(this.tabisopen){
        this.updateEntities();
      }
    }

    enableDragArea(){
      this.$boussole.addEventListener("dragenter", (event) => {
        event.preventDefault();
      });
      this.$boussole.addEventListener("dragover", (event) => {
        event.preventDefault();
      });
      this.$boussole.addEventListener("drop", (event) => {
        event.preventDefault();
        let data = JSON.parse(event.dataTransfer.getData('text/plain'));
        console.log('boussole drop', event, data);
        data.clientX = event.clientX;
        data.clientY = event.clientY;
        // return on the entity class
        this.entities[data.entity_id].dropedOnBoussole(data);
      });
    }

    updateCreateEntities(entities){
      // console.log('Class Boussole update entities', entities);
      for (const [id, values] of Object.entries(entities)) {
        console.log(id, typeof this.entities[id], values);
        if (typeof this.entities[id] === 'undefined') {
          this.entities[id] = new Entity(this.$boussole, id, values);
        }else{
          this.entities[id].updateValuesFromForm(values);
        }
      }
    }

    updateEntities(){
      for (const [id, entity] of Object.entries(this.entities)) {
        entity.updatePos();
      }
    }
  }

  class Entity {
    constructor($container, id, values){
      console.log('Entity constructor', $container, id, values);
      this.$container = $container;
      this.updateSceneSize();
      this.id = id;
      this.values = values;
      this.listenToFormValue();
      this.createDomElemt();
    }

    updateSceneSize(){
      this.sceneSize = {
        w: this.$container.clientWidth,
        h: this.$container.clientHeight
      };
    }

    createDomElemt(){
      this.$e = document.createElement('div');
      this.$e.classList.add('entity');
      this.$e.setAttribute('id', `${this.id}-boussole-point`);
      if (this.values.title.value) {
        // the paragraph as an entity referenced
        this.$e.setAttribute('title', this.values.title.value);
      }else{
        // new paragraph with no entity referenced
        this.$e.classList.add('ajax-new');
      }
      
      this.$container.append(this.$e);
      console.log(this.$e);
      this.updatePos();
      this.initOverEvents();
      this.initDraggable();
    }
    
    updateDomElemt(){
      if (this.values.title.value) {
        // the paragraph as an entity referenced
        this.$e.setAttribute('title', this.values.title.value);
        this.$e.classList.remove('ajax-new');
      }else{
        // new paragraph with no entity referenced
        this.$e.classList.add('ajax-new');
      }
    }

    initOverEvents(){
      // on mouseover point on boussole
      this.$e.addEventListener('mouseenter', (event) => {
        console.log('enter boussole entité', this.values);
        this.values.wrapper.$dom.classList.add('entite-over');
        // scroll to form item
        var $tr  = this.values.wrapper.$dom.closest("tr.draggable");
        var top = $tr.offsetTop;
        // console.log('offsetTop', top, $tr);
        this.values.form.scrollTo({top: top, left: 0, behaviour: "smooth"});
      });
      this.$e.addEventListener('mouseleave', (event) => {
        // console.log('leave boussole entité', this.values);
        this.values.wrapper.$dom.classList.remove('entite-over');
      });

      // on mouseover formitem on drupal form
      this.values.wrapper.$dom.addEventListener('mouseenter', (event) => {
        this.$e.classList.add('entite-over');
      })
      this.values.wrapper.$dom.addEventListener('mouseleave', (event) => {
        this.$e.classList.remove('entite-over');
      })

    }

    initDraggable(){
      this.$e.setAttribute('draggable', true);
      this.$e.addEventListener('dragstart', event => {
        // console.log('dragstart', event);
        let data = {
          entity_id : this.id,
          dragging_start_x : event.target.offsetLeft - event.clientX,
          dragging_start_y : event.target.offsetTop - event.clientY
        };
        event.dataTransfer.setData('text/plain', JSON.stringify(data));
        // event.dataTransfer.effectAllowed = "move";
        setTimeout(() => {event.target.classList.add('hide');}, 0);
      });
      this.$e.addEventListener('dragend', event => {
        // console.log('dragend', event);
        event.target.classList.remove('hide');
      });
      // CAN'T DO THAT, EVENT IS FIRED FOR EACH ENTITIES
      // TAKING CARE OF THE DROP IN THE BOUSSOLE CLASS
      // this.$container.addEventListener('drop', event =>{
      //   event.preventDefault();
      //   console.log('drop', event);
      // })
    }

    dropedOnBoussole(data){
      this.$e.style.left = data.clientX + data.dragging_start_x + 'px';
      this.$e.style.top = data.clientY + data.dragging_start_y + 'px';
      this.updateFormValuesFromBoussole();
    }

    updateFormValuesFromBoussole(values){
      let compstyle = window.getComputedStyle(this.$e);
      // AF
      let top = parseInt(compstyle.top); 
      let af = top >= this.sceneSize.h/2 ? 0 : 1;
      this.values.af.value = af;
      this.values.af.$dom.querySelector(`input[value="${af}"]`).checked = true;

      // (x, y)
      let x = parseInt(compstyle.left) - this.sceneSize.h/2;
      let y = Math.abs(this.sceneSize.h/2 - top);
      let a = parseFloat(Math.atan(x/y) * (180/Math.PI)).toFixed(2);
      let r = Math.sqrt(Math.pow(x,2)+Math.pow(y,2));
      
      this.values.mm.value = this.values.mm.$dom.value = a;
      
      // https://stackoverflow.com/questions/5731863/mapping-a-numeric-range-onto-another
      // from range 0 -> rayon_de_la_boussole moins 8% de padding |to| range 0 -> 100
      let r_max = this.sceneSize.w/2 * 0.92; 
      let r_slope = (100 - 0) / (r_max - 0);
      this.values.p.value = this.values.p.$dom.value = 100 - parseFloat(0 + r_slope * (r - 0)).toFixed(2);
      // this.values.p.$dom.value = this.values.p.value;
      
      console.log('x:', x, 'r:', r, 'a:', a, "y:", y);
    }

    listenToFormValue(){
      ['af','p','mm'].forEach(v =>{
        this.values[v].$dom.addEventListener('change', function(v, event) {
          switch (v) {
            case 'af':
              this.values[v].value = parseInt(this.values[v].$dom.querySelector('input:checked').value);
              break;
            default:
              this.values[v].value = parseInt(this.values[v].$dom.value);
              break;
          }
          
          console.log(`new ${v}`, this.values[v].value);
          this.updatePos();
        }.bind(this, v));
      })
    }

    updateValuesFromForm(values){
      this.values = values;
      this.updateDomElemt();
    }

    updatePos(){
      console.log('update pos');

      this.updateSceneSize()

      // converte degres to radian, inverse it and rotate it
      let a = parseInt((this.values.mm.value) * -1 + 90) * (Math.PI/180);
      // https://stackoverflow.com/questions/5731863/mapping-a-numeric-range-onto-another
      // from range 0 -> 100 to range 0 -> diagonale de la scene
      // let diagonale = Math.sqrt(Math.pow(this.sceneSize.w, 2) + Math.pow(this.sceneSize.h, 2))/2;
      // let r_max = diagonale;
      // actualy we don't use diagonale, we stay on the circle less 8% padding to get 100 on the external circle
      let r_max = this.sceneSize.w/2 * 0.92; 
      let r_slope = (r_max - 0) / (100 - 0)
      // we inverse p as the more p you have the closest you are from the center
      let r = parseInt(0 + r_slope * ((100 - this.values.p.value) - 0));

      let x = r * Math.cos(a);
      let y = r * Math.sin(a);
      // console.log('x:', x, 'r:', r, 'a:', a, "y:", y);

      let af = this.values.af.value;

      let left = this.sceneSize.w / 2 + x;
      let top = this.sceneSize.h / 2 - (af ? y : -y);
      // console.log('left:', left, "top:", top);

      this.$e.style.left = left+'px';
      this.$e.style.top = top+'px';
    }
    

  }



})(jQuery, Drupal, once);