// http://a.amap.com/jsapi_demos/static/remogeo/remogeo.js

function RemoGeoLocation() {
  this._remoteSvrUrl = 'https://webapi.amap.com/html/geolocate.html';
  this._callbackList = [];
  this._seqBase = 1;
  this._frameReady = 0;
  this._watchIdMap = {};
}

RemoGeoLocation.prototype = {
  _getSeq: function() {
    return this._seqBase++;
  },
  _onRrameReady: function(callback) {
    if (this._frameReady === 0) {
      if (!this._frameReadyList) {
        this._frameReadyList = [];
      }
      this._frameReadyList.push(callback);
      this._prepareIframe();
      return;
    }

    callback.call(this);
  },
  _prepareIframe: function() {

    if (this._iframeWin) {
      return;
    }

    var ifrm = document.createElement('iframe');

    ifrm.src = this._remoteSvrUrl +
          (this._remoteSvrUrl.indexOf('?') > 0 ? '&' : '?');

    ifrm.width = '0px';
    ifrm.height = '0px';
    ifrm.style.position = 'absolute';
    ifrm.style.display = 'none';
    ifrm.allow = 'geolocation';

    var self = this;

    var timeoutId = setTimeout(function() {

      self._frameReady = false;

      self._callbackFrameReadyList();

    }, 5000);

    ifrm.onload = function() {

      clearTimeout(timeoutId);

      self._frameReady = true;

      self._callbackFrameReadyList();

      ifrm.onload = null;
    };

    document.body.appendChild(ifrm);

    this._iframeWin = ifrm.contentWindow;

    window.addEventListener('message', function(e) {

      if (self._remoteSvrUrl.indexOf(e['origin']) !== 0) {
        return;
      }

      self._handleRemoteMsg(e['data']);

    }, false);
  },
  _callbackFrameReadyList: function() {

    if (this._frameReadyList) {

      var list = this._frameReadyList;
      this._frameReadyList = null;

      for (var i = 0, len = list.length; i < len; i++) {
        list[i].call(this, this._frameReady);
      }
    }
  },
  _pickCallback: function(seqNum, keepInList) {

    var callbackList = this._callbackList;

    for (var i = 0, len = callbackList.length; i < len; i++) {

      var cbkInfo = callbackList[i];

      if (seqNum === cbkInfo.seq) {

        if (!keepInList) {
          callbackList.splice(i, 1);
        }

        return cbkInfo;
      }
    }
  },
  _handleRemoteMsg: function(msg) {

    var seqNum = msg['seq'];

    var cbkInfo = this._pickCallback(seqNum, !!msg['notify']);

    if (cbkInfo) {

      cbkInfo.cbk.call(null, msg['error'], msg['result']);

    } else {

      console.warn('Receive remote msg: ', msg);
    }

  },
  _postMessage: function(cmd, args, callback, seq) {

    this._prepareIframe();

    var msg = {
      'cmd': cmd,
      'args': args,
      'seq': seq || this._getSeq()
    };

    this._callbackList.push({
      cbk: callback,
      seq: msg['seq']
    });

    this._onRrameReady(function() {

      if (this._frameReady === true) {

        try {

          this._iframeWin.postMessage(msg, '*');

        } catch (e) {

          this._pickCallback(msg['seq']);

          callback(e);
        }
      } else {

        this._pickCallback(msg['seq']);

        callback({
          'message': 'iFrame load failed!'
        });
      }
    });
  },
  'getCurrentPosition': function(succHandler, errHandler, options) {

    this._postMessage('getCurrentPosition', [options], function(err, result) {

      if (err) {
        if (errHandler) {
          errHandler(err);
        }
        return;
      }
      if (succHandler) {
        succHandler(result);
      }
    });
  },
  'watchPosition': function(succHandler, errHandler, options) {

    var watchKey = 'wk' + this._getSeq();
    var cmdSeq = this._getSeq();

    this._watchIdMap[watchKey] = {
      stat: 0,
      seq: cmdSeq
    };

    var self = this;

    this._postMessage('watchPosition', [options], function(err, result) {

      var id = null;

      if (result) {
        id = result['id'];
      }

      var watchInfo = self._watchIdMap[watchKey];

      watchInfo.id = id;
      watchInfo.stat = 1;

      if (watchInfo.callbackList) {

        var list = watchInfo.callbackList;
        watchInfo.callbackList = null;

        for (var i = 0, len = list.length; i < len; i++) {
          list[i].call(self, id);
        }
      }

      if (err) {
        if (errHandler) {
          errHandler(err);
        }
        return;
      }

      if (succHandler) {
        succHandler(result['pos']);
      }

    }, cmdSeq);

    return watchKey;
  },
  'clearWatch': function(watchKey, callback) {

    if (!this._watchIdMap[watchKey]) {
      callback('Id not exists: ' + watchKey);
      return;
    }

    var watchInfo = this._watchIdMap[watchKey];

    var self = this;

    function clearId(id) {

      self._postMessage('clearWatch', [id], function(err, result) {

        if (!err) {

          self._pickCallback(watchInfo.seq);

          delete self._watchIdMap[watchKey];
        }

        if (callback) {
          callback(err, result);
        }

      });
    }

    if (watchInfo.stat < 1) {

      if (!watchInfo.callbackList) {
        watchInfo.callbackList = [];
      }

      watchInfo.callbackList.push(function(id) {
        clearId(id);
      });

    } else {
      clearId(watchInfo.id);
    }
  }
};

export default RemoGeoLocation;