/* MVT加载类 作者： 木遥（微信:  http://marsgis.cn/weixin.html ） */
(function (window) {

  function MvtImageryProvider (options) {
    options = Cesium.defaultValue(options, Cesium.defaultValue.EMPTY_OBJECT);
    this.options = options;

    this._tileWidth = Cesium.defaultValue(options.tileWidth, 512);
    this._tileHeight = Cesium.defaultValue(options.tileHeight, 512);
    this._minimumLevel = Cesium.defaultValue(options.minimumLevel, 0);
    this._maximumLevel = Cesium.defaultValue(options.maximumLevel, 18);


    if (options.rectangle && options.rectangle.xmin && options.rectangle.xmax && options.rectangle.ymin && options.rectangle.ymax) {
      var xmin = options.rectangle.xmin;
      var xmax = options.rectangle.xmax;
      var ymin = options.rectangle.ymin;
      var ymax = options.rectangle.ymax;
      options.rectangle = Cesium.Rectangle.fromDegrees(xmin, ymin, xmax, ymax);
    }
    this._tilingScheme = Cesium.defaultValue(options.tilingScheme, new Cesium.WebMercatorTilingScheme({ ellipsoid: options.ellipsoid }));
    this._rectangle = Cesium.defaultValue(options.rectangle, this._tilingScheme.rectangle);
    this._rectangle = Cesium.Rectangle.intersection(this._rectangle, this._tilingScheme.rectangle);
    this._hasAlphaChannel = Cesium.defaultValue(options.hasAlphaChannel, true);

    this._errorEvent = new Cesium.Event();
    this._readyPromise = Cesium.when.resolve(true);
    this._credit = undefined;
    this._ready = true;

    //mvt相关的处理
    if (!window.ol) {
      throw new DeveloperError('请引入Openlayers类库！');
    }
    this._ol = window.ol;
    this._mvtParser = new this._ol.format.MVT();

    switch (options.style) {
      case "mapbox-streets-v6":
        this._styleClass = new mars3d.MapboxStreetsV6()
        break;
      default:
        this._styleClass = options.style;
        break;

    }

    this._key = Cesium.defaultValue(options.key, "");
    this._url = Cesium.defaultValue(options.url, "");

    var sw = this._tilingScheme._rectangleSouthwestInMeters;
    var ne = this._tilingScheme._rectangleNortheastInMeters;
    var mapExtent = [sw.x, sw.y, ne.x, ne.y];
    this._resolutions = ol.tilegrid.resolutionsFromExtent(mapExtent, this._maximumLevel, this._tileWidth);

    this._pixelRatio = 1;
    this._transform = [0.125, 0, 0, 0.125, 0, 0];
    this._replays = ["Default", "Image", "Polygon", "LineString", "Text"];

    this._tileQueue = new Cesium.TileReplacementQueue();
    this._cacheSize = 1000;
  }

  Object.defineProperties(MvtImageryProvider.prototype, {
    proxy: {
      get: function () {
        return undefined;
      }
    },

    tileWidth: {
      get: function () {
        return this._tileWidth;
      }
    },

    tileHeight: {
      get: function () {
        return this._tileHeight;
      }
    },

    maximumLevel: {
      get: function () {
        return undefined;
      }
    },

    minimumLevel: {
      get: function () {
        return undefined;
      }
    },

    tilingScheme: {
      get: function () {
        return this._tilingScheme;
      }
    },

    rectangle: {
      get: function () {
        return this._tilingScheme.rectangle;
      }
    },

    tileDiscardPolicy: {
      get: function () {
        return undefined;
      }
    },

    errorEvent: {
      get: function () {
        return this._errorEvent;
      }
    },

    ready: {
      get: function () {
        return true;
      }
    },

    readyPromise: {
      get: function () {
        return this._readyPromise;
      }
    },

    credit: {
      get: function () {
        return undefined;
      }
    },

    hasAlphaChannel: {
      get: function () {
        return true;
      }
    }
  });

  MvtImageryProvider.prototype.getTileCredits = function (x, y, level) {
    return undefined;
  };

  MvtImageryProvider.prototype.pickFeatures = function (x, y, level, longitude, latitude) {
    return undefined;
  };

  MvtImageryProvider.prototype.requestImage = function (x, y, level, request) {
    var cacheTile = findTileInQueue(x, y, level, this._tileQueue);
    if (cacheTile != undefined) {
      return cacheTile;
    }
    else {
      var that = this;
      var url = this._url;

      var reverseY = this._tilingScheme.getNumberOfYTilesAtLevel(level) - y - 1;
      url = url.replace('{x}', x).replace('{y}', y).replace('{reverseY}', reverseY).replace('{z}', level).replace('{k}', this._key);

      var resource = Cesium.Resource.createIfNeeded(url);
      return resource.fetchArrayBuffer().then(function (arrayBuffer) {

        var canvas = document.createElement('canvas');
        canvas.width = that._tileWidth;
        canvas.height = that._tileHeight;
        var vectorContext = canvas.getContext('2d');

        var features = that._mvtParser.readFeatures(arrayBuffer);

        var styleFun = that._styleClass.getStyle();

        var extent = [0, 0, 4096, 4096];
        var _replayGroup = new ol.render.canvas.ReplayGroup(0, extent,
          8, true, 100);

        for (var i = 0; i < features.length; i++) {
          var feature = features[i];
          var styles = styleFun(features[i], that._resolutions[level]);
          for (var j = 0; j < styles.length; j++) {
            ol.renderer.vector.renderFeature_(_replayGroup, feature, styles[j], 16);
          }
        }
        _replayGroup.finish();

        _replayGroup.replay(vectorContext, that._pixelRatio, that._transform, 0, {}, that._replays, true);
        if (that._tileQueue.count > that._cacheSize) {
          trimTiles(that._tileQueue, that._cacheSize / 2);
        }

        canvas.xMvt = x;
        canvas.yMvt = y;
        canvas.zMvt = level;
        that._tileQueue.markTileRendered(canvas);

        delete _replayGroup;
        _replayGroup = null;

        return canvas;
      }).otherwise(function (error) {
        return undefined;
      });
    }
  };

  function findTileInQueue (x, y, level, tileQueue) {
    var item = tileQueue.head;
    while (item != undefined && !(item.xMvt == x && item.yMvt == y && item.zMvt == level)) {
      item = item.replacementNext;
    }
    return item;
  };

  function removeQueue (tileReplacementQueue, item) {
    var previous = item.replacementPrevious;
    var next = item.replacementNext;

    if (item === tileReplacementQueue._lastBeforeStartOfFrame) {
      tileReplacementQueue._lastBeforeStartOfFrame = next;
    }

    if (item === tileReplacementQueue.head) {
      tileReplacementQueue.head = next;
    } else {
      previous.replacementNext = next;
    }

    if (item === tileReplacementQueue.tail) {
      tileReplacementQueue.tail = previous;
    } else {
      next.replacementPrevious = previous;
    }

    item.replacementPrevious = undefined;
    item.replacementNext = undefined;

    --tileReplacementQueue.count;
  }

  function trimTiles (tileQueue, maximumTiles) {
    var tileToTrim = tileQueue.tail;
    while (tileQueue.count > maximumTiles &&
      Cesium.defined(tileToTrim)) {
      var previous = tileToTrim.replacementPrevious;

      removeQueue(tileQueue, tileToTrim);
      delete tileToTrim;
      tileToTrim = null;

      tileToTrim = previous;
    }
  };



  function createImageryProvider (options) {
    return new MvtImageryProvider(options)
  }

  class MvtLayer extends mars3d.layer.BaseTileLayer {
    //构建ImageryProvider
    _createImageryProvider (options, urltemplate) {
      return createImageryProvider(options, urltemplate)
    }
  }
  MvtLayer.createImageryProvider = createImageryProvider

  //注册下
  const layerType = 'mvt'//图层类型
  mars3d.layer.register(layerType, MvtLayer)
  mars3d.layer.registerImageryProvider(layerType, createImageryProvider)

  //对外接口
  window.mars3d.provider.MvtImageryProvider = MvtImageryProvider;
  window.mars3d.layer.MvtLayer = MvtLayer;

})(window);
