/* ============================================================
   Wildlife Kingdom — js/util.js
   Math, RNG, color & canvas helpers. Exposes global WK.
   ============================================================ */
(function(){
'use strict';
const WK = (window.WK = window.WK || {});

WK.TILE_W = 64;   // iso diamond width
WK.TILE_H = 32;   // iso diamond height
WK.HW = WK.TILE_W / 2;
WK.HH = WK.TILE_H / 2;

/* ---- math ---- */
WK.clamp = (v,a,b)=> v<a?a:(v>b?b:v);
WK.lerp  = (a,b,t)=> a+(b-a)*t;
WK.dist2 = (ax,ay,bx,by)=>{const dx=ax-bx,dy=ay-by;return dx*dx+dy*dy;};
WK.dist  = (ax,ay,bx,by)=> Math.hypot(ax-bx,ay-by);
WK.TAU   = Math.PI*2;
WK.easeOutCubic = t=>1-Math.pow(1-t,3);
WK.easeInOut = t=> t<0.5 ? 2*t*t : 1-Math.pow(-2*t+2,2)/2;

/* deterministic RNG */
WK.mulberry32 = function(seed){
  let a = seed>>>0;
  return function(){
    a |= 0; a = (a + 0x6D2B79F5)|0;
    let t = Math.imul(a ^ (a>>>15), 1|a);
    t = (t + Math.imul(t ^ (t>>>7), 61|t)) ^ t;
    return ((t ^ (t>>>14)) >>> 0) / 4294967296;
  };
};
WK.hash2 = function(x,y,s){
  let h = (x*374761393 + y*668265263) ^ (s|0)*1442695041;
  h = (h ^ (h>>>13)) >>> 0;
  h = Math.imul(h, 1274126177) >>> 0;
  return ((h ^ (h>>>16)) % 100000)/100000; // 0..1
};

/* ---- formatting ---- */
WK.fmtMoney = n=>{
  const neg = n<0; n = Math.round(Math.abs(n));
  let s = String(n).replace(/\B(?=(\d{3})+(?!\d))/g,',');
  return (neg?'-$':'$')+s;
};
WK.fmtClock = min=>{ // minutes in day
  min = ((min%1440)+1440)%1440;
  const h=Math.floor(min/60), m=Math.floor(min%60);
  return String(h).padStart(2,'0')+':'+String(m).padStart(2,'0');
};

/* ---- colors ---- */
WK.hexToRgb = hex=>{
  hex = hex.replace('#','');
  if(hex.length===3) hex = hex.split('').map(c=>c+c).join('');
  const n=parseInt(hex,16);
  return [(n>>16)&255,(n>>8)&255,n&255];
};
WK.shade = (hex,amt)=>{ // amt -1..1 (negative darker)
  const [r,g,b]=WK.hexToRgb(hex);
  const f=c=> WK.clamp(Math.round(amt>=0 ? c+(255-c)*amt : c*(1+amt)),0,255);
  return `rgb(${f(r)},${f(g)},${f(b)})`;
};
WK.rgba = (hex,a)=>{
  const [r,g,b]=WK.hexToRgb(hex); return `rgba(${r},${g},${b},${a})`;
};
WK.mix = (h1,h2,t)=>{
  const a=WK.hexToRgb(h1), b=WK.hexToRgb(h2);
  return `rgb(${Math.round(WK.lerp(a[0],b[0],t))},${Math.round(WK.lerp(a[1],b[1],t))},${Math.round(WK.lerp(a[2],b[2],t))})`;
};

/* ---- canvas helpers ---- */
WK.mkCanvas = (w,h)=>{ const c=document.createElement('canvas'); c.width=Math.max(1,Math.ceil(w)); c.height=Math.max(1,Math.ceil(h)); return c; };

WK.rr = function(ctx,x,y,w,h,r){ // rounded rect path
  if(typeof r==='number') r={tl:r,tr:r,br:r,bl:r};
  else r = Object.assign({tl:0,tr:0,br:0,bl:0},r);
  ctx.beginPath();
  ctx.moveTo(x+r.tl,y);
  ctx.lineTo(x+w-r.tr,y); ctx.arcTo(x+w,y,x+w,y+r.tr,r.tr);
  ctx.lineTo(x+w,y+h-r.br); ctx.arcTo(x+w,y+h,x+w-r.br,y+h,r.br);
  ctx.lineTo(x+r.bl,y+h); ctx.arcTo(x,y+h,x,y+h-r.bl,r.bl);
  ctx.lineTo(x,y+r.tl); ctx.arcTo(x,y,x+r.tl,y,r.tl);
  ctx.closePath();
};

WK.softShadow = function(ctx, blur, color, offY){
  ctx.shadowBlur=blur; ctx.shadowColor=color||'rgba(70,45,10,.35)';
  ctx.shadowOffsetY = offY==null?2:offY;
};
WK.clearShadow = ctx=>{ ctx.shadowBlur=0; ctx.shadowColor='transparent'; ctx.shadowOffsetY=0; };

/* ellipse blob with vertical gradient for cute volume */
WK.blob = function(ctx,x,y,rx,ry,color,lighten){
  const g = ctx.createRadialGradient(x-rx*0.35,y-ry*0.55,ry*0.15,x,y,Math.max(rx,ry));
  g.addColorStop(0, lighten || WK.shade(color,0.28));
  g.addColorStop(1, color);
  ctx.fillStyle=g;
  ctx.beginPath(); ctx.ellipse(x,y,rx,ry,0,0,WK.TAU); ctx.fill();
};

/* simple eye: white + pupil + shine */
WK.eye = function(ctx,x,y,r,opt){
  opt=opt||{};
  if(opt.closed){
    ctx.strokeStyle=opt.lidColor||'#3a2c20'; ctx.lineWidth=Math.max(1.5,r*0.5);
    ctx.beginPath(); ctx.moveTo(x-r,y*0.98); ctx.quadraticCurveTo(x,y+r*0.9,x+r,y*0.98); ctx.stroke();
    return;
  }
  ctx.fillStyle='#fff';
  ctx.beginPath(); ctx.ellipse(x,y,r,r*(opt.tall||1),0,0,WK.TAU); ctx.fill();
  const px = x + (opt.lookX||0)*r*0.28, py = y + (opt.lookY||0.08)*r*0.4;
  ctx.fillStyle=opt.pupil||'#33261a';
  ctx.beginPath(); ctx.ellipse(px,py,r*0.52,r*0.62*(opt.tall||1),0,0,WK.TAU); ctx.fill();
  ctx.fillStyle='#fff';
  ctx.beginPath(); ctx.arc(px-r*0.18,py-r*0.24,r*0.2,0,WK.TAU); ctx.fill();
};

/* tiny smile / open mouth */
WK.mouth = function(ctx,x,y,w,open,color){
  ctx.fillStyle = color||'#7c4a3a';
  if(open>0.05){
    ctx.beginPath(); ctx.ellipse(x,y,w,w*(0.5+open),0,0,Math.PI); ctx.fill();
    ctx.fillStyle='#e8756f';
    ctx.beginPath(); ctx.ellipse(x,y+w*(0.35+open*0.5),w*0.55,w*0.4*open,0,0,Math.PI); ctx.fill();
  }else{
    ctx.strokeStyle=color||'#7c4a3a'; ctx.lineWidth=Math.max(1.2,w*0.22); ctx.lineCap='round';
    ctx.beginPath(); ctx.moveTo(x-w,y-w*0.25); ctx.quadraticCurveTo(x,y+w*0.65,x+w,y-w*0.25); ctx.stroke();
  }
};

WK.blush = function(ctx,x,y,r,color,alpha){
  ctx.globalAlpha = alpha==null?0.35:alpha;
  ctx.fillStyle=color||'#ff9d8a';
  ctx.beginPath(); ctx.ellipse(x,y,r,r*0.62,0,0,WK.TAU); ctx.fill();
  ctx.globalAlpha=1;
};

/* object pool-ish array clear */
WK.clearArr = a=>{ a.length=0; return a; };

/* pick weighted {id:w} map or [ [item,w],... ] */
WK.weightedPick = function(entries,rnd){
  rnd = rnd||Math.random;
  let tot=0; for(const e of entries) tot+=e.w;
  let r=rnd()*tot;
  for(const e of entries){ r-=e.w; if(r<=0) return e; }
  return entries[entries.length-1];
};
})();
