'use strict';
define(['angular'], function(angular) {
  /**
   * Recuperation du bizedittransform de la map
   * Pour simplification et améliorations (gestion des geom de type geometry)
   * @TODO à factoriser
   */
  var mod = angular.module('gc_featuregeometry_editor', []);

  /**************************************************************************** */
  /* FONCTIONS DE TRANSFORMATION */
  /**************************************************************************** */

  /**********************Fin Fonctions Rotation*************/
  /**********************Homothécie functions ****************/
  /**
   * [changeCoordinatesForHomothetic calculer les nouvelles coordonnées en fonction du rapport homothétique]
   * @param  {Array.<number>} coordP [tableau des coordonnées de l'élément à transformer]
   * @param  {Array.<number>} anchor    [les coordonnées du point anchor (centre de rotation)]
   * @param  {number} factH     [rapport de l'homothécie ]
   * @return {Array.<number>}   [tableau coordPh des nouvelles coordonnées ]
   */
  function calculateHomotheticCoords(coordP, anchor, factH) {
    var coordPh = [];
    for (var j = 0; j < coordP.length; j++) {
      var coord = [];
      //vecteur composé à partir du point anchor et le point p (le point du polygon à modifier)
      var vectAP = [coordP[j][0] - anchor[0], coordP[j][1] - anchor[1]];
      coord[0] = anchor[0] + vectAP[0] * factH;
      coord[1] = anchor[1] + vectAP[1] * factH;
      //stocker tous les coordonnées du polygon, modifiés par le facteur homothetique dans une tableau dest
      coordPh.push(coord);
    }
    return coordPh;
  }

  /**
   * [getHomotheticCoordsByType det les coordonées modifié par l'homothétie en fonction du type de la géometrie de l'objet]
   * @param  {Array.<number>} coords [coordonnées de la geom]
   * @param  {Array.<number>} anchor    [les coordonnées du point anchor (centre de rotation)]
   * @param  {number} factH     [rapport de l'homothécie ]
   * @param  {string} type   [type de la géometrie]
   * @return {Array.<number>}        [tableau des nouvelles coordonnées]
   */
  function getHomotheticCoordsByType(coords, anchor, factH, type) {
    switch (type) {
      case 'Polygon':
        var coordinates = coords[0];
        var newCoords = calculateHomotheticCoords(coordinates, anchor, factH);
        return [newCoords];
        break;
      case 'MultiPolygon':
        console.log(coords);

        var out = [];
        // @TODO faire une vraie recursion ici
        console.log(coords);

        for (var i in coords) {
          if (angular.isArray(coords[i])) {
            out[i] = [];
            for (var j in coords[i]) {
              out[i].push(
                calculateHomotheticCoords(coords[i][j], anchor, factH)
              );
            }
          } else {
            out[i] = calculateHomotheticCoords(coords[i], anchor, factH);
          }
        }
        return out;

        // var coordinates = coords[0][0];
        // var newCoords = calculateHomotheticCoords(coordinates, anchor, factH)
        // return [[newCoords]];
        break;
      case 'LineString':
        var coordinates = coords;
        var newCoords = calculateHomotheticCoords(coordinates, anchor, factH);
        return newCoords;
        break;
      case 'MultiLineString':
        var coordinates = coords[0];
        var newCoords = calculateHomotheticCoords(coordinates, anchor, factH);
        return [newCoords];
        break;
      case 'Point':
        var coordinates = coords[0];
        var newCoords = calculateHomotheticCoords(coordinates, anchor, factH);
        return [newCoords];
        break;
    }
  }

  /**********************Fin Fonctions Homothecie*************/
  /**********************Deformation functions ***************/
  /**
   * [calculateDeformationCoords calculer les nouvelles coordonnées en fonction du rapport déformation]
   * @param  {Array.<number>} coordP [tableau des coordonnées de l'élément à transformer]
   * @param  {Array.<number>} vectAB [vecteur composé de l'anchor et du point représentant le feature de déformation]
   * @param  {number} normAB [norme du vecteur AB]
   * @param  {Array.<number>} anchor [anchor Rotation anchor point]
   * @param  {number} factD  [rapport de déformation]
   * @return {Array.<number>}   [tableau des coordonnées aprés déformation]
   */
  function calculateDeformationCoords(coordP, vectAB, normAB, anchor, factD) {
    var coordPD = [];
    for (var j = 0; j < coordP.length; j++) {
      var coord = [];
      //vecteur composé à partir du point anchor et le point p (le point du polygon à modifier)
      var vectAP = [coordP[j][0] - anchor[0], coordP[j][1] - anchor[1]];
      var vectAPproj1 =
        (vectAB[0] * vectAP[0] + vectAB[1] * vectAP[1]) / normAB;
      var vectAPproj2 =
        (vectAB[0] * vectAP[1] - vectAB[1] * vectAP[0]) / normAB;
      var vectAPproj1Def = vectAPproj1 * factD;
      var vectUxproj1 = vectAB[0] / normAB;
      var vectUxproj2 = vectAB[1] / normAB;
      var APdefx = vectAPproj1Def * vectUxproj1 - vectAPproj2 * vectUxproj2;
      var APdefy = vectAPproj1Def * vectUxproj2 + vectAPproj2 * vectUxproj1;

      coord[0] = anchor[0] + APdefx;
      coord[1] = anchor[1] + APdefy;
      //stocker tous les coordonnées du polygon, modifiés par le facteur de déformation dans une tableau coordPD
      coordPD.push(coord);
    }
    return coordPD;
  }
  /**
   * [getDeformationCoordsByType det les coordonées modifié par la déformation en fonction du type de la géometrie de l'objet]
   * @param  {Array.<number>} coords [coordonnées de la geom]
   * @param  {Array.<number>} vectAB [vecteur composé de l'anchor et du point représentant le feature de déformation]
   * @param  {number} normAB [norme du vecteur AB]
   * @param  {Array.<number>} anchor [les coordonnées du point anchor (centre de rotation)]
   * @param  {number} factD  [rapport de déformation]
   * @param  {string} type   [type de la géometrie]
   * @return {Array.<number>}        [tableau des nouvelles coordonnées]
   */
  function getDeformationCoordsByType(
    coords,
    vectAB,
    normAB,
    anchor,
    factD,
    type
  ) {
    switch (type) {
      case 'Polygon':
        var coordinates = coords[0];
        var newCoords = calculateDeformationCoords(
          coordinates,
          vectAB,
          normAB,
          anchor,
          factD
        );
        return [newCoords];
        break;
      case 'MultiPolygon':
        var out = [];
        // @TODO faire une vraie recursion ici
        for (var i in coords) {
          if (angular.isArray(coords[i])) {
            out[i] = [];
            for (var j in coords[i]) {
              out[i].push(
                calculateDeformationCoords(
                  coords[i][j],
                  vectAB,
                  normAB,
                  anchor,
                  factD
                )
              );
            }
          } else {
            out[i] = calculateDeformationCoords(
              coords[i],
              vectAB,
              normAB,
              anchor,
              factD
            );
          }
        }

        // var coordinates = coords[0][0];
        // var newCoords = calculateDeformationCoords(coordinates, vectAB, normAB, anchor, factD)
        // return [[newCoords]];
        return out;
        break;
      case 'LineString':
        var coordinates = coords;
        var newCoords = calculateDeformationCoords(
          coordinates,
          vectAB,
          normAB,
          anchor,
          factD
        );
        return newCoords;
        break;
      case 'MultiLineString':
        var coordinates = coords[0];
        var newCoords = calculateDeformationCoords(
          coordinates,
          vectAB,
          normAB,
          anchor,
          factD
        );
        return [newCoords];
        break;
      case 'Point':
        var coordinates = coords[0];
        var newCoords = calculateDeformationCoords(
          coordinates,
          vectAB,
          normAB,
          anchor,
          factD
        );
        return [newCoords];
        break;
    }
  }
  /**
   * [geomDeformation mise à jour de la geometrie en fonction des nouveau coordonnées]
   * @param  {Array.<number>} vectAB [vecteur composé de l'anchor et du point représentant le feature de déformation]
   * @param  {number} normAB [norme du vecteur AB]
   * @param  {number} factD  [rapport de déformation]
   * @param  {Array.<number>} anchor [les coordonnées du point anchor (centre de rotation)]
   * @param  {undefined} geom
   * @return {undefined} [geometrie de l'objet aprés la déformation]
   */
  function geomDeformation(vectAB, normAB, factD, anchor, geom) {
    // GeometryCollection
    if (geom.getType() == 'GeometryCollection') {
      var geometries = geom.getGeometries();
      for (var key in geometries) {
        var newCoords = getDeformationCoordsByType(
          geometries[key].getCoordinates(),
          vectAB,
          normAB,
          anchor,
          factD,
          geometries[key].getType()
        );
        geometries[key].setCoordinates(newCoords);
      }
      geom.setGeometries(geometries);
    } else {
      var newCoords = getDeformationCoordsByType(
        geom.getCoordinates(),
        vectAB,
        normAB,
        anchor,
        factD,
        geom.getType()
      );
      geom.setCoordinates(newCoords);
    }
    return geom;
  }
  /*
   * fonction deformation utilisé à chaque point de transformation (Def1, Def2, Def3, Def4)
   * dans cette fonction le vecteur AB change en fonction du point selectionné pour la trasnformation
   */
  function deformation(
    vectAB,
    newCoordinate,
    anchorCoordinate,
    featureBox,
    featureSelection,
    featureRot,
    featureHom1,
    featureHom2,
    featureHom3,
    featureDef1,
    featureDef2,
    featureDef3,
    featureDef4
  ) {
    var normAB = Math.sqrt(vectAB[0] * vectAB[0] + vectAB[1] * vectAB[1]);
    var vectAC = [
      newCoordinate[0] - anchorCoordinate[0],
      newCoordinate[1] - anchorCoordinate[1],
    ];
    var normACh = (vectAB[0] * vectAC[0] + vectAB[1] * vectAC[1]) / normAB;
    var factH = normACh / normAB;

    // calculer les geometries de la box et du feature selectionné par la fonction d'homothécie
    var geomBox = geomDeformation(
      vectAB,
      normAB,
      factH,
      anchorCoordinate,
      featureBox.getGeometry()
    );
    var geomSelect = geomDeformation(
      vectAB,
      normAB,
      factH,
      anchorCoordinate,
      featureSelection.getGeometry()
    );
    //mis à jour des geometries en fonction de celles calculés par l'homothécie
    featureBox.setGeometry(geomBox);
    featureSelection.setGeometry(geomSelect);

    // mise à jour des positions des btn de transformation en fonction de la translation effectué
    updateBtns(
      geomBox,
      featureRot,
      featureHom1,
      featureHom2,
      featureHom3,
      featureDef1,
      featureDef2,
      featureDef3,
      featureDef4
    );
  }

  /**
   * geomHomothetic
   * @param {*} factH
   * @param {*} anchor
   * @param {*} geom
   */
  function geomHomothetic(factH, anchor, geom) {
    // GeometryCollection
    if (geom.getType() == 'GeometryCollection') {
      var geometries = geom.getGeometries();
      for (var key in geometries) {
        var newCoords = getHomotheticCoordsByType(
          geometries[key].getCoordinates(),
          anchor,
          factH,
          geometries[key].getType()
        );
        geometries[key].setCoordinates(newCoords);
      }
      geom.setGeometries(geometries);
    } else {
      var newCoords = getHomotheticCoordsByType(
        geom.getCoordinates(),
        anchor,
        factH,
        geom.getType()
      );
      geom.setCoordinates(newCoords);
    }
    return geom;
  }

  function updateBtns(
    geomBox,
    featRot,
    featHom1,
    featHom2,
    featHom3,
    featDef1,
    featDef2,
    featDef3,
    featDef4
  ) {
    // recupérer les coordonnées (aprés transformation) du premier point du polygon box correspondant au point de rotation
    var coordPoint0Box = geomBox.getCoordinates()[0][0];
    // recupérer les coordonnées (aprés transformation) des différents points de la box correspondant au points homothétiques
    var coordPoint1Box = geomBox.getCoordinates()[0][1];
    var coordPoint2Box = geomBox.getCoordinates()[0][2];
    var coordPoint3Box = geomBox.getCoordinates()[0][3];
    var coordPoint4Box = [
      (coordPoint0Box[0] + coordPoint1Box[0]) / 2,
      (coordPoint0Box[1] + coordPoint1Box[1]) / 2,
    ];
    var coordPoint5Box = [
      (coordPoint1Box[0] + coordPoint2Box[0]) / 2,
      (coordPoint1Box[1] + coordPoint2Box[1]) / 2,
    ];
    var coordPoint6Box = [
      (coordPoint2Box[0] + coordPoint3Box[0]) / 2,
      (coordPoint2Box[1] + coordPoint3Box[1]) / 2,
    ];
    var coordPoint7Box = [
      (coordPoint0Box[0] + coordPoint3Box[0]) / 2,
      (coordPoint0Box[1] + coordPoint3Box[1]) / 2,
    ];
    // mise à jour du btn de rotation
    featRot.getGeometry().setCoordinates(coordPoint0Box);
    // mise à jour des btns homothetic
    featHom1.getGeometry().setCoordinates(coordPoint1Box);
    featHom2.getGeometry().setCoordinates(coordPoint2Box);
    featHom3.getGeometry().setCoordinates(coordPoint3Box);
    featDef1.getGeometry().setCoordinates(coordPoint4Box);
    featDef2.getGeometry().setCoordinates(coordPoint5Box);
    featDef3.getGeometry().setCoordinates(coordPoint6Box);
    featDef4.getGeometry().setCoordinates(coordPoint7Box);
  }

  /*
   * fonction homthetic utilisé à chaque point de transformation (Hom1, Hom2, Hom3)
   * dans cette fonction le vecteur AB change en fonction du point selectionné pour la trasnformation
   */
  function homthetic(
    evt,
    vectAB,
    newCoordinate,
    anchorCoordinate,
    featureBox,
    featureSelection,
    featureRot,
    featureHom1,
    featureHom2,
    featureHom3,
    featureDef1,
    featureDef2,
    featureDef3,
    featureDef4
  ) {
    var normAB = Math.sqrt(vectAB[0] * vectAB[0] + vectAB[1] * vectAB[1]);
    var vectAC = [
      newCoordinate[0] - anchorCoordinate[0],
      newCoordinate[1] - anchorCoordinate[1],
    ];
    var normACh = (vectAB[0] * vectAC[0] + vectAB[1] * vectAC[1]) / normAB;
    var factH = normACh / normAB; // A vérifier // calculer les geometries de la box et du feature selectionné par la fonction d'homothécie
    //A voir
    /*var vectAC2= [firstPointOfEvent[0] - anchorCoordinate[0], firstPointOfEvent[1] - anchorCoordinate[1]];
        var normACh2 = (vectAB[0]*vectAC2[0] + vectAB[1]*vectAC2[1])/normAB;
        var factH2 = Math.abs(Math.round((normAB/normACh2)*100)/100) ;
        fctMeasTransf(evt,featureRot,factH2);*/ var geomBox = geomHomothetic(
      factH,
      anchorCoordinate,
      featureBox.getGeometry()
    );
    console.log(geomBox);

    var geomSelect = geomHomothetic(
      factH,
      anchorCoordinate,
      featureSelection.getGeometry()
    );
    console.log(geomSelect);

    //mis à jour des geometries en fonction de celles calculés par l'homothécie
    featureBox.setGeometry(geomBox);
    featureSelection.setGeometry(geomSelect);
    // mise à jour des positions des btn de transformation en fonction de la translation effectué
    updateBtns(
      geomBox,
      featureRot,
      featureHom1,
      featureHom2,
      featureHom3,
      featureDef1,
      featureDef2,
      featureDef3,
      featureDef4
    );
  }
  function sign(x) {
    // Si x vaut NaN, le résultat vaudra NaN.
    // Si x vaut -0, le résultat vaudra -0.
    // Si x vaut +0, le résultat vaudra +0.
    // Si x est négatif et différent de -0, le résultat vaudra -1.
    // Si x est positif et différent de +0, le résultat vaudra +1.
    x = +x; // on convertit la valeur en un nombre
    if (x === 0 || isNaN(x)) {
      return Number(x);
    }
    return x > 0 ? 1 : -1;
  }
  /**
   * [calculateAng calcul de l'angle à partir de 3 points]
   * @param  {Array.<number>} coordinates [tableau des coordonnées des 3 points ]
   * @return {string}    [angle en degré]
   */
  function calculateAng(coordinates) {
    var radian = 0;
    var rad = 0;
    if (coordinates.length >= 3) {
      var coordA = coordinates[coordinates.length - 3];
      var coordB = coordinates[coordinates.length - 2];
      var coordC = coordinates[coordinates.length - 1];
    }
    var uX = coordA[0] - coordB[0];
    var uY = coordA[1] - coordB[1];
    var vX = coordC[0] - coordB[0];
    var vY = coordC[1] - coordB[1];
    var normU = Math.sqrt(Math.pow(uX, 2) + Math.pow(uY, 2));
    var normV = Math.sqrt(Math.pow(vX, 2) + Math.pow(vY, 2));
    var scalUV = uX * vX + uY * vY;
    var cosAng = scalUV / (normU * normV);
    // radian = Math.sign(uX*vY-uY*vX)*Math.acos(cosAng);
    rad = (sign(uX * vY - uY * vX) * Math.acos(cosAng) * 180) / Math.PI;

    radian = Math.round(rad * 100) / 100;
    // Pour avoir la valeur de l'angle toujours positive
    if (radian < 0) radian = 360 + radian;

    return radian + '°';
  }

  /**
   * [fctMeasTransf fct de definition et placement de l'indicateur de transformation]
   * @param  {[type]} evt
   * @param  {undefined} featureTrans [le feature à transformer]
   * @param  {number} transfParam  [parametre de transformation (angle ou facteur d'homothecie)]
   * @return {undefined}
   */
  var fctMeasTransf = function(evt, featureTrans, transfParam) {
    if (featureTrans) {
      var tooltipCoord = evt.coordinate;
      var output;
      var geom = featureTrans.getGeometry();
      if (geom instanceof ol.geom.Point) {
        output = transfParam;
        tooltipCoord = geom.getLastCoordinate();
      }
      // scope.transTooltipElement.innerHTML = output;
      // scope.transTooltip.setPosition(tooltipCoord);
    }
  };

  /**************************************************************************** */
  /* PROViDER */
  /**************************************************************************** */
  mod.provider('gcFeatureGeometryEditor', function() {
    var baseIcon = new ol.style.Style({
      image: new ol.style.Circle({
        radius: 12,
        stroke: new ol.style.Stroke({
          color: '#abacaf',
        }),
        fill: new ol.style.Fill({
          color: '#fafafa',
        }),
      }),
    });
    var rotateIcon = [
      baseIcon,
      new ol.style.Style({
        image: new ol.style.Icon({
          opacity: 0.75,
          src: 'img/widget/editFeature/rotate.png',
        }),
      }),
    ];

    var homothetieIcon = [
      baseIcon,
      new ol.style.Style({
        image: new ol.style.Icon({
          opacity: 0.75,
          src: 'img/widget/editFeature/resize.png',
        }),
      }),
    ];
    var homothetieIcon2 = [
      baseIcon,
      new ol.style.Style({
        image: new ol.style.Icon({
          opacity: 0.75,
          src: 'img/widget/editFeature/resize.png',
          rotation: (90 * Math.PI) / 180,
        }),
      }),
    ];

    var deformationIcon = [
      baseIcon,
      new ol.style.Style({
        image: new ol.style.Icon({
          opacity: 0.75,
          src: 'img/widget/editFeature/deformation.png',
        }),
      }),
    ];
    var deformationIcon2 = [
      baseIcon,
      new ol.style.Style({
        image: new ol.style.Icon({
          opacity: 0.75,
          src: 'img/widget/editFeature/deformation.png',
          rotation: (90 * Math.PI) / 180,
        }),
      }),
    ];

    this.map = false;
    this.featureConcernee = false;
    this.$get = function() {
      return {
        init: function(map, featureConcernee) {
          this.map = map;
          this.featureConcernee = featureConcernee;

          this.createBox();

          this.createRotationPoint();
          this.createHomotheticPoints();

          this.addCustomInteraction();
        },

        /**
         * Creation de la boite
         */
        createBox: function() {
          // recuperer l'extent du feature selectionné
          var ext = this.featureConcernee.getGeometry().getExtent();
          //créer la geometrie de la box à partir de l'extent
          this.box = new ol.geom.Polygon.fromExtent(ext);
          //créer le feature représentant la box de transformation
          this.featureBox = new ol.Feature({
            geometry: this.box,
            name: 'MyBox',
          });

          // créer la source de la box
          var srcBox = new ol.source.Vector();
          // ajout du feature box à la source
          srcBox.addFeature(this.featureBox);
          // créer une couche vectorielle de la box
          this.layerBox = new ol.layer.Vector({
            source: srcBox,
            style: [
              new ol.style.Style({
                stroke: new ol.style.Stroke({
                  color: '#444',
                  width: 1,
                  lineDash: [4, 8],
                }),
                fill: new ol.style.Fill({
                  color: 'rgba(255,255,255,0.25)',
                }),
              }),
              // points au quatre extremités, utiles ?
              // , new ol.style.Style({
              //     image: new ol.style.Circle({
              //         radius: 1,
              //         stroke: new ol.style.Stroke({
              //             color: 'black'
              //         }), fill: new ol.style.Fill({
              //             color: '#ddd'
              //         })
              //     }),
              //     geometry: function (feature) {
              //         var coordinates = feature.getGeometry().getCoordinates()[0];
              //         return new ol.geom.MultiPoint(coordinates);
              //     }
              // })
            ],
          });
          // pour être sur qu'il est au dessus de la feature concernée sans quoi le forEachFeatureAtPixel
          // de handleDownEvent renvoie parfois la featureConcernee et donc rien ne marche
          this.layerBox.setZIndex(99998);
          // addlayer
          this.map.addLayer(this.layerBox);
        },

        /**
         * Creation du point de rotation
         */
        createRotationPoint: function() {
          // le point de rotation correspond au premier point de la box
          this.poinrtRot = this.box.getCoordinates()[0][0];
          this.featureRot = new ol.Feature({
            geometry: new ol.geom.Point(this.poinrtRot),
            name: 'MyRot',
          });
          var srcRot = new ol.source.Vector();
          srcRot.addFeature(this.featureRot);
          this.layerRot = new ol.layer.Vector({
            source: srcRot,
            style: rotateIcon,
          });
          this.layerRot.setZIndex(99999);
          this.map.addLayer(this.layerRot);
        },

        createHomotheticPoints: function() {
          ///create feature homothetic 1
          ///=> coorespond au deuxième point de la box
          var poinrtHom1 = this.box.getCoordinates()[0][1];
          this.featureHom1 = new ol.Feature({
            geometry: new ol.geom.Point(poinrtHom1),
            name: 'Hom1',
          });
          var srcHom1 = new ol.source.Vector();
          srcHom1.addFeature(this.featureHom1);
          this.layerHom1 = new ol.layer.Vector({
            source: srcHom1,
            style: homothetieIcon2,
          });
          this.layerHom1.setZIndex(99999);
          this.map.addLayer(this.layerHom1);

          ///create feature homothetic 2
          ///=> coorespond au troisième point de la box
          var poinrtHom2 = this.box.getCoordinates()[0][2];
          this.featureHom2 = new ol.Feature({
            geometry: new ol.geom.Point(poinrtHom2),
            name: 'Hom2',
          });
          var srcHom2 = new ol.source.Vector();
          srcHom2.addFeature(this.featureHom2);
          this.layerHom2 = new ol.layer.Vector({
            source: srcHom2,
            style: homothetieIcon,
          });
          this.layerHom2.setZIndex(99999);
          this.map.addLayer(this.layerHom2);

          ///create feature homothetic 3
          ///=> coorespond au quatrième point de la box
          var poinrtHom3 = this.box.getCoordinates()[0][3];
          this.featureHom3 = new ol.Feature({
            geometry: new ol.geom.Point(poinrtHom3),
            name: 'Hom3',
          });
          var srcHom3 = new ol.source.Vector();
          srcHom3.addFeature(this.featureHom3);
          this.layerHom3 = new ol.layer.Vector({
            source: srcHom3,
            style: homothetieIcon2,
          });
          this.layerHom3.setZIndex(99999);
          this.map.addLayer(this.layerHom3);

          ///create feature deformation 1
          ///=> coorespond au point au milieu de la droite composée par le premier (pointde rotation) et le deuxième point de la box
          var coordPointDef1X = (this.poinrtRot[0] + poinrtHom1[0]) / 2;
          var coordPointDef1Y = (this.poinrtRot[1] + poinrtHom1[1]) / 2;
          var poinrtDef1 = [coordPointDef1X, coordPointDef1Y];
          this.featureDef1 = new ol.Feature({
            geometry: new ol.geom.Point(poinrtDef1),
            name: 'Def1',
          });
          var srcDef1 = new ol.source.Vector();
          srcDef1.addFeature(this.featureDef1);
          this.layerDef1 = new ol.layer.Vector({
            source: srcDef1,
            style: deformationIcon,
          });
          this.layerDef1.setZIndex(99999);
          this.map.addLayer(this.layerDef1);

          ///create feature deformation 2
          ///=> coorespond au point au milieu de la droite composée par le deuxième et le troisième point de la box
          var coordPointDef2X = (poinrtHom1[0] + poinrtHom2[0]) / 2;
          var coordPointDef2Y = (poinrtHom1[1] + poinrtHom2[1]) / 2;
          var poinrtDef2 = [coordPointDef2X, coordPointDef2Y];
          this.featureDef2 = new ol.Feature({
            geometry: new ol.geom.Point(poinrtDef2),
            name: 'Def2',
          });
          var srcDef2 = new ol.source.Vector();
          srcDef2.addFeature(this.featureDef2);
          this.layerDef2 = new ol.layer.Vector({
            source: srcDef2,
            style: deformationIcon2,
          });
          this.layerDef2.setZIndex(99999);
          this.map.addLayer(this.layerDef2);

          ///create feature deformation 3
          ///=> coorespond au point au milieu de la droite composée par le troisième et le quatrième point de la box
          var coordPointDef3X = (poinrtHom2[0] + poinrtHom3[0]) / 2;
          var coordPointDef3Y = (poinrtHom2[1] + poinrtHom3[1]) / 2;
          var poinrtDef3 = [coordPointDef3X, coordPointDef3Y];
          this.featureDef3 = new ol.Feature({
            geometry: new ol.geom.Point(poinrtDef3),
            name: 'Def3',
          });
          var srcDef3 = new ol.source.Vector();
          srcDef3.addFeature(this.featureDef3);
          this.layerDef3 = new ol.layer.Vector({
            source: srcDef3,
            style: deformationIcon,
          });
          this.layerDef3.setZIndex(99999);
          this.map.addLayer(this.layerDef3);

          ///create feature deformation 4
          ///=> coorespond au point au milieu de la droite composée par le premier et le quatrième point de la box
          var coordPointDef4X = (this.poinrtRot[0] + poinrtHom3[0]) / 2;
          var coordPointDef4Y = (this.poinrtRot[1] + poinrtHom3[1]) / 2;
          var poinrtDef4 = [coordPointDef4X, coordPointDef4Y];
          this.featureDef4 = new ol.Feature({
            geometry: new ol.geom.Point(poinrtDef4),
            name: 'Def4',
          });
          var srcDef4 = new ol.source.Vector();
          srcDef4.addFeature(this.featureDef4);
          this.layerDef4 = new ol.layer.Vector({
            source: srcDef4,
            style: deformationIcon2,
          });
          this.layerBox.setZIndex(99999);
          this.map.addLayer(this.layerDef4);
        },

        addCustomInteraction: function() {
          var that = this;

          /**
           * Define a namespace for the application.
           */
          var app = {};

          /**
           * @constructor
           * @extends {ol.interaction.Pointer}
           */
          app.Drag = function() {
            ol.interaction.Pointer.call(this, {
              handleDownEvent: app.Drag.prototype.handleDownEvent,
              handleDragEvent: app.Drag.prototype.handleDragEvent,
              handleMoveEvent: app.Drag.prototype.handleMoveEvent,
              handleUpEvent: app.Drag.prototype.handleUpEvent,
            });
            this.lastCoordinate_ = null;
            this.cursor_ = 'pointer';

            this.features = {
              box: that.featureBox,
              selection: that.featureConcernee,
              rotation: that.featureRot,
              hom1: that.featureHom1,
              hom2: that.featureHom2,
              hom3: that.featureHom3,
              def1: that.featureDef1,
              def2: that.featureDef2,
              def3: that.featureDef3,
              def4: that.featureDef4,
            };
            this.previousCursor_ = undefined;
            this.action = undefined;
          };
          ol.inherits(app.Drag, ol.interaction.Pointer);
          app.Drag.prototype.handleDownEvent = function(evt) {
            try {
              var feature = evt.map.forEachFeatureAtPixel(evt.pixel, function(
                f
              ) {
                console.log(f);
                return f;
              });

              if (feature.getId() == that.featureConcernee.getId()) {
                console.log(
                  '%c featureConcernee retournee',
                  'background: #f00; color: #fff'
                );
                return;
              }

              //console.log(map.getTarget());
              if (feature) {
                console.log(feature.getProperties());
                console.log(feature.getProperties().name);

                // definir l'action de transformation en fonction du feature selectionné
                if (feature.getProperties().name === 'MyRot') {
                  this.action = 'rotation';
                  this.lastCoordinate_ = evt.coordinate;
                } else if (feature.getProperties().name === 'Hom1') {
                  this.action = 'homothetic1';

                  this.lastCoordinate_ = evt.coordinate;
                } else if (feature.getProperties().name === 'Hom2') {
                  this.action = 'homothetic2';

                  this.lastCoordinate_ = evt.coordinate;
                } else if (feature.getProperties().name === 'Hom3') {
                  this.action = 'homothetic3';

                  this.lastCoordinate_ = evt.coordinate;
                } else if (feature.getProperties().name === 'Def1') {
                  this.action = 'deformation1';

                  this.lastCoordinate_ = evt.coordinate;
                } else if (feature.getProperties().name === 'Def2') {
                  this.action = 'deformation2';

                  this.lastCoordinate_ = evt.coordinate;
                } else if (feature.getProperties().name === 'Def3') {
                  this.action = 'deformation3';

                  this.lastCoordinate_ = evt.coordinate;
                } else if (feature.getProperties().name === 'Def4') {
                  this.action = 'deformation4';

                  this.lastCoordinate_ = evt.coordinate;
                } else if (feature.getProperties().name === 'MyBox') {
                  this.action = 'translation';
                  this.lastCoordinate_ = evt.coordinate;
                }
              }
              return !!feature;
            } catch (e) {}
          };

          var firstPointOfEvent;
          /**
           * @param {ol.MapBrowserEvent} evt Map browser event.
           */
          app.Drag.prototype.handleDragEvent = function(evt) {
            //if ( !firstPointOfEvent ) firstPointOfEvent = this.lastCoordinate_;
            // Recupération de la geom de la box pour calculer l'anchor (centre de rotation)
            var geom = this.features.box.getGeometry();
            // console.log(geom);

            var anchorCoordinate = new ol.extent.getCenter(
              this.features.box.getGeometry().getExtent()
            );
            var newCoordinate = evt.coordinate;
            // appliquer la rotation si point rotation est selectionné
            //console.log(this.action);

            if (this.lastCoordinate_ && this.action == 'rotation') {
              if (!firstPointOfEvent) firstPointOfEvent = this.lastCoordinate_;
              // calculate vectors of last and current pointer positions
              var lastVector = [
                this.lastCoordinate_[0] - anchorCoordinate[0],
                this.lastCoordinate_[1] - anchorCoordinate[1],
              ];
              var newVector = [
                newCoordinate[0] - anchorCoordinate[0],
                newCoordinate[1] - anchorCoordinate[1],
              ];
              // calculate angle between last and current vectors (positive angle counter-clockwise)
              var angle = Math.atan2(
                lastVector[0] * newVector[1] - newVector[0] * lastVector[1],
                lastVector[0] * newVector[0] + lastVector[1] * newVector[1]
              );
              // calculer l'angle d'affichage de la rotation (droite de référence composé entre l'anchor et le premier point de l'evt)
              var angleDisplay = calculateAng([
                firstPointOfEvent,
                anchorCoordinate,
                newCoordinate,
              ]);

              // calculer les geometries de la box et du feature selectionné parla fonction de rotation
              /* var geomBox = rotatekis(angle,anchorCoordinate ,this.features.box.getGeometry());
                             var geomSelect = rotatekis(angle,anchorCoordinate ,this.features.selection.getGeometry());*/
              //var geomBox = rotatekis(angle,anchorCoordinate ,this.features.box.getGeometry());
              //var geomSelect = rotatekis(angle,anchorCoordinate ,this.features.selection.getGeometry());

              //var geom3 = rotatekis(angle,anchorCoordinate ,_this4.features.rotation.getGeometry());
              //mis à jour des geometries en fonction de celles calculés par la rotation
              //this.features.box.setGeometry(geomBox);
              //this.features.selection.setGeometry(geomSelect);

              ///// changement rotate function dans la version 3.18.2 (plus besoin de determiner les flat coordinate par :geom.j)
              ///la fonction rotate est en mode experimentale
              var geomBox = this.features.box.getGeometry();
              geomBox.rotate(angle, anchorCoordinate);
              var geomSelect = this.features.selection.getGeometry();
              geomSelect.rotate(angle, anchorCoordinate);
              updateBtns(
                geomBox,
                this.features.rotation,
                this.features.hom1,
                this.features.hom2,
                this.features.hom3,
                this.features.def1,
                this.features.def2,
                this.features.def3,
                this.features.def4
              );

              fctMeasTransf(evt, this.features.rotation, angleDisplay);

              //console.log('styleRot',this.features.rotation.getStyle());
              this.lastCoordinate_ = evt.coordinate;
            }
            //appliquer l'homothecie par le point Hom1
            if (this.lastCoordinate_ && this.action == 'homothetic1') {
              var coordHom1 = this.features.hom1.getGeometry().getCoordinates();
              if (!firstPointOfEvent) firstPointOfEvent = coordHom1;
              var vectAB = [
                coordHom1[0] - anchorCoordinate[0],
                coordHom1[1] - anchorCoordinate[1],
              ];

              homthetic(
                evt,
                vectAB,
                newCoordinate,
                anchorCoordinate,
                this.features.box,
                this.features.selection,
                this.features.rotation,
                this.features.hom1,
                this.features.hom2,
                this.features.hom3,
                this.features.def1,
                this.features.def2,
                this.features.def3,
                this.features.def4
              );

              this.lastCoordinate_ = evt.coordinate;
            }
            //appliquer l'homothecie par le point  Hom2
            if (this.lastCoordinate_ && this.action == 'homothetic2') {
              var coordHom2 = this.features.hom2.getGeometry().getCoordinates();
              if (!firstPointOfEvent) firstPointOfEvent = coordHom2;
              var vectAB = [
                coordHom2[0] - anchorCoordinate[0],
                coordHom2[1] - anchorCoordinate[1],
              ];
              //var vectAB = [this.lastCoordinate_[0] - anchorCoordinate[0], this.lastCoordinate_[1] - anchorCoordinate[1]];

              homthetic(
                evt,
                vectAB,
                newCoordinate,
                anchorCoordinate,
                this.features.box,
                this.features.selection,
                this.features.rotation,
                this.features.hom1,
                this.features.hom2,
                this.features.hom3,
                this.features.def1,
                this.features.def2,
                this.features.def3,
                this.features.def4
              );

              this.lastCoordinate_ = evt.coordinate;
            }
            //appliquer l'homothecie par le point  Hom3
            if (this.lastCoordinate_ && this.action == 'homothetic3') {
              var coordHom3 = this.features.hom3.getGeometry().getCoordinates();
              if (!firstPointOfEvent) firstPointOfEvent = coordHom3;
              var vectAB = [
                coordHom3[0] - anchorCoordinate[0],
                coordHom3[1] - anchorCoordinate[1],
              ];

              homthetic(
                evt,
                vectAB,
                newCoordinate,
                anchorCoordinate,
                this.features.box,
                this.features.selection,
                this.features.rotation,
                this.features.hom1,
                this.features.hom2,
                this.features.hom3,
                this.features.def1,
                this.features.def2,
                this.features.def3,
                this.features.def4
              );

              this.lastCoordinate_ = evt.coordinate;
            }

            //appliquer la deformation par le point  Def1
            if (this.lastCoordinate_ && this.action == 'deformation1') {
              var coordDef1 = this.features.def1.getGeometry().getCoordinates();
              var vectAB = [
                coordDef1[0] - anchorCoordinate[0],
                coordDef1[1] - anchorCoordinate[1],
              ];
              //var vectAB = [this.lastCoordinate_[0] - anchorCoordinate[0], this.lastCoordinate_[1] - anchorCoordinate[1]];

              deformation(
                vectAB,
                newCoordinate,
                anchorCoordinate,
                this.features.box,
                this.features.selection,
                this.features.rotation,
                this.features.hom1,
                this.features.hom2,
                this.features.hom3,
                this.features.def1,
                this.features.def2,
                this.features.def3,
                this.features.def4
              );

              this.lastCoordinate_ = evt.coordinate;
            }

            //appliquer la deformation par le point  Def2
            if (this.lastCoordinate_ && this.action == 'deformation2') {
              var coordDef2 = this.features.def2.getGeometry().getCoordinates();
              var vectAB = [
                coordDef2[0] - anchorCoordinate[0],
                coordDef2[1] - anchorCoordinate[1],
              ];
              //var vectAB = [this.lastCoordinate_[0] - anchorCoordinate[0], this.lastCoordinate_[1] - anchorCoordinate[1]];

              deformation(
                vectAB,
                newCoordinate,
                anchorCoordinate,
                this.features.box,
                this.features.selection,
                this.features.rotation,
                this.features.hom1,
                this.features.hom2,
                this.features.hom3,
                this.features.def1,
                this.features.def2,
                this.features.def3,
                this.features.def4
              );

              this.lastCoordinate_ = evt.coordinate;
            }

            //appliquer la deformation par le point  Def3
            if (this.lastCoordinate_ && this.action == 'deformation3') {
              var coordDef3 = this.features.def3.getGeometry().getCoordinates();
              var vectAB = [
                coordDef3[0] - anchorCoordinate[0],
                coordDef3[1] - anchorCoordinate[1],
              ];
              //var vectAB = [this.lastCoordinate_[0] - anchorCoordinate[0], this.lastCoordinate_[1] - anchorCoordinate[1]];

              deformation(
                vectAB,
                newCoordinate,
                anchorCoordinate,
                this.features.box,
                this.features.selection,
                this.features.rotation,
                this.features.hom1,
                this.features.hom2,
                this.features.hom3,
                this.features.def1,
                this.features.def2,
                this.features.def3,
                this.features.def4
              );

              this.lastCoordinate_ = evt.coordinate;
            }

            //appliquer la deformation par le point  Def4
            if (this.lastCoordinate_ && this.action == 'deformation4') {
              var coordDef4 = this.features.def4.getGeometry().getCoordinates();
              var vectAB = [
                coordDef4[0] - anchorCoordinate[0],
                coordDef4[1] - anchorCoordinate[1],
              ];
              //var vectAB = [this.lastCoordinate_[0] - anchorCoordinate[0], this.lastCoordinate_[1] - anchorCoordinate[1]];

              deformation(
                vectAB,
                newCoordinate,
                anchorCoordinate,
                this.features.box,
                this.features.selection,
                this.features.rotation,
                this.features.hom1,
                this.features.hom2,
                this.features.hom3,
                this.features.def1,
                this.features.def2,
                this.features.def3,
                this.features.def4
              );

              this.lastCoordinate_ = evt.coordinate;
            }

            //appliquer la rotation si la box est selectionné
            if (this.lastCoordinate_ && this.action == 'translation') {
              //calcul des distances dernières coordonnées et les coordonnées courantes du curseur
              var deltaX = evt.coordinate[0] - this.lastCoordinate_[0];
              var deltaY = evt.coordinate[1] - this.lastCoordinate_[1];
              // mise à jour de la box geom en fonction des distances calculées
              var geomBox = this.features.box.getGeometry();
              geomBox.translate(deltaX, deltaY);
              // mise à jour de la geom du feature à transformer en fonction des distances calculées
              var geomSelect = this.features.selection.getGeometry();
              geomSelect.translate(deltaX, deltaY);
              // mise à jour des positions des btn de transformation en fonction de la translation effectué
              updateBtns(
                geomBox,
                this.features.rotation,
                this.features.hom1,
                this.features.hom2,
                this.features.hom3,
                this.features.def1,
                this.features.def2,
                this.features.def3,
                this.features.def4
              );

              this.lastCoordinate_[0] = evt.coordinate[0];
              this.lastCoordinate_[1] = evt.coordinate[1];
            }
          };

          // création d'une interaction de transformation
          this.interactionTransform = new app.Drag();
          this.map.addInteraction(this.interactionTransform);
        },

        /**
         * resetBoxAndHandles
         */
        resetBoxAndHandles: function(map) {
          if (!angular.isDefined(this.map)) this.map = map;

          this.map.removeLayer(this.layerBox);
          this.map.removeLayer(this.layerRot);
          this.map.removeLayer(this.layerHom1);
          this.map.removeLayer(this.layerHom2);
          this.map.removeLayer(this.layerHom3);
          this.map.removeLayer(this.layerDef1);
          this.map.removeLayer(this.layerDef2);
          this.map.removeLayer(this.layerDef3);
          this.map.removeLayer(this.layerDef4);

          if (angular.isDefined(this.interactionTransform))
            this.map.removeInteraction(this.interactionTransform);
        },
      };
    };
  });

  return mod;
});
