import fs from 'fs'; import zlib from 'zlib';
const [mapP, layP] = process.argv.slice(2);
const M = JSON.parse(fs.readFileSync(mapP)); const L = JSON.parse(fs.readFileSync(layP));
const fc0 = zlib.gunzipSync(Buffer.from(M.free_class,'base64'));
const {nx,ny,res} = M;
function down(fc,nx,ny,f){ const X=Math.floor(nx/f),Y=Math.floor(ny/f),o=new Uint8Array(X*Y);
  for(let y=0;y<Y;y++)for(let x=0;x<X;x++){let m=2;for(let j=0;j<f;j++)for(let i=0;i<f;i++)m=Math.min(m,fc[(y*f+j)*nx+x*f+i]);o[y*X+x]=m;} return [o,X,Y]; }
// binary heap A*
function astar(fc,X,Y,r,s,g){
  const N=X*Y,G=new Float32Array(N).fill(Infinity),P=new Int32Array(N).fill(-1),closed=new Uint8Array(N);
  const h=(i)=>{const x=i%X,y=(i/X)|0,dx=Math.abs(x-g%X),dy=Math.abs(y-((g/X)|0));return (Math.max(dx,dy)+(Math.SQRT2-1)*Math.min(dx,dy))*r;};
  const heap=[],push=(f,i)=>{heap.push([f,i]);let k=heap.length-1;while(k>0){const p=(k-1)>>1;if(heap[p][0]<=heap[k][0])break;[heap[p],heap[k]]=[heap[k],heap[p]];k=p;}};
  const pop=()=>{const t=heap[0],l=heap.pop();if(heap.length){heap[0]=l;let k=0;for(;;){let a=2*k+1,b=a+1,m=k;if(a<heap.length&&heap[a][0]<heap[m][0])m=a;if(b<heap.length&&heap[b][0]<heap[m][0])m=b;if(m===k)break;[heap[m],heap[k]]=[heap[k],heap[m]];k=m;}}return t;};
  G[s]=0;push(h(s),s);let exp=0;
  const D=[[1,0,1],[-1,0,1],[0,1,1],[0,-1,1],[1,1,Math.SQRT2],[1,-1,Math.SQRT2],[-1,1,Math.SQRT2],[-1,-1,Math.SQRT2]];
  while(heap.length){const [,i]=pop();if(closed[i])continue;closed[i]=1;exp++;if(i===g)break;const x=i%X,y=(i/X)|0;
    for(const[dx,dy,c]of D){const a=x+dx,b=y+dy;if(a<0||b<0||a>=X||b>=Y)continue;const j=b*X+a;const v=fc[j];if(!v)continue;
      if(dx&&dy&&(!fc[y*X+a]||!fc[b*X+x]))continue;const w=v===2?1:3;const ng=G[i]+c*r*w;if(ng<G[j]){G[j]=ng;P[j]=i;push(ng+h(j),j);}}}
  let len=0,k=g;if(P[g]<0&&g!==s)return {len:NaN,exp};while(P[k]>=0){const p=P[k];len+=Math.hypot(k%X-p%X,((k/X)|0)-((p/X)|0))*r;k=p;}return {len,exp};}
const snap=(fc,X,Y,r,[px,py])=>{let best=-1,bd=1e9;const cx=Math.floor(px/r),cy=Math.floor(py/r);for(let d=0;d<40&&best<0;d++)for(let j=-d;j<=d;j++)for(let i=-d;i<=d;i++){const a=cx+i,b=cy+j;if(a<0||b<0||a>=X||b>=Y)continue;if(fc[b*X+a]){const dd=i*i+j*j;if(dd<bd){bd=dd;best=b*X+a;}}}return best;};
const pass=L.places.find(p=>p.kind==='FoodPass').point;
for(const f of [1,2,4]){
  const [fc,X,Y]=f===1?[fc0,nx,ny]:down(fc0,nx,ny,f);const r=res*f;let t0=performance.now(),rows=[],fail=0;
  for(const t of L.tables){if(!t.stop)continue;const s=snap(fc,X,Y,r,pass),g=snap(fc,X,Y,r,t.stop);const o=astar(fc,X,Y,r,s,g);const ref=L.routes.from_pass?.[t.id]?.grid_length;if(isNaN(o.len))fail++;rows.push([t.id,o.len,ref,o.exp]);}
  const ms=performance.now()-t0;const diffs=rows.filter(r=>r[2]&&!isNaN(r[1])).map(r=>Math.abs(r[1]-r[2]));
  console.log(`cell ${(r*100).toFixed(1)}cm grid ${X}x${Y}: ${rows.length} routes, fail ${fail}, ${(ms/rows.length).toFixed(1)} ms/route, |len-site| mean ${(diffs.reduce((a,b)=>a+b,0)/diffs.length).toFixed(2)} m max ${Math.max(...diffs).toFixed(2)} m`);
}
