/* ============================================================
   Wildlife Kingdom — js/input.js
   Mouse + touch controls · tool painting · placement ghosts ·
   selection picking · camera pan/zoom · hotkeys
   ============================================================ */
(function(){
'use strict';
const WK = window.WK;
const D = WK.Data;

class Input{
  constructor(canvas,game,cam,renderer,ui){
    this.canvas=canvas;
    this.game=game;
    this.cam=cam;
    this.renderer=renderer;
    this.ui=ui;
    this.pointers=new Map();
    this.drag=null;          // {mode:'pan'|'paint', lastX,lastY,lastTile}
    this.pinch=null;
    this.keys=new Set();
    this.spaceHeld=false;
    this.hover=null;         // {x,y}
    this.hoverEdge=null;     // {x,y,edge}
    this._bind();
  }

  /* ---------------- tool application ---------------- */
  toolValid(cat,item,x,y){
    const g=this.game, w=g.world;
    if(!w.inBounds(x,y)) return false;
    const k=w.idx(x,y);
    switch(cat){
      case 'ground':{
        const def=D.GROUND[item];
        return def.tier<=g.researchTier && w.gid(x,y)!==item && w.occ[k]<0;
      }
      case 'paths':
        return D.PATH[item].tier<=g.researchTier &&
               !D.GROUND[w.gid(x,y)].water && w.occ[k]<0 &&
               w.pid(x,y)!==item;
      case 'nature':{
        const def=D.NATURE[item];
        return def.tier<=g.researchTier && w.occ[k]<0 && !D.GROUND[w.gid(x,y)].water;
      }
      case 'fences':
        return true;
      case 'animals':{
        const sp=D.SPECIES[item];
        if(sp.tier>g.researchTier) return false;
        const chk=g.validHabitatAt(x,y);
        if(chk.error) return false;
        return chk.region.area>=sp.minArea && g.habitatScoreFor(chk.region.id,item)>=50;
      }
      case 'facilities':{
        const def=D.BUILDING[item];
        if(def.tier>g.researchTier) return false;
        return w.canPlaceBuilding(x,y,def.w,def.h);
      }
    }
    return false;
  }

  edgeFromEvent(sx,sy){
    const t=this.cam.screenToTile(sx,sy);
    if(!this.game.world.inBounds(t.x,t.y)) return null;
    const p=this.cam.project(t.x,t.y);
    const lx=(sx-p.x)/WK.HW, ly=(sy-p.y)/WK.HH;
    // distances to four diamond sides
    const dSE=1-(lx+ly), dSW=1+(lx-ly), dNW=1+(lx+ly), dNE=1-(lx-ly);
    const m=Math.min(dSE,dSW,dNW,dNE);
    const w=this.game.world;
    if(m===dSE) return {x:t.x,y:t.y,edge:'S'};
    if(m===dSW) return w.inBounds(t.x-1,t.y)?{x:t.x-1,y:t.y,edge:'E'}:{x:t.x,y:t.y,edge:'S'};
    if(m===dNW) return w.inBounds(t.x-1,t.y)?{x:t.x-1,y:t.y,edge:'E'}:null;
    return w.inBounds(t.x,t.y-1)?{x:t.x,y:t.y-1,edge:'S'}:null;
  }

  applyAt(h){
    const ui=this.ui, g=this.game;
    if(ui.bulldoze){
      if(this.hoverEdge&&g.tryBulldozeEdge(this.hoverEdge.x,this.hoverEdge.y,this.hoverEdge.edge)) return;
      g.tryBulldoze(h.x,h.y);
      return;
    }
    const t=ui.tool;
    if(!t) return;
    switch(t.cat){
      case 'ground': g.tryGround(h.x,h.y,t.item); break;
      case 'paths': g.tryPath(h.x,h.y,t.item); break;
      case 'nature': g.tryNature(h.x,h.y,t.item); break;
      case 'fences':
        if(this.hoverEdge) g.tryFence(this.hoverEdge.x,this.hoverEdge.y,this.hoverEdge.edge,t.item);
        break;
      case 'animals':
        g.tryAdopt(t.item,h.x,h.y);
        break;
      case 'facilities':
        if(g.tryBuilding(t.item,h.x,h.y)){ /* tool stays active for combos */ }
        break;
    }
  }
  applyLine(a,b){
    /* Wall-aware drag painting. Iso fence topology:
       S-edges chain over +x ("\") · E-edges chain over +y ("/").
       So a drag paints the SIDE edge of each visited tile → a straight,
       fully-connected wall hugging the swept line. */
    let x=a.x,y=a.y;
    let guard=80;
    const w=this.game.world;
    const fenceMode=(this.ui.tool&&this.ui.tool.cat==='fences')||this.ui.bulldoze;
    while(guard--&&(x!==b.x||y!==b.y)){
      const movedX=b.x!==x;
      if(b.x>x) x++; else if(b.x<x) x--;
      else if(b.y>y) y++; else y--;
      if(!w.inBounds(x,y)){ continue; }
      if(fenceMode){
        this.hoverEdge=movedX?{x,y,edge:'S'}:{x,y,edge:'E'};
      }
      this.applyAt({x,y});
    }
    this.hoverEdge=null;
  }

  /* ---------------- picking ---------------- */
  pick(sx,sy){
    const g=this.game, cam=this.cam;
    let best=null,bd=34*34;
    const consider=(ent,rad)=>{
      const p=cam.project(ent.x,ent.y);
      const dd=(p.x-sx)**2+(p.y-sy+14)**2;
      if(dd<bd*(rad||1)){ best=ent; bd=dd; }
    };
    bd=34*34; for(const a of g.animals) consider(a,(a.size||1)*1.3);
    bd=26*26; for(const st of g.staff) consider(st,1);
    bd=22*22; for(const gu of g.guests) consider(gu,1);
    if(best){ this.ui.showSelection(best); return true; }
    const t=cam.screenToTile(sx,sy);
    const b=g.world.buildingAt(t.x,t.y);
    if(b){ this.ui.showSelection(b); return true; }
    // habitat summary
    g.world.ensureRegions();
    const rid=g.world.regionOf(t.x,t.y);
    if(rid>=0){
      const reg=g.world.regions.find(r=>r.id===rid);
      if(reg&&reg.openEdges===0&&!reg.hasPath&&reg.area>=8){
        this.ui.showSelection({kind:'habitat',regionId:rid,x:t.x,y:t.y});
        return true;
      }
    }
    this.ui.hideSelection();
    return false;
  }

  /* ---------------- events ---------------- */
  _bind(){
    const cv=this.canvas;
    cv.addEventListener('pointerdown',e=>{
      WK.AudioSys.ensure();
      cv.setPointerCapture(e.pointerId);
      this.pointers.set(e.pointerId,{x:e.offsetX,y:e.offsetY});
      if(this.pointers.size===2){ this._startPinch(); return; }
      const sx=e.offsetX,sy=e.offsetY;
      const wantPan = e.button===1||e.button===2||this.spaceHeld||
                      (e.pointerType==='touch'&&(!this.ui.tool&&!this.ui.bulldoze));
      if(wantPan){
        this.drag={mode:'pan',lastX:sx,lastY:sy};
        cv.style.cursor='grabbing';
        return;
      }
      if(e.button===2){ this.ui.cancelTool(); return; }
      const t=this.cam.screenToTile(sx,sy);
      if(!this.game.world.inBounds(t.x,t.y)) return;
      this.hoverEdge=this.toolActiveFence()?this.edgeFromEvent(sx,sy):null;
      if(this.ui.tool||this.ui.bulldoze){
        this.drag={mode:'paint',lastTile:t,startTile:t};
        this.applyAt(t);
      }else{
        this.pick(sx,sy);
      }
    });
    cv.addEventListener('pointermove',e=>{
      const sx=e.offsetX,sy=e.offsetY;
      if(this.pointers.has(e.pointerId)) this.pointers.set(e.pointerId,{x:sx,y:sy});
      if(this.pinch&&this.pointers.size===2){ this._movePinch(); return; }
      const t=this.cam.screenToTile(sx,sy);
      this.hover=t;
      this.hoverEdge=this.toolActiveFence()?this.edgeFromEvent(sx,sy):null;
      if(!this.drag) return;
      if(this.drag.mode==='pan'){
        this.cam.panBy(sx-this.drag.lastX,sy-this.drag.lastY);
        this.drag.lastX=sx; this.drag.lastY=sy;
      }else{
        if(!this.game.world.inBounds(t.x,t.y)) return;
        if(t.x!==this.drag.lastTile.x||t.y!==this.drag.lastTile.y){
          if(e.buttons&2){ }
          this.applyLine(this.drag.lastTile,t);
          this.drag.lastTile=t;
        }
      }
    });
    const up=e=>{
      this.pointers.delete(e.pointerId);
      if(this.pointers.size<2) this.pinch=null;
      if(this.drag&&this.pointers.size===0){
        this.drag=null;
        cv.style.cursor=this.ui.tool?'crosshair':'grab';
      }
    };
    cv.addEventListener('pointerup',up);
    cv.addEventListener('pointercancel',up);
    cv.addEventListener('pointerleave',e=>{ this.hover=null; });
    cv.addEventListener('contextmenu',e=>e.preventDefault());
    cv.addEventListener('wheel',e=>{
      e.preventDefault();
      const f=Math.pow(1.0016,-e.deltaY);
      this.cam.zoomAt(e.offsetX,e.offsetY,f);
    },{passive:false});

    window.addEventListener('keydown',e=>{
      if(e.target&&(e.target.tagName==='INPUT')) return;
      this.keys.add(e.key);
      if(e.code==='Space'){ this.spaceHeld=true; e.preventDefault(); }
      const ui=this.ui;
      switch(e.key){
        case 'Escape': ui.cancelTool(); break;
        case 'x': case 'X': ui.toggleBulldoze(); break;
        case ' ': ui.togglePause(); e.preventDefault(); break;
        case '1': ui.setSpeed(1); break;
        case '2': ui.setSpeed(2); break;
        case '3': ui.setSpeed(4); break;
        case '+': case '=': this.cam.zoomAt(this.cam.w/2,this.cam.h/2,1.15); break;
        case '-': this.cam.zoomAt(this.cam.w/2,this.cam.h/2,0.87); break;
      }
    });
    window.addEventListener('keyup',e=>{
      this.keys.delete(e.key);
      if(e.code==='Space') this.spaceHeld=false;
    });
  }
  toolActiveFence(){
    return (this.ui.tool&&this.ui.tool.cat==='fences')||(this.ui.bulldoze);
  }
  _startPinch(){
    const pts=[...this.pointers.values()];
    this.pinch={
      d:Math.hypot(pts[0].x-pts[1].x,pts[0].y-pts[1].y),
      mx:(pts[0].x+pts[1].x)/2, my:(pts[0].y+pts[1].y)/2,
    };
    this.drag=null;
  }
  _movePinch(){
    const pts=[...this.pointers.values()];
    if(pts.length<2) return;
    const d=Math.hypot(pts[0].x-pts[1].x,pts[0].y-pts[1].y);
    const mx=(pts[0].x+pts[1].x)/2, my=(pts[0].y+pts[1].y)/2;
    const f=WK.clamp(d/Math.max(20,this.pinch.d),0.5,2);
    this.cam.zoomAt(mx,my,f);
    this.cam.panBy(mx-this.pinch.mx,my-this.pinch.my);
    this.pinch={d,mx,my};
  }
  update(dt){
    // keyboard panning
    const sp=520*dt/this.cam.zoom;
    let dx=0,dy=0;
    if(this.keys.has('ArrowLeft')||this.keys.has('a')||this.keys.has('A')) dx-=sp;
    if(this.keys.has('ArrowRight')||this.keys.has('d')||this.keys.has('D')) dx+=sp;
    if(this.keys.has('ArrowUp')||this.keys.has('w')||this.keys.has('W')) dy-=sp;
    if(this.keys.has('ArrowDown')||this.keys.has('s')||this.keys.has('S')) dy+=sp;
    if(dx||dy) this.cam.panBy(dx,dy);
  }
  /* overlay hook for renderer */
  overlay(ctx,cam,range){
    const ui=this.ui;
    if(ui.bulldoze){
      if(this.hover){
        const p=cam.project(this.hover.x,this.hover.y);
        WK.Sprites.diamondPath(ctx,p.x,p.y,1);
        ctx.fillStyle='rgba(230,80,70,.25)'; ctx.fill();
        ctx.strokeStyle='rgba(230,80,70,.9)'; ctx.lineWidth=2; ctx.stroke();
        if(this.hoverEdge){
          const h=this.hoverEdge;
          const p2=cam.project(h.x,h.y);
          const zz=cam.zoom;
          let ax,ay,bx,by;
          if(h.edge==='S'){ ax=p2.x-WK.HW*zz; ay=p2.y; bx=p2.x; by=p2.y+WK.HH*zz; }
          else{ ax=p2.x; ay=p2.y+WK.HH*zz; bx=p2.x+WK.HW*zz; by=p2.y; }
          ctx.lineWidth=7; ctx.beginPath(); ctx.moveTo(ax,ay-6); ctx.lineTo(bx,by-6); ctx.stroke();
        }
      }
      return;
    }
    const t=ui.tool;
    if(!t||!this.hover) return;
    const h=this.hover;
    const valid=this.toolValid(t.cat,t.item,h.x,h.y);
    let g={x:h.x,y:h.y,valid};
    if(t.cat==='facilities'){ g.mode='foot'; g.item=t.item; }
    else if(t.cat==='fences'){
      if(this.hoverEdge){ g={...this.hoverEdge,mode:'edge',valid:true}; }
      else g=null;
    }
    else{
      g.mode='tile';
      if(t.cat==='nature') g.sprite=WK.Sprites.getObject(t.item,0);
      else if(t.cat==='animals'){
        g.sprite=null;
        // animal ghost: draw mini animal
        const p=cam.project(h.x,h.y);
        WK.Sprites.diamondPath(ctx,p.x,p.y,1);
        ctx.fillStyle=valid?'rgba(140,230,150,.28)':'rgba(230,90,80,.3)';
        ctx.fill();
        ctx.strokeStyle=valid?'rgba(110,220,120,.85)':'rgba(230,80,70,.9)';
        ctx.lineWidth=2; ctx.stroke();
        ctx.save();
        ctx.translate(p.x,p.y);
        ctx.globalAlpha=.85;
        const sc=(WK.ANIMAL_SIZE[t.item]||1)*(valid?1:0.8);
        ctx.scale(sc,sc);
        WK.Sprites.drawAnimal(ctx,t.item,{state:'idle',ph:0,t:this.game.timeAbs});
        ctx.restore();
        return;
      }
    }
    this.renderer.drawGhost(ctx,cam,g);
  }
}
WK.Input = Input;
})();
