'use strict';
define(function() {
  var pv2Annotations = function(
    $translate,
    $location,
    $filter,
    BaseMapFactory,
    maStyle,
    gclayers,
    $rootScope
  ) {
    let scopePolygoneBulle;

    function annotTextFrame(feat, scale) {
      var ii, geom, x, y, x0, y0;
      var newFeat, sc, nbLignes;
      var coords = [];

      var properties = feat.getProperties();
      var textValue;
      //if (properties.styleGraphic){
      //  textValue = properties.styleGraphic.textValue;
      //}else{
      textValue = feat.getProperties().styleTemplate.text;
      //}

      // Récupération de l'élément d'id unique
      var container = document.getElementById(
        properties.styleTemplate.containerId
      );

      // Récupération des propriétés longueur / largeur pour son échelle
      var actualHeight = parseInt($(container).css('height')),
        actualWidth = parseInt($(container).css('width')),
        actualScale = $rootScope.xgos.scale4Representation;

      // Calcul des valeurs en px pour l'échelle d'impression
      var scaledHeight = (actualHeight * actualScale) / scale,
        scaledWidth = (actualWidth * actualScale) / scale;

      // Conversion des px en cm on admet que 1 pixel = (2.54 / 96)
      var actualCm = (scaledWidth * 2.54) / 96;
      var actualHCm = (scaledHeight * 2.54) / 96;

      // Positionnement de l'accroche en [0,0] [scale(-0.5),scale(0.5)]
      var scalingAccroche = (0.1 * actualCm) / ((actualWidth * 2.54) / 96);
      if (textValue.indexOf('<br>') !== -1) {
        var ttt = textValue.split('<br>');
        nbLignes = ttt.length;
      } else nbLignes = textValue.length / 60;

      var mg = 0;
      if (nbLignes > 1) mg = 0.3;
      //-- Préparation d'une bulle en coordonnées [0.0,0.0]
      //-- Dessin du début de l'accroche au point \/
      coords.push([0, -mg]);
      coords.push([-scalingAccroche, scalingAccroche - mg]);

      //-- Dessin du coin bas gauche
      x0 = -(actualCm / 2);
      y0 = 2 * scalingAccroche;
      if (nbLignes < 1) nbLignes = 1;
      for (ii = 1.07 + Math.PI; ii > Math.PI; ii -= 0.2) {
        x = x0 + scalingAccroche * Math.cos(ii);
        y = y0 + scalingAccroche * Math.sin(ii) - mg;
        coords.push([x, y]);
      }
      //-- Dessin du coin haut gauche
      x0 = -(actualCm / 2);
      y0 = actualHCm + scalingAccroche / nbLignes;
      for (ii = Math.PI; ii > Math.PI / 2.0; ii -= 0.2) {
        x = x0 + scalingAccroche * Math.cos(ii);
        y = y0 + scalingAccroche * Math.sin(ii);
        coords.push([x, y]);
      }
      //-- Dessin du coin haut droit
      x0 = actualCm / 2;
      y0 = actualHCm + scalingAccroche / nbLignes;
      for (ii = Math.PI / 2.0; ii > 0.0; ii -= 0.2) {
        x = x0 + scalingAccroche * Math.cos(ii);
        y = y0 + scalingAccroche * Math.sin(ii);
        coords.push([x, y]);
      }
      //-- Dessin du coin bas droit
      x0 = actualCm / 2;
      y0 = 2 * scalingAccroche;
      for (ii = Math.PI + Math.PI; ii > (3.0 * Math.PI) / 2.0; ii -= 0.2) {
        x = x0 + scalingAccroche * Math.cos(ii);
        y = y0 + scalingAccroche * Math.sin(ii) - mg;
        coords.push([x, y]);
      }
      //-- Dessin de la fin de l'accroche au point \/
      coords.push([scalingAccroche, scalingAccroche - mg]);
      coords.push([0, -mg]);

      //-- Placement de la bulle au point de positionnement.
      geom = feat.getGeometry().getCoordinates();
      x = geom[0];
      y = geom[1];
      for (ii = 0; ii < coords.length; ii++) {
        coords[ii][0] += x;
        coords[ii][1] += y;
      }
      var polygon = new ol.geom.Polygon([coords]);
      //-- Mise à l'échelle de la bulle.
      sc = scale / 100;
      polygon.scale(sc, sc, geom);
      //-- Création du polygone de la bulle.
      newFeat = new ol.Feature({
        geometry: polygon,
        name: 'Polygon',
        featureType: 'Polygon',
      });

      scopePolygoneBulle = newFeat;

      return newFeat;
    }

    /**
     *  Préparation du style du polygone et du texte
     *  pour l'annotation écrite dans une bulle.
     */
    function getTextStyle(feat, scale) {
      var properties = feat.getProperties();
      var textValue, colorFill, colorStroke;
      textValue = feat.getProperties().styleTemplate.text;
      if (feat.getProperties().styleGraphic) {
        // colorFill = feat.getProperties().styleGraphic.Fill.CssParameter[0].__text;
        colorFill = '#000000';
        //colorStroke = feat.getProperties().styleGraphic.Stroke.CssParameter[0].__text;
        colorStroke = '#000000';
      } else {
        colorFill = feat.getProperties().styleTemplate.textColor;
        colorStroke = feat.getProperties().styleTemplate.textColor;
      }

      // // Récupération de l'élément d'id unique
      var container = document.getElementById(
        properties.styleTemplate.containerId
      );

      // Récupération de la taille de la font pour scale
      var actualFontSize = parseInt($(container).css('font-size')),
        actualWidth = parseInt($(container).css('width')),
        actualScale = $rootScope.xgos.scale4Representation;

      // Calcul des valeurs en px pour l'échelle d'impression
      ///!\ 0.8 is totally arbitrary, scale is not good on font-size, need to set it to 80% of calculated value
      var scaledFontSize = ((actualFontSize * actualScale) / scale) * 0.8,
        scaledWidth = (actualWidth * actualScale) / scale,
        actualCm = (scaledWidth * 2.54) / 96;

      var nbLignes = Math.ceil(textValue.length / 60);
      var scalingAccroche = (0.1 * actualCm) / ((actualWidth * 2.54) / 96);

      var newText;
      //Si le scale est inférieur à 2px, supprime le texte
      scaledFontSize < 2 ? (newText = null) : (newText = textValue);

      let geom = feat.getGeometry().getCoordinates();

      if (newText != null) {
        for (var i = nbLignes; i > 0; i--) {
          var position = i * 60;
          newText =
            newText.substr(0, position) + '\n' + newText.substr(position);
        }
        newText = newText.replace('<br>', '\n');
        var ttt = newText.split('\n');
        nbLignes = ttt.length;
      }

      var polygoneBulle = scopePolygoneBulle.getGeometry().getExtent();

      var posText = [];
      posText[0] = geom[0];
      posText[1] =
        polygoneBulle[3] - (2 * scalingAccroche + scalingAccroche * nbLignes);

      if (!$scope.projImpEqualsProjDisplay()) {
        posText = proj4($scope.olProjection, $scope.secondProjection, posText);
      }

      var style_01 = new ol.style.Style({
        fill: new ol.style.Fill({
          color: hexToRGBA(
            feat.getProperties().styleTemplate.backgroundColor,
            feat.getProperties().styleTemplate / 100.0
          ),
        }),
        stroke: new ol.style.Stroke({
          width: 0.3,
          color: 'rgba(150, 150, 150, 1.0)',
        }),
        text: new ol.style.Text({
          //font: 'normal '+scaledFontSize+'px opensans,sans-serif',
          font: 'normal ' + scaledFontSize + 'px opensans,sans-serif',
          textAlign: 'center',
          textBaseline: 'top',
          overflow: true,
          placement: 'line',
          fill: new ol.style.Fill({ color: hexToRGBA(colorFill, 1.0) }),
          stroke: new ol.style.Stroke({
            color: hexToRGBA(colorStroke, 1.0),
            width: 2,
          }),
          // get the text from the feature - `this` is ol.Feature
          // and show only under certain resolution
          text: newText,
          offsetX: posText[0],
          offsetY: posText[1],
        }),
      });

      return style_01;
    }

    /**
     *     Préparation du style pour un symbole de type image.
     */
    function getSymbolStyle(feat, scale) {
      var imgStyleOptions = {};
      var imgStyle, anchor;

      if (
        feat.getProperties().styleTemplate.image.src.substr(0, 11) ==
        '/geoserver/'
      )
        imgStyleOptions.src =
          location.origin + feat.getProperties().styleTemplate.image.src;
      else
        imgStyleOptions.src =
          location.origin +
          location.pathname +
          feat.getProperties().styleTemplate.image.src;
      imgStyleOptions.size = feat
        .getStyle()
        .getImage()
        .getSize();
      anchor = feat
        .getStyle()
        .getImage()
        .getAnchor();
      if (anchor != null)
        imgStyleOptions.anchor = [
          anchor[0] / imgStyleOptions.size[0],
          anchor[1] / imgStyleOptions.size[1],
        ];
      imgStyle = new ol.style.Icon(imgStyleOptions);

      imgStyle.setRotation(
        (feat
          .getStyle()
          .getImage()
          .getRotation() *
          180) /
          Math.PI
      );
      // imgStyle.setScale(feat.getProperties().styleTemplate.image.scale)

      var ratio;
      scale > feat.getProperties().styleTemplate.scale100
        ? (ratio = feat.getProperties().styleTemplate.scale100 / scale)
        : (ratio = scale / feat.getProperties().styleTemplate.scale100);
      imgStyle.setScale(feat.getProperties().styleTemplate.image.scale * ratio);

      return new ol.style.Style({ image: imgStyle });
    }

    function hexToRGBA(hex, opacity) {
      return (
        'rgba(' +
        (hex = hex.replace('#', ''))
          .match(new RegExp('(.{' + hex.length / 3 + '})', 'g'))
          .map(function(l) {
            return parseInt(hex.length % 2 ? l + l : l, 16);
          })
          .concat(opacity || 1)
          .join(',') +
        ')'
      );
    }

    function removeAnnotationLayers(layers) {
      var layerHasBeenRemoved = true;

      while (layerHasBeenRemoved) {
        layerHasBeenRemoved = false;
        layers.forEach(function(layer) {
          if (layer.name == 'Edition') {
            layers.remove(layer);
            layerHasBeenRemoved = true;
          }
        });
      }
    }

    /**
     *     Création du style pour les vrais annotations.
     *  Attention: avec MapFishPrint V2 pas de halo possible
     *  autour des caractères du texte.
     */
    function getLabelStyle(feat, scale) {
      var styleProp = feat.getProperties().styleTemplate;
      var al, bl, fontSize, scaledFontSize;
      var fontProps = '';
      var textValue, colorFill, colorStroke, rotation;
      var sg = feat.getProperties().styleGraphic;
      if (sg) {
        fontSize = 16;
        //fontProps = feat.getProperties().styleGraphic.textValue;
        fontProps = ' sans-serif';
        textValue = sg.textValue;
        colorFill = sg.Fill.CssParameter[0].__text;
        colorStroke = sg.Stroke.CssParameter[0].__text;
        rotation = sg.Rotation;
      } else {
        styleProp.text.font.split(' ').forEach(function(word) {
          if (word.endsWith('px'))
            fontSize = word.substring(0, word.length - 2);
          else fontProps += ' ' + word;
        });
        textValue = styleProp.text.text;
        colorFill = styleProp.text.fill.color;
        colorStroke = styleProp.text.stroke.color;
        rotation = styleProp.text.rotation;
      }

      var ratio;
      if (sg && sg.fixedSize) ratio = 1.0;
      else
        /* scale > styleProp.scale100
          ? (ratio = styleProp.scale100 / scale)
          : (ratio = scale / styleProp.scale100); */
        ratio = scale / styleProp.scale100;
      const template = maStyle.getDefaultTemplate('Label');
      const elts = template.text.font.split(' ');
      const size = parseInt(elts[0]) / ratio / 2;
      elts[0] = size.toFixed() + 'px';

      scaledFontSize = parseInt(fontSize * ratio) + 'px';

      //-- 17/06/2019 parseInt(fontSize * ratio) < 5 ? text = null : text = textValue;

      al = feat
        .getStyle()
        .getText()
        .getTextAlign();
      if (al == undefined) al = 'center';
      bl = feat
        .getStyle()
        .getText()
        .getTextBaseline();
      if (bl == undefined) bl = 'middle';
      var style_01 = new ol.style.Style({
        text: new ol.style.Text({
          //offsetX: -3*scale/100,
          rotation: rotation, // styleProp.text.rotation,
          font: scaledFontSize + fontProps,
          textAlign: al,
          textBaseline: bl,
          fill: new ol.style.Fill({
            color: hexToRGBA(colorFill, 1.0),
          }),
          // fill: new ol.style.Fill({ color: '#000' }),
          stroke: new ol.style.Stroke({
            color: hexToRGBA(colorStroke, 1.0),
            width: 5,
          }),
          // get the text from the feature - `this` is ol.Feature
          // and show only under certain resolution
          text: textValue,
        }),
      });
      style_01.getText().setFont(elts.join(' '));

      return style_01;
    }

    function getTextStyleForTip(anno, scale) {
      //-- Il faut cloner le style, sinon on va modifier
      //-- celui du dessin sur la carte et alors là:
      //-- bonjour les effets de bord.
      var style = anno.vectorText.getStyle().clone();
      var template = maStyle.getDefaultTemplate('Label');
      var ratio;
      if (anno.properties.styleGraphic.fixedSize) {
        ratio = 1.0;
      } else ratio = scale / anno.properties.styleTemplate.scale100;
      var elts = template.text.font.split(' ');
      var size = parseInt(elts[0]) / ratio / 2;
      elts[0] = size.toFixed() + 'px';
      style.getText().setFont(elts.join(' '));
      var lines = style
        .getText()
        .getText()
        .split('\n');
      if (lines.length == 1) style.getText().setOffsetY(-size / 4);
      else style.getText().setOffsetY(lines.length * size - size / 1.5);
      return style;
    }

    function checkLineStyle(vectorLayer, scale) {
      if (vectorLayer.getStyle()()[0].getStroke) {
        //-- Epaisseur du trait.
        let factor = $rootScope.xgos.scale4Representation / scale;
        //-- Angule copy pour ne pas modifier le style du dessin à l'écran.
        let theStyle = angular.copy(vectorLayer.getStyle()()[0]);
        theStyle.getStroke().setWidth(theStyle.getStroke().getWidth() * factor);

        //-- Longueur tiret et espacement.
        let ld = theStyle.getStroke().getLineDash();
        if (ld != null) {
          //-- Le facteur 0.8 est un ajustement pour que la longueur des tirets
          //-- et des espacement correspondent à ce que l'on a sur l'écran
          //-- quand on imprime.
          factor *= 0.8;
          for (let ind = 0; ind < ld.length; ind++) {
            ld[ind] *= factor;
          }
        }
        vectorLayer.setStyle(theStyle);
      }
    }

    /**
     *     Dessin de tout sauf ses annotations de type bulle
     * car elles doivent être par dessus tout comme sur la carte.
     *
     * @param {[[type]]} src [[Description]]
     * @param {[[type]]} ext [[Description]]
     */
    function drawAnnotExceptTipLabel(src, ext, scale, layers) {
      var vectorLayer, vectorSource;

      src.forEachFeatureInExtent(ext, function(feat) {
        vectorSource = new ol.source.Vector({});
        vectorLayer = new ol.layer.Vector({
          source: vectorSource,
        });
        //-- Permet d'identifier les layers annotation
        vectorLayer.name = 'Edition';

        if (feat.get('visible')) {
          var zoomratio = 1.0;
          var props = feat.getProperties();
          var template = props.styleTemplate;
          if (props.styleGraphic && props.styleGraphic.fixedSize)
            zoomratio = 1.0;
          else zoomratio = template.scale100 / scale;
          vectorSource.addFeature(feat);
          var isTiplabel =
            props.featureType == 'Polygon' &&
            props.styleGraphic.text == 'Label';
          if (props.featureType == 'Symbol') {
            vectorLayer.setStyle(getSymbolStyle(feat, scale));
          } else if (props.featureType == 'Point' && props.styleGraphic) {
            var type = props.featureType;
            var annGrph = props.styleGraphic;
            if ('SYMBOL' === annGrph.styleMode && annGrph.Mark)
              vectorLayer.setStyle(
                maStyle.getSymbolStyleMarker(scale, feat, annGrph, zoomratio)
              );
            var style = maStyle.getAnnotationStyle(
              type,
              props.styleGraphic,
              zoomratio
            );
            if (style.getImage() instanceof ol.style.Icon) {
              //ajustement pour impression des symboles images
              var ratio = zoomratio * 0.55;
              style
                .getImage()
                .setScale(parseFloat(props.styleGraphic.Size) * ratio);
            }
            if (
              props.featureType == 'Point' &&
              props.styleGraphic &&
              props.styleGraphic.Mark
            ) {
              style.graphicName = props.styleGraphic.Mark.WellKnownName;
            }
            //vectorLayer.setStyle(maStyle.getAnnotationStyle(type,props.styleGraphic,zoomratio));
            vectorLayer.setStyle(style);
          } else if (props.featureType == 'Label') {
            vectorLayer.setStyle(getLabelStyle(feat, scale));
          } else if (
            props.featureType.toLocaleLowerCase() != 'text' &&
            !isTiplabel
          ) {
            vectorLayer.setStyle(feat.getStyleFunction());
            checkLineStyle(vectorLayer, scale);
          }
          if (props.featureType.toLocaleLowerCase() != 'text' && !isTiplabel)
            layers.push(vectorLayer);
        }
      });
    }

    function drawAnnotOnlyTipLabelV1(src, ext, scale, layers) {
      var vectorLayer, vectorSource;

      src.forEachFeatureInExtent(ext, function(feat) {
        vectorSource = new ol.source.Vector({});
        vectorLayer = new ol.layer.Vector({
          source: vectorSource,
          zIndex: 1000,
        });
        //-- Permet d'identifier les layers annotation
        vectorLayer.name = 'Edition';
        if (
          feat.getProperties().featureType &&
          feat.getProperties().featureType.toLowerCase() == 'text' &&
          feat.get('visible')
        ) {
          vectorSource.addFeature(annotTextFrame(feat, scale));
          vectorLayer.setStyle(getTextStyle(feat, scale));
          layers.push(vectorLayer);
        }
      });
    }

    function extendExtent(ext, percentage) {
      var dx, dy;

      dx = ol.extent.getWidth(ext) * percentage;
      dy = ol.extent.getHeight(ext) * percentage;
      ext[0] -= dx;
      ext[1] -= dy;
      ext[2] += dx;
      ext[3] += dy;

      return ext;
    }

    /**
     *    Dessin des annotations de type bulle annotation V2.
     *
     * @param {[[type]]} src [[Description]]
     * @param {[[type]]} ext [[Description]]
     * @param {[[type]]} scale [[Description]]
     */
    function drawAnnotOnlyTipLabelV2(src, ext, scale, layers) {
      var vectorLayer, vectorSource, annoText, coord, contains;

      ext = extendExtent(ext, 0.25);
      annoText = gclayers.getAnnoText();
      if (annoText.length > 0) {
        annoText.forEach(function(anno) {
          var res, ratio, annoVecText;
          if (anno.properties.styleGraphic.fixedSize) {
            ratio = (1.25 * scale) / anno.properties.styleTemplate.scale100;
            res = anno.annotTextFrame(ratio);
            coord = res[0].getGeometry().getCoordinates();
            annoVecText = res[0];
          } else {
            annoVecText = anno.vectorText;
            coord = anno.geometry.coordinates;
          }
          contains = ol.extent.containsCoordinate(
            ext,
            anno.geometry.coordinates
          );
          if (contains) {
            var feat = new ol.Feature({
              geometry: new ol.geom.Point(coord),
            });
            vectorSource = new ol.source.Vector({});
            feat.setProperties(anno.properties);
            vectorLayer = new ol.layer.Vector({
              source: vectorSource,
              zIndex: 1000,
            });
            //-- Permet d'identifier les layers annotation
            vectorLayer.name = 'Edition';
            vectorSource.addFeature(annoVecText);
            vectorLayer.setStyle(getTextStyleForTip(anno, scale));
            layers.push(vectorLayer);
          }
        });
      }
    }

    /**
     *      Ajout des annotations avec un layer par annotation.
     *  On ne peut avoir qu un style par layer, on est donc
     *  obligé de faire comme cela.
     */
    function addAnnotationsAsLayer(ext, layers, scale) {
      var tmp;
      var src = gclayers.getAnnotationLayer().getSource();

      tmp = ext[1];
      ext[1] = ext[3];
      ext[3] = tmp;

      //-- On s assure que les layers ajoutés précédemment pour traiter
      //-- le cas des annotations ne reste pas dans le circuit.
      removeAnnotationLayers(layers);

      //-- En premier tout sauf les annotations de type bulle
      //-- car elles doivent être par dessus tout comme sur la carte.
      drawAnnotExceptTipLabel(src, ext, scale, layers);

      //-- En second seulement les annotations de type bulle (annotation v1)
      //-- car elles sont par dessus tout comme sur la carte.
      drawAnnotOnlyTipLabelV1(src, ext, scale, layers);
      drawAnnotOnlyTipLabelV2(src, ext, scale, layers);
    }

    return {
      addAnnotationsAsLayer: addAnnotationsAsLayer,
      removeAnnotationLayers: removeAnnotationLayers,
    };
  };
  pv2Annotations.$inject = [
    '$translate',
    '$location',
    '$filter',
    'BaseMapFactory',
    'maStyle',
    'gclayers',
    '$rootScope',
  ];
  return pv2Annotations;
});
