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';

        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(),
            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);
        }
    }
};