'use strict';
define(function () {
  /**
   * Class : EditRulesFeatGeomFactory
   * Cette classe est chargée d'exécuter les règles métiers d'édition
   * associées à une couche (un FeatureTypeInfo).
   */
  var EditRulesFeatGeomFactory = function (EditTypesFactory) {
    function checkFeatureHasBeenDeleted(feat, featFtiUid, editDesc) {
      for (let i = 0; i < editDesc.relatedfeatures.length; i++) {
        const related = editDesc.relatedfeatures[i];
        const feature = related.feature;

        if (
          featFtiUid == related.fti.uid &&
          related.editType == EditTypesFactory.editTypes.delete.name &&
          feat.id == feature.id
        ) {
          return true;
        }
      }
      return false;
    }

    function checkFeatureHasBeenUpdated(feat, featFtiUid, editDesc) {
      for (let i = 0; i < editDesc.relatedfeatures.length; i++) {
        const related = editDesc.relatedfeatures[i];
        const feature = related.feature;

        if (
          featFtiUid == related.fti.uid &&
          related.editType == EditTypesFactory.editTypes.update.name &&
          feat.id == feature.id
        ) {
          return true;
        }
      }
      return false;
    }

    function checkFeatureLengthOf(li) {
      let ind;
      let len = 0;
      for (ind = 0; ind < li.length; ind++) {
        len += li[ind].getLength();
      }
      return len;
    }

    function checkFeatureGetRounded(val) {
      return Math.round(val * 100) / 100;
    }

    function checkFeatureLi1OnLi2(li1, li2) {
      let ind1, ind2, coords1, coords2;
      let x,
        x1,
        x2,
        y,
        y1,
        y2,
        onSegment = true;

      coords1 = li1.getCoordinates();
      coords2 = li2.getCoordinates();
      for (ind1 = 0; ind1 < coords1.length && onSegment; ind1++) {
        onSegment = false;
        x = checkFeatureGetRounded(coords1[ind1][0]);
        y = checkFeatureGetRounded(coords1[ind1][1]);
        for (ind2 = 0; ind2 < coords2.length - 1; ind2++) {
          x1 = checkFeatureGetRounded(coords2[ind2][0]);
          y1 = checkFeatureGetRounded(coords2[ind2][1]);
          x2 = checkFeatureGetRounded(coords2[ind2 + 1][0]);
          y2 = checkFeatureGetRounded(coords2[ind2 + 1][1]);

          if ((x - x1) / (x2 - x1) == (y - y1) / (y2 - y1)) {
            //-- Le point est sur la droite, est elle sur le segment ?
            if (
              ((x >= x1 && x <= x2) || (x >= x2 && x <= x1)) &&
              ((y >= y1 && y <= y2) || (y >= y2 && y <= y1))
            ) {
              onSegment = true;
              break;
            }
          }
        }
      }
      return onSegment;
    }

    function checkFeatureGeometyOnAnother(feat1, feat2) {
      //-- traité pour ligne et multilignes

      let tmp;
      let geom1 = feat1.getGeometry();
      let geom2 = feat2.getGeometry();
      let li1, li2, ind1, ind2;

      if (geom2 instanceof ol.geom.Point) {
        return false;
      }
      if (geom1 instanceof ol.geom.MultiLineString) {
        li1 = geom1.getLineStrings();
      } else {
        li1 = [geom1];
      }
      if (geom2 instanceof ol.geom.MultiLineString) {
        li2 = geom2.getLineStrings();
      } else {
        li2 = [geom2];
      }

      //-- feat1 doit être plus court que feat2.
      if (checkFeatureLengthOf(li1) > checkFeatureLengthOf(li2)) {
        tmp = li2;
        li2 = li1;
        li1 = tmp;
      }

      for (ind1 = 0; ind1 < li1.length; ind1++) {
        for (ind2 = 0; ind2 < li2.length; ind2++) {
          if (checkFeatureLi1OnLi2(li1[ind1], li2[ind2])) {
            break;
          }
        }

        //-- Si la ligne1 n'est pas sur la ligne2
        //-- feat1 n'est pas sur feat2.
        if (ind2 == li2.length) {
          return false;
        }
      }
      return true;
    }

    function checkFeatureGetNewOne(feat, featFtiUid, editDesc, removeAdded) {
      for (let i = 0; i < editDesc.relatedfeatures.length; i++) {
        const related = editDesc.relatedfeatures[i];
        const feature = related.feature;

        if (
          related.editType == EditTypesFactory.editTypes.add.name ||
          related.editType == EditTypesFactory.editTypes.update.name
        ) {
          //-- ATTENTION ce test n'est pa suffisant pour trouver
          //-- une feature modifiée suite à un split (cutting)
          //-- on a une chance sur 2 d'obtenir la bonne feature.
          //-- Il faut donc si possible exécuter un split en dernier.
          if (checkFeatureGeometyOnAnother(feat, feature)) {
            //-- Enlever la feature à ajouter,
            //-- elle va être transformée par une règle suivante.
            if (
              related.editType == EditTypesFactory.editTypes.add.name &&
              removeAdded
            ) {
              editDesc.relatedfeatures.splice(i, 1);
            }

            return feature;
          }
        }
      }
      return feat;
    }

    /**
     *    Si une feature à traiter fait partie des features supprimées
     * et que sa geométrie ligne est superposé à une portion du segment
     * d'une feature ajoutée ou modifiée c'est avec cette feature
     * qu'il faut travailler.
     *
     * @param {Object} feat
     * @param {Object} featFtiUid
     * @param {Object} editDesc
     * @param {Object} removeAdded: si VRAI, suppression du feature
     *                              de la liste des objets à créer.
     *                              Exemple d'utilisation =
     *                              quand on split le résultat
     *                              d'un merge, l'objet à créer issu
     *                              du merge ne sera plus à créer
     *                              puisqu'on le split.'
     */
    function checkFeatureStillTheSame(feat, featFtiUid, editDesc, removeAdded) {
      if (checkFeatureHasBeenDeleted(feat, featFtiUid, editDesc)) {
        return checkFeatureGetNewOne(feat, featFtiUid, editDesc, removeAdded);
      } else if (checkFeatureHasBeenUpdated(feat, featFtiUid, editDesc)) {
        return checkFeatureGetNewOne(feat, featFtiUid, editDesc);
      } else {
        return feat;
      }
    }

    function smlsGetLsEndingWith(point, li, iStart) {
      let ind, liPt;

      for (ind = iStart; ind < li.length; ind++) {
        liPt = li[ind].getLastCoordinate();
        if (liPt[0] == point[0] && liPt[1] == point[1]) {
          return ind;
        }
      }
      return -1;
    }

    function smlsGetLsStartingWith(point, li, iStart) {
      let ind, liPt;

      for (ind = iStart; ind < li.length; ind++) {
        liPt = li[ind].getFirstCoordinate();
        if (liPt[0] == point[0] && liPt[1] == point[1]) {
          return ind;
        }
      }
      return -1;
    }

    function smlsConcat(li1, li2, concatType) {
      let ind1, ind2, coords;

      if (concatType == 'revert_2') {
        //-- 2018-11-20 coords = li2.getCoordinates();
        coords = li2;
        for (ind2 = coords.length - 1; ind2 >= 0; ind2--) {
          li1.push(coords[ind2]);
        }
      } else if (concatType == 'second_is_first') {
        return li2.concat(li1);
      } else {
        return li1.concat(li2);
      }
    }

    /**
     *    Suppression des points en double se suivant.
     *
     * @param {Object} li
     */
    function smlsCleanLineCoordArray(li) {
      let ind;

      for (ind = li.length - 1; ind > 0; ind--) {
        if (li[ind - 1][0] == li[ind][0] && li[ind - 1][1] == li[ind][1]) {
          li.splice(ind, 1);
        }
      }
      return li;
    }

    function inverserLine(mline) {
      console.log(mline);
      const newli = simplifyMultiLineStrings(mline);
      const listInversee = new Array();

      listInversee[0] = new Array();
      let j = 0;
      for (var i = newli.getCoordinates()[0].length - 1; i >= 0; i--) {
        listInversee[0][j++] = newli.getCoordinates()[0][i];
      }
      newli.setCoordinates(listInversee);
      return newli;
    }

    function simplifyMultiLineStrings(mline) {
      let li1, newli, ind1, ind2, indLast;

      if (!mline instanceof ol.geom.MultiLineString) {
        newli = new ol.geom.MultiLineString([mline.getCoordinates()]);
      } else {
        newli = [];
        li1 = mline.getLineStrings();
        newli = smlsConcat(newli, li1[0].getCoordinates(), 'init');
        li1.splice(0, 1);
        for (ind1 = 0; ind1 < li1.length; ind1++) {
          ind2 = smlsGetLsStartingWith(newli[0], li1, ind1);
          if (ind2 != -1) {
            newli = smlsConcat(newli, li1[ind2].getCoordinates(), 'revert_2');
            li1.splice(ind2, 1);
            ind1 = -1;
          } else {
            ind2 = smlsGetLsEndingWith(newli[0], li1, ind1);
            if (ind2 != -1) {
              newli = smlsConcat(
                newli,
                li1[ind2].getCoordinates(),
                'second_is_first'
              );
              li1.splice(ind2, 1);
              ind1 = -1;
            } else {
              indLast = newli.length - 1;
              ind2 = smlsGetLsStartingWith(newli[indLast], li1, ind1);
              if (ind2 != -1) {
                newli = smlsConcat(newli, li1[ind2].getCoordinates(), 'normal');
                li1.splice(ind2, 1);
                ind1 = -1;
              } else {
                ind2 = smlsGetLsEndingWith(newli[indLast], li1, ind1);
                if (ind2 != -1) {
                  newli = smlsConcat(
                    newli,
                    li1[ind2].getCoordinates(),
                    'revert_2'
                  );
                  li1.splice(ind2, 1);
                  ind1 = -1;
                }
              }
            }
          }
        }
        newli = new ol.geom.MultiLineString([smlsCleanLineCoordArray(newli)]);
      }
      return newli;
    }

    return {
      checkFeatureStillTheSame: checkFeatureStillTheSame,
      simplifyMultiLineStrings: simplifyMultiLineStrings,
      inverserLine: inverserLine,
    };
  };

  EditRulesFeatGeomFactory.$inject = ['EditTypesFactory'];
  return EditRulesFeatGeomFactory;
});
