/* eslint-disable */
/* This file is basically a copy of
 * https://github.com/Viglino/ol-ext/blob/v3.2.2/src/interaction/Transform.js
 * by https://github.com/Viglino
 * That code is licenced under the French Opensource BSD like CeCILL-B FREE
 * SOFTWARE LICENSE
 * (http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.html)
 */
/**
 * A wrapper class that loads the excellent `ol.interaction.Transform` into
 * the page.
 *
 * Changes to the original class by [Viglino](https://github.com/Viglino):
 *   * replace imports by legacy ol object
 *   * Wrapped in an Ext.class, so this extension can be required from other
 *     classes
 *   * The vectorlayer for the handles has KEY_DISPLAY_IN_LAYERSWITCHER from
 *     BasiGX.util.Layer set to false
 */
Ext.define('BasiGX.olExt.TransformInteraction', {
    requires: [
        'BasiGX.util.Layer'
    ],
    singleton: true
}, function() {
    // Once this class is required, this function will execute and create the
    // extension in the ol namespace.

    // some basic sanity checks
    if (!ol || !ol.interaction || !ol.interaction.Pointer) {
        Ext.log.error('Cannot define `ol.interaction.Transform`: Not ' +
            'all needed OpenLayers classes present.');
        return;
    } else if ('Transform' in ol.interaction) {
        Ext.log.error('Cannot define `ol.interaction.Transform`: It is ' +
            ' defined already.');
        return;
    }

    // ------------------------------------------------------------------------------------------------------
    // ---       Start of inherits from https://github.com/Viglino/ol-ext/blob/v3.2.2/src/util/ext.js     ---

    var inherits = function(child,parent) {
        child.prototype = Object.create(parent.prototype);
        child.prototype.constructor = child;
    };

    // ---       End of inherits from https://github.com/Viglino/ol-ext/blob/v3.2.2/src/util/ext.js       ---
    // ------------------------------------------------------------------------------------------------------


    // ------------------------------------------------------------------------------------------------------
    // ---       Start of https://github.com/Viglino/ol-ext/blob/v3.2.2/src/interaction/Transform.js      ---

    /** Interaction rotate
     * @constructor
     * @extends {ol.interaction.Pointer}
     * @fires select | rotatestart | rotating | rotateend | translatestart | translating | translateend | scalestart | scaling | scaleend
     * @param {any} options
     *  @param {function} options.filter A function that takes a Feature and a Layer and returns true if the feature may be transformed or false otherwise.
     *  @param {Array<ol.Layer>} options.layers array of layers to transform,
     *  @param {ol.Collection<ol.Feature>} options.features collection of feature to transform,
     *	@param {ol.EventsConditionType|undefined} options.condition A function that takes an ol.MapBrowserEvent and a feature collection and returns a boolean to indicate whether that event should be handled. default: ol.events.condition.always.
     *	@param {ol.EventsConditionType|undefined} options.addCondition A function that takes an ol.MapBrowserEvent and returns a boolean to indicate whether that event should be handled ie. the feature will be added to the transforms features. default: ol.events.condition.never.
     *	@param {number | undefined} options.hitTolerance Tolerance to select feature in pixel, default 0
     *	@param {bool} options.translateFeature Translate when click on feature
     *	@param {bool} options.translate Can translate the feature
     *  @param {bool} options.translateBBox Enable translate when the user drags inside the bounding box
     *	@param {bool} options.stretch can stretch the feature
     *	@param {bool} options.scale can scale the feature
     *	@param {bool} options.rotate can rotate the feature
     *	@param {bool} options.noFlip prevent the feature geometry to flip, default false
     *	@param {bool} options.selection the intraction handle selection/deselection, if not use the select prototype to add features to transform, default true
     *	@param {ol.events.ConditionType | undefined} options.keepAspectRatio A function that takes an ol.MapBrowserEvent and returns a boolean to keep aspect ratio, default ol.events.condition.shiftKeyOnly.
     *	@param {ol.events.ConditionType | undefined} options.modifyCenter A function that takes an ol.MapBrowserEvent and returns a boolean to apply scale & strech from the center, default ol.events.condition.metaKey or ol.events.condition.ctrlKey.
     *	@param {boolean} options.enableRotatedTransform Enable transform when map is rotated
     *	@param {} options.style list of ol.style for handles
     *
     */
    ol.interaction.Transform = function(options) {
        if (!options) options = {};
        var self = this;

        this.selection_ = new ol.Collection();

        // Create a new overlay layer for the sketch
        this.handles_ = new ol.Collection();
        this.overlayLayer_ = new ol.layer.Vector({
            source: new ol.source.Vector({
                features: this.handles_,
                useSpatialIndex: false,
                wrapX: false // For vector editing across the -180° and 180° meridians to work properly, this should be set to false
            }),
            name:'Transform overlay',
            displayInLayerSwitcher: false,
            // Return the style according to the handle type
            style: function (feature) {
                return (self.style[(feature.get('handle')||'default')+(feature.get('constraint')||'')+(feature.get('option')||'')]);
            },
        });

        // Extend pointer
        ol.interaction.Pointer.call(this, {
            handleDownEvent: this.handleDownEvent_,
            handleDragEvent: this.handleDragEvent_,
            handleMoveEvent: this.handleMoveEvent_,
            handleUpEvent: this.handleUpEvent_
        });

        // Collection of feature to transform
        this.features_ = options.features;
        // Filter or list of layers to transform
        if (typeof(options.filter)==='function') this._filter = options.filter;
        this.layers_ = options.layers ? (options.layers instanceof Array) ? options.layers:[options.layers] : null;

        this._handleEvent = options.condition || function() { return true; };
        this.addFn_ = options.addCondition || function() { return false; };
        /* Translate when click on feature */
        this.set('translateFeature', (options.translateFeature!==false));
        /* Can translate the feature */
        this.set('translate', (options.translate!==false));
        /* Translate when click on the bounding box */
        this.set('translateBBox', (options.translateBBox===true));
        /* Can stretch the feature */
        this.set('stretch', (options.stretch!==false));
        /* Can scale the feature */
        this.set('scale', (options.scale!==false));
        /* Can rotate the feature */
        this.set('rotate', (options.rotate!==false));
        /* Keep aspect ratio */
        this.set('keepAspectRatio', (options.keepAspectRatio || function(e){ return e.originalEvent.shiftKey }));
        /* Modify center */
        this.set('modifyCenter', (options.modifyCenter || function(e){ return e.originalEvent.metaKey || e.originalEvent.ctrlKey }));
        /* Prevent flip */
        this.set('noFlip', (options.noFlip || false));
        /* Handle selection */
        this.set('selection', (options.selection !== false));
        /*  */
        this.set('hitTolerance', (options.hitTolerance || 0));
        /* Enable view rotated transforms */
        this.set('enableRotatedTransform', (options.enableRotatedTransform || false));


        // Force redraw when changed
        this.on ('propertychange', function() {
            this.drawSketch_();
        });

        // Change BasiGX:
        this.overlayLayer_.set(
          BasiGX.util.Layer.KEY_DISPLAY_IN_LAYERSWITCHER,
          false
        );

        // setstyle
        this.setDefaultStyle();
    };
    inherits(ol.interaction.Transform, ol.interaction.Pointer);

    /** Cursors for transform
     */
    ol.interaction.Transform.prototype.Cursors = {
        'default': 'auto',
        'select': 'pointer',
        'translate': 'move',
        'rotate': 'move',
        'rotate0': 'move',
        'scale': 'nesw-resize',
        'scale1': 'nwse-resize',
        'scale2': 'nesw-resize',
        'scale3': 'nwse-resize',
        'scalev': 'ew-resize',
        'scaleh1': 'ns-resize',
        'scalev2': 'ew-resize',
        'scaleh3': 'ns-resize'
    };

    /**
     * Remove the interaction from its current map, if any,  and attach it to a new
     * map, if any. Pass `null` to just remove the interaction from the current map.
     * @param {ol.Map} map Map.
     * @api stable
     */
    ol.interaction.Transform.prototype.setMap = function(map) {
        var oldMap = this.getMap();
        if (oldMap) {
            var targetElement = oldMap.getTargetElement();
            oldMap.removeLayer(this.overlayLayer_);
            if (this.previousCursor_ && targetElement) {
                targetElement.style.cursor = this.previousCursor_;
            }
            this.previousCursor_ = undefined;
        }
        ol.interaction.Pointer.prototype.setMap.call (this, map);
        this.overlayLayer_.setMap(map);
        if (map === null) {
            this.select(null);
        }
        if (map !== null) {
            this.isTouch = /touch/.test(map.getViewport().className);
            this.setDefaultStyle();
        }
    };

    /**
     * Activate/deactivate interaction
     * @param {bool}
     * @api stable
     */
    ol.interaction.Transform.prototype.setActive = function(b) {
        this.select(null);
        this.overlayLayer_.setVisible(b);
        ol.interaction.Pointer.prototype.setActive.call (this, b);
    };

    /** Set default sketch style
     * @param {Object|undefined} options
     *  @param {ol.style.Stroke} stroke stroke style for selection rectangle
     *  @param {ol.style.Fill} fill fill style for selection rectangle
     *  @param {ol.style.Stroke} pointStroke stroke style for handles
     *  @param {ol.style.Fill} pointFill fill style for handles
     */
    ol.interaction.Transform.prototype.setDefaultStyle = function(options) {
        options = options || {}
        // Style
        var stroke = options.pointStroke || new ol.style.Stroke({ color: [255,0,0,1], width: 1 });
        var strokedash = options.stroke || new ol.style.Stroke({ color: [255,0,0,1], width: 1, lineDash:[4,4] });
        var fill0 = options.fill || new ol.style.Fill({ color:[255,0,0,0.01] });
        var fill = options.pointFill || new ol.style.Fill({ color:[255,255,255,0.8] });
        var circle = new ol.style.RegularShape({
            fill: fill,
            stroke: stroke,
            radius: this.isTouch ? 12 : 6,
            points: 15
        });
        circle.getAnchor()[0] = this.isTouch ? -10 : -5;
        var bigpt = new ol.style.RegularShape({
            fill: fill,
            stroke: stroke,
            radius: this.isTouch ? 16 : 8,
            points: 4,
            angle: Math.PI/4
        });
        var smallpt = new ol.style.RegularShape({
            fill: fill,
            stroke: stroke,
            radius: this.isTouch ? 12 : 6,
            points: 4,
            angle: Math.PI/4
        });
        function createStyle (img, stroke, fill) {
            return [ new ol.style.Style({image:img, stroke:stroke, fill:fill}) ];
        }
        /** Style for handles */
        this.style = {
            'default': createStyle (bigpt, strokedash, fill0),
            'translate': createStyle (bigpt, stroke, fill),
            'rotate': createStyle (circle, stroke, fill),
            'rotate0': createStyle (bigpt, stroke, fill),
            'scale': createStyle (bigpt, stroke, fill),
            'scale1': createStyle (bigpt, stroke, fill),
            'scale2': createStyle (bigpt, stroke, fill),
            'scale3': createStyle (bigpt, stroke, fill),
            'scalev': createStyle (smallpt, stroke, fill),
            'scaleh1': createStyle (smallpt, stroke, fill),
            'scalev2': createStyle (smallpt, stroke, fill),
            'scaleh3': createStyle (smallpt, stroke, fill),
        };
        this.drawSketch_();
    }

    /**
     * Set sketch style.
     * @param {style} style Style name: 'default','translate','rotate','rotate0','scale','scale1','scale2','scale3','scalev','scaleh1','scalev2','scaleh3'
     * @param {ol.style.Style|Array<ol.style.Style>} olstyle
     * @api stable
     */
    ol.interaction.Transform.prototype.setStyle = function(style, olstyle) {
        if (!olstyle) return;
        if (olstyle instanceof Array) this.style[style] = olstyle;
        else this.style[style] = [ olstyle ];
        for (var i=0; i<this.style[style].length; i++) {
            var im = this.style[style][i].getImage();
            if (im) {
                if (style == 'rotate') im.getAnchor()[0] = -5;
                if (this.isTouch) im.setScale(1.8);
            }
            var tx = this.style[style][i].getText();
            if (tx) {
                if (style == 'rotate') tx.setOffsetX(this.isTouch ? 14 : 7);
                if (this.isTouch) tx.setScale(1.8);
            }
        }
        this.drawSketch_();
    };

    /** Get Feature at pixel
     * @param {ol.Pixel}
     * @return {ol.feature}
     * @private
     */
    ol.interaction.Transform.prototype.getFeatureAtPixel_ = function(pixel) {
        var self = this;
        return this.getMap().forEachFeatureAtPixel(pixel,
          function(feature, layer) {
              var found = false;
              // Overlay ?
              if (!layer) {
                  if (feature===self.bbox_) {
                      if (self.get('translateBBox')) {
                          return { feature: feature, handle: 'translate', constraint:'', option: '' };
                      } else {
                          return false;
                      }
                  }
                  self.handles_.forEach (function(f) { if (f===feature) found=true; });
                  if (found) return { feature: feature, handle:feature.get('handle'), constraint:feature.get('constraint'), option:feature.get('option') };
              }
              // No seletion
              if (!self.get('selection')) {
                  // Return the currently selected feature the user is interacting with.
                  if (self.selection_.getArray().some(function(f) { return feature === f; })) {
                      return { feature: feature };
                  }
                  return null;
              }
              // filter condition
              if (self._filter) {
                  if (self._filter(feature,layer)) return { feature: feature };
                  else return null;
              }
              // feature belong to a layer
              else if (self.layers_) {
                  for (var i=0; i<self.layers_.length; i++) {
                      if (self.layers_[i]===layer) return { feature: feature };
                  }
                  return null;
              }
              // feature in the collection
              else if (self.features_) {
                  self.features_.forEach (function(f) { if (f===feature) found=true; });
                  if (found) return { feature: feature };
                  else return null;
              }
              // Others
              else return { feature: feature };
          },
          { hitTolerance: this.get('hitTolerance') }
        ) || {};
    }

    /** Rotate feature from map view rotation
     * @param {ol.Feature} f the feature
     * @param {boolean} clone clone resulting geom
     * @param {ol.geom.Geometry} rotated geometry
     */
    ol.interaction.Transform.prototype.getGeometryRotateToZero_ = function(f, clone) {
        var origGeom = f.getGeometry();
        var viewRotation = this.getMap().getView().getRotation();
        if (viewRotation === 0 || !this.get('enableRotatedTransform')) {
            return (clone) ? origGeom.clone() : origGeom;
        }
        var rotGeom = origGeom.clone();
        rotGeom.rotate(viewRotation * -1, this.getMap().getView().getCenter());
        return rotGeom;
    }

    /** Draw transform sketch
     * @param {boolean} draw only the center
     */
    ol.interaction.Transform.prototype.drawSketch_ = function(center) {
        var i, f, geom;
        this.overlayLayer_.getSource().clear();
        if (!this.selection_.getLength()) return;
        var viewRotation = this.getMap().getView().getRotation();
        var ext = this.getGeometryRotateToZero_(this.selection_.item(0)).getExtent();
        // Clone and extend
        ext = ol.extent.buffer(ext, 0);
        this.selection_.forEach(function (f) {
            var extendExt = this.getGeometryRotateToZero_(f).getExtent();
            ol.extent.extend(ext, extendExt);
        }.bind(this));
        var fromExtent = ol.geom.Polygon_fromExtent;
        if (!fromExtent) {
            fromExtent = ol.geom.Polygon.fromExtent;
        }

        if (center === true) {
            if (!this.ispt_) {
                this.overlayLayer_.getSource().addFeature(new ol.Feature( { geometry: new ol.geom.Point(this.center_), handle:'rotate0' }) );
                geom = fromExtent(ext);
                if (this.get('enableRotatedTransform') && viewRotation !== 0) {
                    geom.rotate(viewRotation, this.getMap().getView().getCenter())
                }
                f = this.bbox_ = new ol.Feature(geom);
                this.overlayLayer_.getSource().addFeature (f);
            }
        }
        else {
            if (this.ispt_) {
                var p = this.getMap().getPixelFromCoordinate([ext[0], ext[1]]);
                ext = ol.extent.boundingExtent([
                    this.getMap().getCoordinateFromPixel([p[0]-10, p[1]-10]),
                    this.getMap().getCoordinateFromPixel([p[0]+10, p[1]+10])
                ]);
            }
            geom = fromExtent(ext);
            if (this.get('enableRotatedTransform') && viewRotation !== 0) {
                geom.rotate(viewRotation, this.getMap().getView().getCenter())
            }
            f = this.bbox_ = new ol.Feature(geom);
            var features = [];
            var g = geom.getCoordinates()[0];
            if (!this.ispt_) {
                features.push(f);
                // Middle
                if (!this.iscircle_ && this.get('stretch') && this.get('scale')) for (i=0; i<g.length-1; i++) {
                    f = new ol.Feature( { geometry: new ol.geom.Point([(g[i][0]+g[i+1][0])/2,(g[i][1]+g[i+1][1])/2]), handle:'scale', constraint:i%2?"h":"v", option:i });
                    features.push(f);
                }
                // Handles
                if (this.get('scale')) for (i=0; i<g.length-1; i++) {
                    f = new ol.Feature( { geometry: new ol.geom.Point(g[i]), handle:'scale', option:i });
                    features.push(f);
                }
                // Center
                if (this.get('translate') && !this.get('translateFeature')) {
                    f = new ol.Feature( { geometry: new ol.geom.Point([(g[0][0]+g[2][0])/2, (g[0][1]+g[2][1])/2]), handle:'translate' });
                    features.push(f);
                }
            }
            // Rotate
            if (!this.iscircle_ && this.get('rotate')) {
                f = new ol.Feature( { geometry: new ol.geom.Point(g[3]), handle:'rotate' });
                features.push(f);
            }
            // Add sketch
            this.overlayLayer_.getSource().addFeatures(features);
        }

    };

    /** Select a feature to transform
     * @param {ol.Feature} feature the feature to transform
     * @param {boolean} add true to add the feature to the selection, default false
     */
    ol.interaction.Transform.prototype.select = function(feature, add) {
        if (!feature) {
            this.selection_.clear();
            this.drawSketch_();
            return;
        }
        if (!feature.getGeometry || !feature.getGeometry()) return;
        // Add to selection
        if (add) {
            this.selection_.push(feature);
        } else {
            var index = this.selection_.getArray().indexOf(feature);
            this.selection_.removeAt(index);
        }
        this.ispt_ = (this.selection_.getLength()===1 ? (this.selection_.item(0).getGeometry().getType() == "Point") : false);
        this.iscircle_ = (this.selection_.getLength()===1 ? (this.selection_.item(0).getGeometry().getType() == "Circle") : false);
        this.drawSketch_();
        this.watchFeatures_();
        // select event
        this.dispatchEvent({ type:'select', feature: feature, features: this.selection_ });
    };

    /** Update the selection collection.
     * @param {ol.Collection<ol.Feature>} features the features to transform
     */
    ol.interaction.Transform.prototype.setSelection = function(features) {
        this.selection_.clear();
        features.forEach(function(feature) {
            this.selection_.push(feature);
        }.bind(this));

        this.ispt_ = (this.selection_.getLength()===1 ? (this.selection_.item(0).getGeometry().getType() == "Point") : false);
        this.iscircle_ = (this.selection_.getLength()===1 ? (this.selection_.item(0).getGeometry().getType() == "Circle") : false);
        this.drawSketch_();
        this.watchFeatures_();
        // select event
        this.dispatchEvent({ type:'select', features: this.selection_ });
    };

    /** Watch selected features
     * @private
     */
    ol.interaction.Transform.prototype.watchFeatures_ = function() {
        // Listen to feature modification
        if (this._featureListeners) {
            this._featureListeners.forEach(function (l) {
                ol.Observable.unByKey(l)
            });
        }
        this._featureListeners = [];
        this.selection_.forEach(function(f) {
            this._featureListeners.push(
              f.on('change', function() {
                  if (!this.isUpdating_) {
                      this.drawSketch_();
                  }
              }.bind(this))
            );
        }.bind(this));
    };

    /**
     * @param {ol.MapBrowserEvent} evt Map browser event.
     * @return {boolean} `true` to start the drag sequence.
     * @private
     */
    ol.interaction.Transform.prototype.handleDownEvent_ = function(evt) {
        var fromExtent = ol.geom.Polygon_fromExtent;
        if (!fromExtent) {
            fromExtent = ol.geom.Polygon.fromExtent;
        }
        if (!this._handleEvent(evt, this.selection_)) return;
        var sel = this.getFeatureAtPixel_(evt.pixel);
        var feature = sel.feature;
        if (this.selection_.getLength()
          && this.selection_.getArray().indexOf(feature) >= 0
          && ((this.ispt_ && this.get('translate')) || this.get('translateFeature'))
        ){
            sel.handle = 'translate';
        }
        if (sel.handle) {
            this.mode_ = sel.handle;
            this.opt_ = sel.option;
            this.constraint_ = sel.constraint;
            // Save info
            var viewRotation = this.getMap().getView().getRotation();
            this.coordinate_ = evt.coordinate;
            this.pixel_ = evt.pixel;
            this.geoms_ = [];
            this.rotatedGeoms_ = [];
            var extent = ol.extent.createEmpty();
            var rotExtent = ol.extent.createEmpty();
            for (var i=0, f; f=this.selection_.item(i); i++) {
                this.geoms_.push(f.getGeometry().clone());
                extent = ol.extent.extend(extent, f.getGeometry().getExtent());
                if (this.get('enableRotatedTransform') && viewRotation !== 0) {
                    var rotGeom = this.getGeometryRotateToZero_(f, true);
                    this.rotatedGeoms_.push(rotGeom);
                    rotExtent = ol.extent.extend(rotExtent, rotGeom.getExtent());
                }
            }
            this.extent_ = fromExtent(extent).getCoordinates()[0];
            if (this.get('enableRotatedTransform') && viewRotation !== 0) {
                this.rotatedExtent_ = fromExtent(rotExtent).getCoordinates()[0];
            }
            if (this.mode_==='rotate') {
                this.center_ = this.getCenter() || ol.extent.getCenter(extent);
                // we are now rotating (cursor down on rotate mode), so apply the grabbing cursor
                var element = evt.map.getTargetElement();
                element.style.cursor = this.Cursors.rotate0;
                this.previousCursor_ = element.style.cursor;
            } else {
                this.center_ = ol.extent.getCenter(extent);
            }
            this.angle_ = Math.atan2(this.center_[1]-evt.coordinate[1], this.center_[0]-evt.coordinate[0]);

            this.dispatchEvent({
                type: this.mode_+'start',
                feature: this.selection_.item(0), // backward compatibility
                features: this.selection_,
                pixel: evt.pixel,
                coordinate: evt.coordinate
            });
            return true;
        }
        else if (this.get('selection')) {
            if (feature){
                if (!this.addFn_(evt)) this.selection_.clear();
                var index = this.selection_.getArray().indexOf(feature);
                if (index < 0) this.selection_.push(feature);
                else this.selection_.removeAt(index);
            } else {
                this.selection_.clear();
            }
            this.ispt_ = this.selection_.getLength()===1 ? (this.selection_.item(0).getGeometry().getType() == "Point") : false;
            this.iscircle_ = (this.selection_.getLength()===1 ? (this.selection_.item(0).getGeometry().getType() == "Circle") : false);
            this.drawSketch_();
            this.watchFeatures_();
            this.dispatchEvent({ type:'select', feature: feature, features: this.selection_, pixel: evt.pixel, coordinate: evt.coordinate });
            return false;
        }
    };


    /**
     * Get features to transform
     * @return {ol.Collection<ol.Feature>}
     */
    ol.interaction.Transform.prototype.getFeatures = function() {
        return this.selection_;
    };

    /**
     * Get the rotation center
     * @return {ol.coordinates|undefined}
     */
    ol.interaction.Transform.prototype.getCenter = function() {
        return this.get('center');
    };

    /**
     * Set the rotation center
     * @param {ol.coordinates|undefined} c the center point, default center on the objet
     */
    ol.interaction.Transform.prototype.setCenter = function(c) {
        return this.set('center', c);
    }

    /**
     * @param {ol.MapBrowserEvent} evt Map browser event.
     * @private
     */
    ol.interaction.Transform.prototype.handleDragEvent_ = function(evt) {
        if (!this._handleEvent(evt, this.features_)) return;
        var viewRotation = this.getMap().getView().getRotation();
        var i, f, geometry;
        var pt0 = [this.coordinate_[0], this.coordinate_[1]];
        var pt = [evt.coordinate[0], evt.coordinate[1]];
        this.isUpdating_ = true;
        switch (this.mode_) {
            case 'rotate': {
                var a = Math.atan2(this.center_[1]-pt[1], this.center_[0]-pt[0]);
                if (!this.ispt) {
                    // var geometry = this.geom_.clone();
                    // geometry.rotate(a-this.angle_, this.center_);
                    // this.feature_.setGeometry(geometry);
                    for (i=0, f; f=this.selection_.item(i); i++) {
                        geometry = this.geoms_[i].clone();
                        geometry.rotate(a - this.angle_, this.center_);
                        // bug: ol, bad calculation circle geom extent
                        if (geometry.getType() == 'Circle') geometry.setCenterAndRadius(geometry.getCenter(), geometry.getRadius());
                        f.setGeometry(geometry);
                    }
                }
                this.drawSketch_(true);
                this.dispatchEvent({
                    type:'rotating',
                    feature: this.selection_.item(0),
                    features: this.selection_,
                    angle: a-this.angle_,
                    pixel: evt.pixel,
                    coordinate: evt.coordinate
                });
                break;
            }
            case 'translate': {
                var deltaX = pt[0] - pt0[0];
                var deltaY = pt[1] - pt0[1];

                //this.feature_.getGeometry().translate(deltaX, deltaY);
                for (i=0, f; f=this.selection_.item(i); i++) {
                    f.getGeometry().translate(deltaX, deltaY);
                }
                this.handles_.forEach(function(f) {
                    f.getGeometry().translate(deltaX, deltaY);
                });

                this.coordinate_ = evt.coordinate;
                this.dispatchEvent({
                    type:'translating',
                    feature: this.selection_.item(0),
                    features: this.selection_,
                    delta:[deltaX,deltaY],
                    pixel: evt.pixel,
                    coordinate: evt.coordinate
                });
                break;
            }
            case 'scale': {
                var center = this.center_;
                if (this.get('modifyCenter')(evt)) {
                    var extentCoordinates = this.extent_;
                    if (this.get('enableRotatedTransform') && viewRotation !== 0) {
                        extentCoordinates = this.rotatedExtent_;
                    }
                    center = extentCoordinates[(Number(this.opt_)+2)%4];
                }

                var downCoordinate = this.coordinate_;
                var dragCoordinate = evt.coordinate;
                if (this.get('enableRotatedTransform') && viewRotation !== 0) {
                    var downPoint = new ol.geom.Point(this.coordinate_);
                    downPoint.rotate(viewRotation * -1, center);
                    downCoordinate = downPoint.getCoordinates();

                    var dragPoint = new ol.geom.Point(evt.coordinate);
                    dragPoint.rotate(viewRotation * -1, center);
                    dragCoordinate = dragPoint.getCoordinates();
                }

                var scx = ((dragCoordinate)[0] - (center)[0]) / (downCoordinate[0] - (center)[0]);
                var scy = ((dragCoordinate)[1] - (center)[1]) / (downCoordinate[1] - (center)[1]);

                if (this.get('enableRotatedTransform') && viewRotation !== 0) {
                    var centerPoint = new ol.geom.Point(center);
                    centerPoint.rotate(viewRotation * -1, this.getMap().getView().getCenter());
                    center = centerPoint.getCoordinates();
                }

                if (this.get('noFlip')) {
                    if (scx<0) scx=-scx;
                    if (scy<0) scy=-scy;
                }

                if (this.constraint_) {
                    if (this.constraint_=="h") scx=1;
                    else scy=1;
                }
                if (this.get('keepAspectRatio')(evt)) {
                    scx = scy = Math.min(scx,scy);
                }

                for (i=0, f; f=this.selection_.item(i); i++) {
                    geometry = (viewRotation === 0 || !this.get('enableRotatedTransform')) ? this.geoms_[i].clone() : this.rotatedGeoms_[i].clone();
                    geometry.applyTransform(function(g1, g2, dim) {
                        if (dim<2) return g2;

                        for (var j=0; j<g1.length; j+=dim) {
                            if (scx!=1) g2[j] = center[0] + (g1[j]-center[0])*scx;
                            if (scy!=1) g2[j+1] = center[1] + (g1[j+1]-center[1])*scy;
                        }
                        // bug: ol, bad calculation circle geom extent
                        if (geometry.getType() == 'Circle') geometry.setCenterAndRadius(geometry.getCenter(), geometry.getRadius());
                        return g2;
                    });
                    if (this.get('enableRotatedTransform') && viewRotation !== 0) {
                        //geometry.rotate(viewRotation, rotationCenter);
                        geometry.rotate(viewRotation, this.getMap().getView().getCenter());
                    }
                    f.setGeometry(geometry);
                }
                this.drawSketch_();
                this.dispatchEvent({
                    type:'scaling',
                    feature: this.selection_.item(0),
                    features: this.selection_,
                    scale:[scx,scy],
                    pixel: evt.pixel,
                    coordinate: evt.coordinate
                });
                break;
            }
            default: break;
        }
        this.isUpdating_ = false;
    };

    /**
     * @param {ol.MapBrowserEvent} evt Event.
     * @private
     */
    ol.interaction.Transform.prototype.handleMoveEvent_ = function(evt) {
        if (!this._handleEvent(evt, this.features_)) return;
        // console.log("handleMoveEvent");
        if (!this.mode_) {
            var sel = this.getFeatureAtPixel_(evt.pixel);
            var element = evt.map.getTargetElement();
            if (sel.feature) {
                var c = sel.handle ? this.Cursors[(sel.handle||'default')+(sel.constraint||'')+(sel.option||'')] : this.Cursors.select;

                if (this.previousCursor_===undefined) {
                    this.previousCursor_ = element.style.cursor;
                }
                element.style.cursor = c;
            } else {
                if (this.previousCursor_!==undefined) element.style.cursor = this.previousCursor_;
                this.previousCursor_ = undefined;
            }
        }
    };

    /**
     * @param {ol.MapBrowserEvent} evt Map browser event.
     * @return {boolean} `false` to stop the drag sequence.
     */
    ol.interaction.Transform.prototype.handleUpEvent_ = function(evt) {
        // remove rotate0 cursor on Up event, otherwise it's stuck on grab/grabbing
        if (this.mode_ === 'rotate') {
            var element = evt.map.getTargetElement();
            element.style.cursor = this.Cursors.default;
            this.previousCursor_ = undefined;
        }

        //dispatchEvent
        this.dispatchEvent({
            type:this.mode_+'end',
            feature: this.selection_.item(0),
            features: this.selection_,
            oldgeom: this.geoms_[0],
            oldgeoms: this.geoms_
        });

        this.drawSketch_();
        this.mode_ = null;
        return false;
    };

    /** Get the features that are selected for transform
     * @return ol.Collection
     */
    ol.interaction.Transform.prototype.getFeatures = function() {
        return this.selection_;
    };

    // ---       End of https://github.com/Viglino/ol-ext/blob/v3.2.2/src/interaction/Transform.js        ---
    // ------------------------------------------------------------------------------------------------------
});