text: import Super from './super';
  import * as u from './utils';
  
  ///////////////////////
  ///   THE NIPPLE    ///
  ///////////////////////
  
  function Nipple (collection, options) {
      this.identifier = options.identifier;
      this.position = options.position;
      this.frontPosition = options.frontPosition;
      this.collection = collection;
  
      // Defaults
      this.defaults = {
          size: {
              val: 100,
              unit: 'px'
          },
          threshold: 0.1,
          color: 'white',
          fadeTime: 250,
          dataOnly: false,
          restJoystick: true,
          restOpacity: 0.5,
          mode: 'dynamic',
          zone: document.body,
          lockX: false,
          lockY: false
      };
  
      this.config(options);
  
      // Overwrites
      if (this.options.mode === 'dynamic') {
          this.options.restOpacity = 0;
      }
  
      this.id = Nipple.id;
      Nipple.id += 1;
      this.buildEl()
          .stylize();
  
      // Nipple's API.
      this.instance = {
          el: this.ui.el,
          on: this.on.bind(this),
          off: this.off.bind(this),
          show: this.show.bind(this),
          hide: this.hide.bind(this),
          add: this.addToDom.bind(this),
          remove: this.removeFromDom.bind(this),
          destroy: this.destroy.bind(this),
          resetDirection: this.resetDirection.bind(this),
          computeDirection: this.computeDirection.bind(this),
          trigger: this.trigger.bind(this),
          position: this.position,
          frontPosition: this.frontPosition,
          ui: this.ui,
          identifier: this.identifier,
          id: this.id,
          options: this.options
      };
  
      return this.instance;
  }
  
  Nipple.prototype = new Super();
  Nipple.constructor = Nipple;
  Nipple.id = 0;
  
  // Build the dom element of the Nipple instance.
  Nipple.prototype.buildEl = function (options) {
      this.ui = {};
  
      if (this.options.dataOnly) {
          return this;
      }
  
      this.ui.el = document.createElement('div');
      this.ui.back = document.createElement('div');
      this.ui.front = document.createElement('div');
  
      this.ui.el.className = 'nipple collection_' + this.collection.id;
      this.ui.back.className = 'back';
      this.ui.front.className = 'front';
  
      this.ui.el.setAttribute('id', 'nipple_' + this.collection.id +
          '_' + this.id);
  
      this.ui.el.appendChild(this.ui.back);
      this.ui.el.appendChild(this.ui.front);
  
      return this;
  };
  
  // Apply CSS to the Nipple instance.
  Nipple.prototype.stylize = function () {
      if (this.options.dataOnly) {
          return this;
      }
      var animTime = this.options.fadeTime + 'ms';
      var borderStyle = u.getVendorStyle('borderRadius', '50%');
      var transitStyle = u.getTransitionStyle('transition', 'opacity', animTime);
      var styles = {};
      styles.el = {
          position: 'absolute',
          opacity: this.options.restOpacity,
          display: 'block',
          'zIndex': 999
      };
  
      styles.back = {
          position: 'absolute',
          display: 'block',
          width: this.options.size.val + this.options.size.unit ,
          height: this.options.size.val + this.options.size.unit,
          marginLeft: -this.options.size.val / 2 + this.options.size.unit,
          marginTop: -this.options.size.val / 2 + this.options.size.unit,
          background: this.options.color,
          'opacity': '.5'
      };
  
      styles.front = {
          width: this.options.size.val / 2 + this.options.size.unit,
          height: this.options.size.val / 2 + this.options.size.unit,
          position: 'absolute',
          display: 'block',
          marginLeft: -this.options.size.val / 4 + this.options.size.unit,
          marginTop: -this.options.size.val / 4 + this.options.size.unit,
          background: this.options.color,
          'opacity': '.5'
      };
  
      u.extend(styles.el, transitStyle);
      u.extend(styles.back, borderStyle);
      u.extend(styles.front, borderStyle);
  
      this.applyStyles(styles);
  
      return this;
  };
  
  Nipple.prototype.applyStyles = function (styles) {
      // Apply styles
      for (var i in this.ui) {
          if (this.ui.hasOwnProperty(i)) {
              for (var j in styles[i]) {
                  this.ui[i].style[j] = styles[i][j];
              }
          }
      }
  
      return this;
  };
  
  // Inject the Nipple instance into DOM.
  Nipple.prototype.addToDom = function () {
      // We're not adding it if we're dataOnly or already in dom.
      if (this.options.dataOnly || document.body.contains(this.ui.el)) {
          return this;
      }
      this.options.zone.appendChild(this.ui.el);
      return this;
  };
  
  // Remove the Nipple instance from DOM.
  Nipple.prototype.removeFromDom = function () {
      if (this.options.dataOnly || !document.body.contains(this.ui.el)) {
          return this;
      }
      this.options.zone.removeChild(this.ui.el);
      return this;
  };
  
  // Entirely destroy this nipple
  Nipple.prototype.destroy = function () {
      clearTimeout(this.removeTimeout);
      clearTimeout(this.showTimeout);
      clearTimeout(this.restTimeout);
      this.trigger('destroyed', this.instance);
      this.removeFromDom();
      this.off();
  };
  
  // Fade in the Nipple instance.
  Nipple.prototype.show = function (cb) {
      var self = this;
  
      if (self.options.dataOnly) {
          return self;
      }
  
      clearTimeout(self.removeTimeout);
      clearTimeout(self.showTimeout);
      clearTimeout(self.restTimeout);
  
      self.addToDom();
  
      self.restCallback();
  
      setTimeout(function () {
          self.ui.el.style.opacity = 1;
      }, 0);
  
      self.showTimeout = setTimeout(function () {
          self.trigger('shown', self.instance);
          if (typeof cb === 'function') {
              cb.call(this);
          }
      }, self.options.fadeTime);
  
      return self;
  };
  
  // Fade out the Nipple instance.
  Nipple.prototype.hide = function (cb) {
      var self = this;
  
      if (self.options.dataOnly) {
          return self;
      }
  
      self.ui.el.style.opacity = self.options.restOpacity;
  
      clearTimeout(self.removeTimeout);
      clearTimeout(self.showTimeout);
      clearTimeout(self.restTimeout);
  
      self.removeTimeout = setTimeout(
          function () {
              var display = self.options.mode === 'dynamic' ? 'none' : 'block';
              self.ui.el.style.display = display;
              if (typeof cb === 'function') {
                  cb.call(self);
              }
  
              self.trigger('hidden', self.instance);
          },
          self.options.fadeTime
      );
      if (self.options.restJoystick) {
          self.restPosition();
      }
  
      return self;
  };
  
  Nipple.prototype.restPosition = function (cb) {
      var self = this;
      self.frontPosition = {
          x: 0,
          y: 0
      };
      var animTime = self.options.fadeTime + 'ms';
  
      var transitStyle = {};
      transitStyle.front = u.getTransitionStyle('transition',
          ['top', 'left'], animTime);
  
      var styles = {front: {}};
      styles.front = {
          left: self.frontPosition.x + 'px',
          top: self.frontPosition.y + 'px'
      };
  
      self.applyStyles(transitStyle);
      self.applyStyles(styles);
  
      self.restTimeout = setTimeout(
          function () {
              if (typeof cb === 'function') {
                  cb.call(self);
              }
              self.restCallback();
          },
          self.options.fadeTime
      );
  };
  
  Nipple.prototype.restCallback = function () {
      var self = this;
      var transitStyle = {};
      transitStyle.front = u.getTransitionStyle('transition', 'none', '');
      self.applyStyles(transitStyle);
      self.trigger('rested', self.instance);
  };
  
  Nipple.prototype.resetDirection = function () {
      // Fully rebuild the object to let the iteration possible.
      this.direction = {
          x: false,
          y: false,
          angle: false
      };
  };
  
  Nipple.prototype.computeDirection = function (obj) {
      var rAngle = obj.angle.radian;
      var angle45 = Math.PI / 4;
      var angle90 = Math.PI / 2;
      var direction, directionX, directionY;
  
      // Angular direction
      //     \  UP /
      //      \   /
      // LEFT       RIGHT
      //      /   \
      //     /DOWN \
      //
      if (
          rAngle > angle45 &&
          rAngle < (angle45 * 3) &&
          !obj.lockX
      ) {
          direction = 'up';
      } else if (
          rAngle > -angle45 &&
          rAngle <= angle45 &&
          !obj.lockY
      ) {
          direction = 'left';
      } else if (
          rAngle > (-angle45 * 3) &&
          rAngle <= -angle45 &&
          !obj.lockX
      ) {
          direction = 'down';
      } else if (!obj.lockY) {
          direction = 'right';
      }
  
      // Plain direction
      //    UP                 |
      // _______               | RIGHT
      //                  LEFT |
      //   DOWN                |
      if (!obj.lockY) {
          if (rAngle > -angle90 && rAngle < angle90) {
              directionX = 'left';
          } else {
              directionX = 'right';
          }
      }
  
      if (!obj.lockX) {
          if (rAngle > 0) {
              directionY = 'up';
          } else {
              directionY = 'down';
          }
      }
  
      if (obj.force > this.options.threshold) {
          var oldDirection = {};
          var i;
          for (i in this.direction) {
              if (this.direction.hasOwnProperty(i)) {
                  oldDirection[i] = this.direction[i];
              }
          }
  
          var same = {};
  
          this.direction = {
              x: directionX,
              y: directionY,
              angle: direction
          };
  
          obj.direction = this.direction;
  
          for (i in oldDirection) {
              if (oldDirection[i] === this.direction[i]) {
                  same[i] = true;
              }
          }
  
          // If all 3 directions are the same, we don't trigger anything.
          if (same.x && same.y && same.angle) {
              return obj;
          }
  
          if (!same.x || !same.y) {
              this.trigger('plain', obj);
          }
  
          if (!same.x) {
              this.trigger('plain:' + directionX, obj);
          }
  
          if (!same.y) {
              this.trigger('plain:' + directionY, obj);
          }
  
          if (!same.angle) {
              this.trigger('dir dir:' + direction, obj);
          }
      } else {
          this.resetDirection();
      }
  
      return obj;
  };
  
  export default Nipple;
  