cordova.define("cordova-plugin-googlemaps.HtmlInfoWindow", function(require, exports, module) {

var utils = require('cordova/utils'),
  event = require('./event'),
  BaseClass = require('./BaseClass');

/*****************************************************************************
 * HTMLInfoWindow Class
 *****************************************************************************/
var HTMLInfoWindow = function () {
  var self = this;
  BaseClass.apply(self);
  var callbackTable = {};
  var listenerMgr = {
    one: function (target, eventName, callback) {
      callbackTable[target.hashCode] = callbackTable[target.hashCode] || {};
      callbackTable[target.hashCode][eventName] = callbackTable[target.hashCode][eventName] || [];
      callbackTable[target.hashCode][eventName].push(callback);
      target.one.call(target, eventName, callback);
    },
    on: function (target, eventName, callback) {
      callbackTable[target.hashCode] = callbackTable[target.hashCode] || {};
      callbackTable[target.hashCode][eventName] = callbackTable[target.hashCode][eventName] || [];
      callbackTable[target.hashCode][eventName].push(callback);
      target.on.call(target, eventName, callback);
    },
    bindTo: function (srcObj, srcField, dstObj, dstField, noNotify) {
      var eventName = srcField + '_changed';
      dstField = dstField || srcField;
      var callback = function (oldValue, newValue) {
        dstObj.set(dstField, newValue, noNotify);
      };
      callbackTable[dstObj.hashCode] = callbackTable[dstObj.hashCode] || {};
      callbackTable[dstObj.hashCode][eventName] = callbackTable[dstObj.hashCode][eventName] || [];
      callbackTable[dstObj.hashCode][eventName].push(callback);
      srcObj.on.call(srcObj, eventName, callback);
    },
    off: function (target, eventName) {
      if (!target || !eventName ||
        !(target.hashCode in callbackTable) ||
        !(eventName in callbackTable[target.hashCode])) {
        return;
      }
      callbackTable[target.hashCode][eventName].forEach(function (listener) {
        target.off.call(target, eventName, listener);
      });
      delete callbackTable[target.hashCode][eventName];
    }
  };

  Object.defineProperty(self, '_hook', {
    value: listenerMgr,
    writable: false
  });

  var frame = document.createElement('div');
  frame.style.overflow = 'visible';
  frame.style.position = 'absolute';
  frame.style.display = 'inline-block';
  frame.classList.add('pgm-html-info-frame');
  self.set('frame', frame);

  var anchorDiv = document.createElement('div');
  anchorDiv.setAttribute('class', 'pgm-anchor');
  anchorDiv.style.overflow = 'visible';
  anchorDiv.style.position = 'absolute';
  anchorDiv.style['z-index'] = 0;
  anchorDiv.style.width = '0 !important';
  anchorDiv.style.height = '0 !important';
  //anchorDiv.style.border = '1px solid green';
  //anchorDiv.style.backgroundColor = 'rgba(125, 125, 255, 0.5)';

  anchorDiv.style.transition = 'transform 0s ease';
  anchorDiv.style['will-change'] = 'transform';

  anchorDiv.style['-webkit-backface-visibility'] = 'hidden';
  anchorDiv.style['-webkit-perspective'] = 1000;
  anchorDiv.style['-webkit-transition'] = '-webkit-transform 0s ease';

  anchorDiv.appendChild(frame);
  self.set('anchor', anchorDiv);

  var contentBox = document.createElement('div');
  contentBox.style.display = 'inline-block';
  contentBox.style.padding = '5px';
  contentBox.style.boxSizing = 'content-box';
  contentBox.classList.add('pgm-html-info-content-box');

  var contentFrame = document.createElement('div');
  contentFrame.style.display = 'block';
  contentFrame.style.position = 'relative';
  contentFrame.style.backgroundColor = 'white';
  contentFrame.style.border = '1px solid rgb(204, 204, 204)';
  contentFrame.style.left = '0px';
  contentFrame.style.right = '0px';
  contentFrame.style.zIndex = '1'; // In order to set higher depth than the map div certainly
  contentFrame.style.overflow = 'hidden';
  contentFrame.classList.add('pgm-html-info-content-frame');
  frame.appendChild(contentFrame);
  contentFrame.appendChild(contentBox);

  var tailFrame = document.createElement('div');
  tailFrame.style.position = 'relative';
  tailFrame.style.top = '-1px';
  tailFrame.style.zIndex = 100;
  tailFrame.classList.add('pgm-html-info-tail-frame');
  frame.appendChild(tailFrame);

  var tailLeft = document.createElement('div');
  /*
  tailLeft.style.position = 'absolute';
  tailLeft.style.marginLeft = '-15px';
  tailLeft.style.left = '50%';
  tailLeft.style.top = '0px';
  tailLeft.style.height = '15px';
  tailLeft.style.width = '16px';
  tailLeft.style.overflow = 'hidden';
  tailLeft.style.borderWidth = '0px';
  */
  tailLeft.classList.add('pgm-html-info-tail-left');

  tailLeft.style.position = 'absolute';
  tailLeft.style.left = '50%';
  tailLeft.style.height = '0px';
  tailLeft.style.width = '0px';
  tailLeft.style.marginLeft = '-15px';
  tailLeft.style.borderWidth = '15px 15px 0px';
  tailLeft.style.borderColor = 'rgb(204, 204, 204) transparent transparent';
  tailLeft.style.borderStyle = 'solid';
  tailFrame.appendChild(tailLeft);

  /*
  var tailLeftCover = document.createElement('div');
  tailLeftCover.style.position = 'absolute';
  tailLeftCover.style.backgroundColor = 'white';
  tailLeftCover.style.transform = 'skewX(45deg)';
  tailLeftCover.style.transformOrigin = '0px 0px 0px';
  tailLeftCover.style.left = '0px';
  tailLeftCover.style.height = '15px';
  tailLeftCover.style.width = '15px';
  tailLeftCover.style.top = '0px';
  tailLeftCover.style.zIndex = 1;
  tailLeftCover.style.borderLeft = '1px solid rgb(204, 204, 204)';
  tailLeft.classList.add('pgm-html-info-tail-left-cover');
  tailLeft.appendChild(tailLeftCover);
  */

  var tailRight = document.createElement('div');
  /*
  tailRight.style.position = 'absolute';
  tailRight.style.left = '50%';
  tailRight.style.top = '0px';
  tailRight.style.height = '15px';
  tailRight.style.width = '16px';
  tailRight.style.overflow = 'hidden';
  tailRight.style.borderWidth = '0px';
  */
  tailRight.style.position = 'absolute';
  tailRight.style.left = '50%';
  tailRight.style.height = '0px';
  tailRight.style.width = '0px';
  tailRight.style.marginLeft = '-14px';
  tailRight.style.borderTopWidth = '14px';
  tailRight.style.borderLeftWidth = '14px';
  tailRight.style.borderRightWidth = '14px';
  tailRight.style.borderColor = 'rgb(255, 255, 255) transparent transparent';
  tailRight.style.borderStyle = 'solid';
  tailRight.classList.add('pgm-html-info-tail-right');
  tailFrame.appendChild(tailRight);
  /*
      var tailRightCover = document.createElement('div');
      tailRightCover.style.position = 'absolute';
      tailRightCover.style.backgroundColor = 'white';
      tailRightCover.style.transform = 'skewX(-45deg)';
      tailRightCover.style.transformOrigin = '0px 0px 0px';
      tailRightCover.style.left = '0px';
      tailRightCover.style.height = '15px';
      tailRightCover.style.width = '15px';
      tailRightCover.style.top = '0px';
      tailRightCover.style.zIndex = 2;
      tailRightCover.style.borderRight = '1px solid rgb(204, 204, 204)';
      tailRightCover.classList.add('pgm-html-info-tail-right-cover');
      tailRight.appendChild(tailRightCover);
  */
  var eraseBorder = document.createElement('div');
  eraseBorder.style.position = 'absolute';
  eraseBorder.style.zIndex = 3;
  eraseBorder.style.backgroundColor = 'white';
  eraseBorder.style.width = '27px';
  eraseBorder.style.height = '2px';
  eraseBorder.style.top = '-1px';
  eraseBorder.style.left = '50%';
  eraseBorder.style.marginLeft = '-13px';
  eraseBorder.classList.add('pgm-html-info-tail-erase-border');
  tailFrame.appendChild(eraseBorder);

  var calculate = function (marker) {

    //var marker = self.get('marker');
    var map = marker.getMap();

    var div = map.getDiv();

    var frame = self.get('frame');
    var contentFrame = frame.firstChild;
    var contentBox = contentFrame.firstChild;
    contentBox.style.minHeight = '50px';
    contentBox.style.width = 'auto';
    contentBox.style.height = 'auto';
    contentBox.style.padding = '5px';

    var content = self.get('content');
    if (typeof content === 'string') {
      contentBox.style.whiteSpace = 'pre-wrap';
      contentBox.innerHTML = content;
    } else {
      if (!content) {
        contentBox.innerText = '';
      } else if (content.nodeType === 1) {
        contentBox.innerHTML = '';
        contentBox.appendChild(content);
      } else {
        contentBox.innerText = content;
      }
    }

    var cssOptions = self.get('cssOptions');
    if (cssOptions && typeof cssOptions === 'object') {
      var keys = Object.keys(cssOptions);
      keys.forEach(function (key) {
        contentBox.style.setProperty(key, cssOptions[key]);
      });
    }
    // Insert the contents to this HTMLInfoWindow
    if (!anchorDiv.parentNode) {
      map._layers.info.appendChild(anchorDiv);
    }

    // Adjust the HTMLInfoWindow size
    var contentsWidth = contentBox.offsetWidth + 10; // padding 5px x 2
    var contentsHeight = contentBox.offsetHeight;
    self.set('contentsHeight', contentsHeight);
    contentFrame.style.width = contentsWidth + 'px';
    contentFrame.style.height = contentsHeight + 'px';
    frame.style.width = contentsWidth + 'px';
    frame.style.height = (contentsHeight + 15) + 'px';

    if (contentBox.offsetWidth > div.offsetWidth * 0.9) {
      contentBox.style.width = (div.offsetWidth * 0.9) + 'px';
    }
    contentBox.style.width = '100%';
    contentBox.style.height = '100%';
    contentBox.style.padding = '5px 17px 17px 5px';
    self.set('contentsWidth', contentsWidth);

    var infoOffset = {
      x: 31,
      y: 31
    };
    var iconSize = {
      width: 62,
      height: 110
    };

    // If there is no specification with `anchor` property,
    // the values {x: 0.5, y: 1} are specified by native APIs.
    // For the case, anchor values are {x: 0} in JS.
    var anchor = {
      x: 15,
      y: 15
    };

    var icon = marker.get('icon');

    if (typeof icon === 'object') {
      if (typeof icon.url === 'string' && icon.url.indexOf('data:image/') === 0) {
        var img = document.createElement('img');
        img.src = icon.url;
        iconSize.width = img.width;
        iconSize.height = img.height;
      }
      if (typeof icon.size === 'object') {
        iconSize.width = icon.size.width;
        iconSize.height = icon.size.height;
      }

      if (Array.isArray(icon.anchor)) {
        anchor.x = icon.anchor[0];
        anchor.y = icon.anchor[1];
      }
    }

    var infoWindowAnchor = marker.get('infoWindowAnchor');
    if (utils.isArray(infoWindowAnchor)) {
      infoOffset.x = infoWindowAnchor[0];
      infoOffset.y = infoWindowAnchor[1];
    }
    infoOffset.x = infoOffset.x / iconSize.width;
    infoOffset.x = infoOffset.x > 1 ? 1 : infoOffset.x;
    infoOffset.x = infoOffset.x < 0 ? 0 : infoOffset.x;
    infoOffset.y = infoOffset.y / iconSize.height;
    infoOffset.y = infoOffset.y > 1 ? 1 : infoOffset.y;
    infoOffset.y = infoOffset.y < 0 ? 0 : infoOffset.y;
    infoOffset.y *= iconSize.height;
    infoOffset.x *= iconSize.width;

    anchor.x = anchor.x / iconSize.width;
    anchor.x = anchor.x > 1 ? 1 : anchor.x;
    anchor.x = anchor.x < 0 ? 0 : anchor.x;
    anchor.y = anchor.y / iconSize.height;
    anchor.y = anchor.y > 1 ? 1 : anchor.y;
    anchor.y = anchor.y < 0 ? 0 : anchor.y;
    anchor.y *= iconSize.height;
    anchor.x *= iconSize.width;

    //console.log('contentsSize = ' + contentsWidth + ', ' + contentsHeight);
    //console.log('iconSize = ' + iconSize.width + ', ' + iconSize.height);
    //console.log('infoOffset = ' + infoOffset.x + ', ' + infoOffset.y);

    //var frameBorder = parseInt(common.getStyle(contentFrame, 'border-left-width').replace(/[^\d]/g, ''), 10);
    //var offsetX = (contentsWidth + frameBorder + anchor.x ) * 0.5 + (iconSize.width / 2  - infoOffset.x);
    //var offsetY = contentsHeight + anchor.y - (frameBorder * 2) - infoOffset.y + 15;
    var offsetX = -(iconSize.width / 2) - (cordova.platformId === 'android' ? 1 : 0);
    var offsetY = -iconSize.height - (cordova.platformId === 'android' ? 1 : 0);
    anchorDiv.style.width = iconSize.width + 'px';
    anchorDiv.style.height = iconSize.height + 'px';

    self.set('offsetX', offsetX);
    self.set('offsetY', offsetY);

    frame.style.bottom = (iconSize.height - infoOffset.y) + 'px';
    frame.style.left = ((-contentsWidth) / 2 + infoOffset.x) + 'px';

    //console.log('frameLeft = ' + frame.style.left );
    var point = map.get('infoPosition');
    anchorDiv.style.visibility = 'hidden';
    var x = point.x + self.get('offsetX');
    var y = point.y + self.get('offsetY');
    anchorDiv.style['-webkit-transform'] = 'translate3d(' + x + 'px, ' + y + 'px, 0px)';
    anchorDiv.style.transform = 'translate3d(' + x + 'px, ' + y + 'px, 0px)';
    anchorDiv.style.visibility = 'visible';
    self.trigger('infoPosition_changed', '', point);
    self.trigger(event.INFO_OPEN);
  };

  self._hook.on(self, 'infoPosition_changed', function (ignore, point) {
    if (!point) return;
    var x = point.x + self.get('offsetX');
    var y = point.y + self.get('offsetY');
    anchorDiv.style['-webkit-transform'] = 'translate3d(' + x + 'px, ' + y + 'px, 0px)';
    anchorDiv.style.transform = 'translate3d(' + x + 'px, ' + y + 'px, 0px)';
  });

  self._hook.on(self, 'infoWindowAnchor_changed', calculate);

  self.set('isInfoWindowVisible', false);

};

utils.extend(HTMLInfoWindow, BaseClass);

HTMLInfoWindow.prototype.isInfoWindowShown = function () {
  return this.get('isInfoWindowVisible') === true;
};

HTMLInfoWindow.prototype.close = function () {
  var self = this;

  var marker = self.get('marker');
  if (marker) {
    self._hook.off(marker, 'isInfoWindowVisible_changed');
  }
  if (!self.isInfoWindowShown() || !marker) {
    return;
  }
  self.set('isInfoWindowVisible', false);
  marker.set('isInfoWindowVisible', false);
  marker.set('infoWindow', undefined);
  this.set('marker', undefined);

  var map = marker.getMap();
  self._hook.off(marker.getMap(), 'map_clear');
  self._hook.off(marker, 'infoPosition_changed');
  self._hook.off(marker, 'icon_changed');
  //self._hook.off(self, 'infoWindowAnchor_changed');
  self._hook.off(marker, event.INFO_CLOSE); //This event listener is assigned in the open method. So detach it.
  self.trigger(event.INFO_CLOSE);
  map.set('active_marker', null);

  //var div = map.getDiv();
  var anchorDiv = self.get('anchor');
  if (anchorDiv && anchorDiv.parentNode) {
    anchorDiv.parentNode.removeChild(anchorDiv);

    // Remove the contents from this HTMLInfoWindow
    var contentFrame = anchorDiv.firstChild.firstChild;
    var contentBox = contentFrame.firstChild;
    contentBox.innerHTML = '';
  }
};

HTMLInfoWindow.prototype.setContent = function (content, cssOptions) {
  var self = this;
  var prevContent = self.get('content');
  self.set('content', content);
  self.set('cssOptions', cssOptions);
  var marker = self.get('marker');
  if (content !== prevContent && marker && marker.isInfoWindowShown()) {
    var anchorDiv = self.get('anchor');
    if (anchorDiv) {
      anchorDiv.style.width = '0 !important';
      anchorDiv.style.height = '0 !important';
      if (anchorDiv.parentNode) {
        anchorDiv.parentNode.removeChild(anchorDiv);

        // Remove the contents from this HTMLInfoWindow
        var contentFrame = anchorDiv.firstChild.firstChild;
        var contentBox = contentFrame.firstChild;
        contentBox.innerHTML = '';
      }
    }
    self.trigger('infoWindowAnchor_changed', marker);
  }
};

HTMLInfoWindow.prototype.open = function (marker) {
  if (!marker) {
    return;
  }
  if (marker._objectInstance) {
    // marker is an instance of the ionic-native wrapper plugin.
    marker = marker._objectInstance;
  }

  var self = this,
    map = marker.getMap();

  marker.set('infoWindow', self);
  marker.set('isInfoWindowVisible', true);
  self._hook.on(marker, 'icon_changed', function () {
    self.trigger.call(self, 'infoWindowAnchor_changed', marker);
  });
  self.set('isInfoWindowVisible', true);
  self._hook.on(marker, 'isInfoWindowVisible_changed', function (prevValue, newValue) {
    if (newValue === false) {
      self.close.call(self);
    }
  });

  map.fromLatLngToPoint(marker.getPosition(), function (point) {
    map.set('infoPosition', {
      x: point[0],
      y: point[1]
    });

    self._hook.bindTo(map, 'infoPosition', self);
    self._hook.bindTo(marker, 'infoWindowAnchor', self);
    self._hook.bindTo(marker, 'icon', self);
    self._hook.one(marker.getMap(), 'map_clear', self.close.bind(self));
    self._hook.one(marker, event.INFO_CLOSE, self.close.bind(self));
    self.set('marker', marker);
    map.set('active_marker', marker);
    self.trigger.call(self, 'infoWindowAnchor_changed', marker);
  });
};

HTMLInfoWindow.prototype.setBackgroundColor = function (backgroundColor) {
  this.get('frame').children[0].style.backgroundColor = backgroundColor;
  this.get('frame').children[1].children[0].style.borderColor = backgroundColor + ' rgba(0,0,0,0) rgba(0,0,0,0)';
  this.get('frame').children[1].children[1].style.borderColor = backgroundColor + ' rgba(0,0,0,0) rgba(0,0,0,0)';
  this.get('frame').children[1].children[2].style.backgroundColor = backgroundColor;
};

module.exports = HTMLInfoWindow;

});
