import CanvasLayerRenderer from"./Layer.js";import DataTile,{asImageLike}from"../../DataTile.js";import ImageTile from"../../ImageTile.js";import LRUCache from"../../structs/LRUCache.js";import ReprojDataTile from"../../reproj/DataTile.js";import ReprojTile from"../../reproj/Tile.js";import TileRange from"../../TileRange.js";import TileState from"../../TileState.js";import{apply as applyTransform,compose as composeTransform}from"../../transform.js";import{ascending}from"../../array.js";import{containsCoordinate,createEmpty,equals,getIntersection,getTopLeft,intersects}from"../../extent.js";import{createOrUpdate as createTileCoord,getKeyZXY}from"../../tilecoord.js";import{fromUserExtent}from"../../proj.js";import{getUid}from"../../util.js";import{toSize}from"../../size.js";function getCacheKey(e,t,r,i){return e+","+getKeyZXY(t,r,i)}function addTileToLookup(e,t,r){if(!(r in e))return e[r]=new Set([t]),!0;const i=e[r];e=i.has(t);return e||i.add(t),!e}function removeTileFromLookup(e,t,r){const i=e[r];return!!i&&i.delete(t)}function getRenderExtent(e,t){const r=e.layerStatesArray[e.layerIndex],i=(r.extent&&(t=getIntersection(t,fromUserExtent(r.extent,e.viewState.projection))),r.layer.getRenderSource());return i.getWrapX()||(e=i.getTileGridForProjection(e.viewState.projection).getExtent())&&(t=getIntersection(t,e)),t}class CanvasTileLayerRenderer extends CanvasLayerRenderer{constructor(e,t){super(e),t=t||{},this.extentChanged=!0,this.renderComplete=!1,this.renderedExtent_=null,this.renderedPixelRatio,this.renderedProjection=null,this.renderedRevision,this.renderedTiles=[],this.renderedSourceKey_,this.renderedSourceRevision_,this.tempExtent=createEmpty(),this.tempTileRange_=new TileRange(0,0,0,0),this.tempTileCoord_=createTileCoord(0,0,0);e=void 0!==t.cacheSize?t.cacheSize:512;this.tileCache_=new LRUCache(e),this.maxStaleKeys=.5*e}getTileCache(){return this.tileCache_}getOrCreateTile(e,t,r,i){const o=this.tileCache_,n=this.getLayer(),a=n.getSource();var s=getCacheKey(a.getKey(),e,t,r);let l;if(o.containsKey(s))l=o.get(s);else{if(!(l=a.getTile(e,t,r,i.pixelRatio,i.viewState.projection)))return null;o.set(s,l)}return l}getTile(e,t,r,i){e=this.getOrCreateTile(e,t,r,i);return e||null}getData(e){var r=this.frameState;if(!r)return null;const t=this.getLayer();var i=applyTransform(r.pixelToCoordinateTransform,e.slice()),e=t.getExtent();if(e&&!containsCoordinate(e,i))return null;var o=r.viewState;const n=t.getRenderSource(),a=n.getTileGridForProjection(o.projection);var s=n.getTilePixelRatio(r.pixelRatio);for(let t=a.getZForResolution(o.resolution);t>=a.getMinZoom();--t){var l=a.getTileCoordForCoordAndZ(i,t);const u=this.getTile(t,l[1],l[2],r);if(u&&u.getState()===TileState.LOADED){var d=a.getOrigin(t),g=toSize(a.getTileSize(t)),h=a.getResolution(t);let e;if(u instanceof ImageTile)e=u.getImage();else{if(!(u instanceof DataTile))continue;if(!(e=asImageLike(u.getData())))continue}var c=Math.floor(s*((i[0]-d[0])/h-l[1]*g[0])),d=Math.floor(s*((d[1]-i[1])/h-l[2]*g[1])),h=Math.round(s*n.getGutterForProjection(o.projection));return this.getImageData(e,c+h,d+h)}}return null}prepareFrame(e){const t=this.getLayer().getSource();if(!t)return!1;var r=this.getLayer().getSource().getRevision();return this.renderedRevision_?this.renderedRevision_!==r&&(this.renderedRevision_=r,this.renderedSourceKey_===t.getKey()&&this.tileCache_.clear()):this.renderedRevision_=r,!0}enqueueTiles(i,e,t,o,r){var n=i.viewState;const a=this.getLayer(),s=a.getRenderSource(),l=s.getTileGridForProjection(n.projection);var d=getUid(s);d in i.wantedTiles||(i.wantedTiles[d]={});const g=i.wantedTiles[d],h=a.getMapInternal();var c=Math.max(t-r,l.getMinZoom(),l.getZForResolution(Math.min(a.getMaxResolution(),h?h.getView().getResolutionForZoom(Math.max(a.getMinZoom(),0)):l.getResolution(0)),s.zDirection));for(let r=t;r>=c;--r){var u,T=l.getTileRangeForExtentAndZ(e,r,this.tempTileRange_),m=l.getResolution(r);for(let t=T.minX;t<=T.maxX;++t)for(let e=T.minY;e<=T.maxY;++e){const p=this.getTile(r,t,e,i);p&&addTileToLookup(o,p,r)&&(u=p.getKey(),g[u]=!0,p.getState()!==TileState.IDLE||i.tileQueue.isKeyQueued(u)||(u=createTileCoord(r,t,e,this.tempTileCoord_),i.tileQueue.enqueue([p,d,l.getTileCoordCenter(u),m])))}}}findStaleTile_(e,t){const r=this.tileCache_;var i=e[0],o=e[1],n=e[2],a=this.getStaleKeys();for(let e=0;e<a.length;++e){var s=getCacheKey(a[e],i,o,n);if(r.containsKey(s)){const l=r.get(s);if(l.getState()===TileState.LOADED)return l.endTransition(getUid(this)),addTileToLookup(t,l,i),!0}}return!1}findAltTiles_(e,t,i,o){var n=e.getTileRangeForTileCoordAndZ(t,i,this.tempTileRange_);if(!n)return!1;let a=!0;const s=this.tileCache_,r=this.getLayer().getRenderSource();var l=r.getKey();for(let r=n.minX;r<=n.maxX;++r)for(let t=n.minY;t<=n.maxY;++t){var d=getCacheKey(l,i,r,t);let e=!1;if(s.containsKey(d)){const g=s.get(d);g.getState()===TileState.LOADED&&(addTileToLookup(o,g,i),e=!0)}e||(a=!1)}return a}renderFrame(t,e){this.renderComplete=!0;var r=t.layerStatesArray[t.layerIndex],i=t.viewState,o=i.projection,P=i.resolution,n=i.center,a=t.pixelRatio;const Z=this.getLayer(),s=Z.getSource();var k=s.getRevision();const l=s.getTileGridForProjection(o),d=l.getZForResolution(P,s.zDirection);var g=l.getResolution(d),h=s.getKey();this.renderedSourceKey_?this.renderedSourceKey_!==h&&(this.prependStaleKey(this.renderedSourceKey_),this.renderedSourceKey_=h):this.renderedSourceKey_=h;let c=t.extent;var u=s.getTilePixelRatio(a),h=(this.prepareContainer(t,e),this.context.canvas.width),e=this.context.canvas.height,z=r.extent&&fromUserExtent(r.extent,o);z&&(c=getIntersection(c,fromUserExtent(r.extent,o)));const T=g*h/2/u,m=g*e/2/u;var p=[n[0]-T,n[1]-m,n[0]+T,n[1]+m];const f={},v=Z.getPreload(),O=(t.nextExtent&&(n=l.getZForResolution(i.nextResolution,s.zDirection),i=getRenderExtent(t,t.nextExtent),this.enqueueTiles(t,i,n,f,v)),getRenderExtent(t,c));this.enqueueTiles(t,O,d,f,0),0<v&&setTimeout(()=>{this.enqueueTiles(t,O,d-1,f,v-1)},0);var b=getUid(this),q=t.time;for(const A of f[d]){var x=A.getState();if(!(A instanceof ReprojTile||A instanceof ReprojDataTile)||x!==TileState.EMPTY){var y=A.tileCoord;if(x===TileState.LOADED)if(1===A.getAlpha(b,q)){A.endTransition(b);continue}if(this.renderComplete=!1,this.findStaleTile_(y,f))removeTileFromLookup(f,A,d),t.animate=!0;else{x=this.findAltTiles_(l,y,d+1,f);if(!x){var X=l.getMinZoom();for(let e=d-1;e>=X;--e)if(this.findAltTiles_(l,y,e,f))break}}}}var R,S=g/P*a/u;const C=this.getRenderContext(t),j=(composeTransform(this.tempTransform,h/2,e/2,S,S,0,-h/2,-e/2),r.extent&&this.clipUnrotated(C,t,z),s.getInterpolate()||(C.imageSmoothingEnabled=!1),this.preRender(C,t),this.renderedTiles.length=0,Object.keys(f).map(Number)),Y=(j.sort(ascending),[]),G=[];for(let e=j.length-1;0<=e;--e){var _=j[e],L=s.getTilePixelSize(_,a,o),Q=l.getResolution(_)/g;const T=L[0]*Q*S,m=L[1]*Q*S;var W=l.getTileCoordForCoordAndZ(getTopLeft(p),_),L=l.getTileCoordExtent(W),E=applyTransform(this.tempTransform,[u*(L[0]-p[0])/g,u*(p[3]-L[3])/g]),N=u*s.getGutterForProjection(o);for(const U of f[_])if(U.getState()===TileState.LOADED){var K,D=U.tileCoord,V=W[1]-D[1],B=Math.round(E[0]-(V-1)*T),D=W[2]-D[2],H=Math.round(E[1]-(D-1)*m),I=Math.round(E[0]-V*T),w=Math.round(E[1]-D*m),F=B-I,M=H-w,J=1===j.length;let r=!1;R=[I,w,I+F,w,I+F,w+M,I,w+M];for(let e=0,t=Y.length;e<t;++e)!J&&_<G[e]&&(K=Y[e],intersects([I,w,I+F,w+M],[K[0],K[3],K[4],K[7]])&&(r||(C.save(),r=!0),C.beginPath(),C.moveTo(R[0],R[1]),C.lineTo(R[2],R[3]),C.lineTo(R[4],R[5]),C.lineTo(R[6],R[7]),C.moveTo(K[6],K[7]),C.lineTo(K[4],K[5]),C.lineTo(K[2],K[3]),C.lineTo(K[0],K[1]),C.clip()));Y.push(R),G.push(_),this.drawTile(U,t,I,w,F,M,N,J),r&&C.restore(),this.renderedTiles.unshift(U),this.updateUsedTiles(t.usedTiles,s,U)}}this.renderedRevision=k,this.renderedResolution=g,this.extentChanged=!this.renderedExtent_||!equals(this.renderedExtent_,p),this.renderedExtent_=p,this.renderedPixelRatio=a,this.renderedProjection=o,this.postRender(this.context,t),r.extent&&C.restore(),C.imageSmoothingEnabled=!0;return t.postRenderFunctions.push((e,t)=>{var r=getUid(s),t=t.wantedTiles[r],r=t?Object.keys(t).length:0;this.updateCacheSize(r),this.tileCache_.expireCache()}),this.container}updateCacheSize(e){this.tileCache_.highWaterMark=Math.max(this.tileCache_.highWaterMark,2*e)}drawTile(e,t,r,i,o,n,a,s){let l;if(e instanceof DataTile){if(!(l=asImageLike(e.getData())))throw new Error("Rendering array data is not yet supported")}else l=this.getTileImage(e);if(l){const u=this.getRenderContext(t);var d=getUid(this),g=t.layerStatesArray[t.layerIndex],h=g.opacity*(s?e.getAlpha(d,t.time):1),c=h!==u.globalAlpha;c&&(u.save(),u.globalAlpha=h),u.drawImage(l,a,a,l.width-2*a,l.height-2*a,r,i,o,n),c&&u.restore(),h!==g.opacity?t.animate=!0:s&&e.endTransition(d)}}getImage(){var e=this.context;return e?e.canvas:null}getTileImage(e){return e.getImage()}updateUsedTiles(e,t,r){t=getUid(t);t in e||(e[t]={}),e[t][r.getKey()]=!0}}export default CanvasTileLayerRenderer;