// SPDX-License-Identifier: AGPL-3.0-or-later
package preservation;

/** Selected US EntitySubwpnThrownVibhuti from pinned src/dra/84B88.c.
 * Original definition/quarter lookup/BP26 are registered as data. Source
 * twelve tiles plus control, velocities, source collisions, landed sizes,
 * timer-indexed hitbox and hiding order are retained. World-camera/factory
 * timing, isolated shared RNG, bounded256 primitives and base8 once-target
 * battle feedback replace full original player/global battle/GPU/audio logic.
 */
public final class VibhutiWorld {
    static final int ONE=65536,ENTITY=12,PRIORITY=150;
    interface RandomSource {int next();}
    interface Environment {VibhutiTerrain.Collision query(int x,int y);int hit(Actor actor,int x,int y,int width,int height,int sequence,int damage);}
    static final class Tile {int x,y,vx,vy,width=2,height=2,delay=1,drawMode=2,primitiveType=17;}
    static final class Actor {
        final int slot,serial,sequence;final Object[] targets=new Object[256];int x,y,face,step,remaining=128,flags,hitboxState,hitboxWidth,hitboxHeight,hitboxOffY,hitFlags,lastHitFeedback,hiddenByHitIndex=-1,selectedIndex=-1,hitCount,targetCount,damageRoll,queryCalls,unsupportedQueries;
        boolean active=true,allocated;String destroyedReason="";Tile[] tiles=new Tile[0];
        Actor(int index,int serial,int seq,int x,int y,int face){slot=index+32;this.serial=serial;sequence=seq;this.x=x;this.y=y;this.face=face;}
        boolean hasHit(Object target){for(int i=0;i<targetCount;i++)if(targets[i]==target)return true;return false;}boolean rememberHit(Object target){if(target==null||hasHit(target)||targetCount==targets.length)return false;targets[targetCount++]=target;return true;}
    }
    private final DaggerWorld.SharedPool pool;private final RandomSource randomSource;private final Actor[] actors=new Actor[16];
    private int[] fields,quarter,blueprint;private int serial,randomState=1,randomCalls,allocationFailures,soundEvents,playerTimerRequests,peakPrimitiveUsage;
    public VibhutiWorld(){this(new DaggerWorld.SharedPool(),null);}VibhutiWorld(DaggerWorld.SharedPool p,RandomSource random){if(p==null)throw new IllegalArgumentException("Missing pool");pool=p;randomSource=random;}
    public boolean register(int[] f,int[] q,int[] bp){if(f==null||f.length!=13||q==null||q.length!=1025||bp==null||bp.length!=6)return false;int[] expected={8,3,4096,2,10,8,188,26,2,1,0,0,0},factory={12,1,193,0,2,0};for(int i=0;i<13;i++)if(f[i]!=expected[i])return false;for(int i=0;i<6;i++)if(bp[i]!=factory[i])return false;if(q[0]!=0||q[1024]!=4096)return false;for(int i=0;i<1025;i++)if(q[i]<0||q[i]>4096||i>0&&q[i]<q[i-1])return false;fields=f.clone();quarter=q.clone();blueprint=bp.clone();reset();return true;}
    public boolean registered(){return fields!=null;}public int heartCost(){return fields==null?0:fields[1];}public int activeCount(){int n=0;for(Actor a:actors)if(a!=null&&a.active)n++;return n;}public int refusal(){return !registered()?1:activeCount()>=fields[3]||pool.freeSlot()<0?2:0;}
    public boolean spawn(int x,int y,int face,int sequence){if(refusal()!=0||face!=1&&face!=-1||sequence<1||x<Integer.MIN_VALUE+64*ONE||x>Integer.MAX_VALUE-64*ONE||y<Integer.MIN_VALUE+64*ONE||y>Integer.MAX_VALUE-64*ONE)return false;int index=pool.freeSlot();Actor a=new Actor(index,++serial,sequence,x,y,face);pool.reserve(index,a);actors[index]=a;return true;}
    public void reset(){for(int i=0;i<16;i++){if(actors[i]!=null)pool.release(actors[i]);actors[i]=null;}serial=randomCalls=allocationFailures=soundEvents=playerTimerRequests=peakPrimitiveUsage=0;randomState=1;}
    private int random(){randomCalls++;if(randomSource!=null)return randomSource.next();randomState=randomState*1103515245+12345;return(randomState>>>16)&32767;}
    private int sin(int angle){return CrossMath.sine(quarter,angle);}private int cos(int angle){return sin(angle+1024);}
    private void destroy(Actor a,String reason){a.active=a.allocated=false;a.hitboxState=a.hitboxWidth=a.hitboxHeight=0;a.destroyedReason=reason;for(Tile t:a.tiles)t.drawMode|=8;pool.release(a);}
    private VibhutiTerrain.Collision probe(Actor a,Environment e,int x,int y){a.queryCalls++;VibhutiTerrain.Collision c=e.query(x,y);if(c.unsupported)a.unsupportedQueries++;return c;}
    private static int writeHigh(int fixed,int high){return((short)high<<16)|(fixed&65535);}
    public void update(Environment e,int cameraX,int cameraY){for(int i=0;i<16;i++)updateSlot(i,e,cameraX,cameraY);}
    void updateSlot(int index,Environment e,int cameraX,int cameraY){if(!registered())return;Actor a=actors[index];if(a==null||!a.active)return;a.hiddenByHitIndex=-1;
        if(a.hitboxState!=0&&a.hitboxWidth>0&&a.hitboxHeight>0){int feedback=e.hit(a,a.x>>16,(a.y>>16)+a.hitboxOffY,a.hitboxWidth,a.hitboxHeight,a.sequence,fields[0]);if(feedback!=0){a.hitFlags=feedback;a.hitCount++;}}a.lastHitFeedback=a.hitFlags;
        switch(a.step){
        case 0:if(!pool.allocate(a,13)){allocationFailures++;destroy(a,"primitive-allocation-failed");return;}a.allocated=true;peakPrimitiveUsage=Math.max(peakPrimitiveUsage,pool.usedPrimitives());a.flags=0x08800000;a.damageRoll=random()%12;a.hitboxState=fields[8];a.hitboxWidth=a.hitboxHeight=4;a.remaining=128;a.tiles=new Tile[13];for(int i=0;i<13;i++){Tile t=a.tiles[i]=new Tile();if(i==12){t.width=t.height=t.delay=0;t.primitiveType=1;continue;}t.x=(short)(a.x>>16)*ONE;t.y=(short)((a.y>>16)-12)*ONE;int angle=(random()&255)+256,speed=(random()&255)+128;t.vx=(((cos(angle)<<4)*speed)>>9)+ONE/2;t.vy=-(((sin(angle)<<4)*speed)>>9);t.vx=(t.vx*3)>>1;if(a.face<0)t.vx=-t.vx;}soundEvents++;playerTimerRequests++;a.step=1;break;
        case 1:if(--a.remaining==0){destroy(a,"ash-lifetime-ended");return;}for(int i=0;i<12;i++){Tile t=a.tiles[i];if(t.delay!=0){if(t.vx!=0){t.x+=t.vx;if((probe(a,e,(t.x>>16)+(a.face<0?-2:2),t.y>>16).effects&2)!=0)t.vx=0;}t.y+=t.vy;t.vy=Math.min(4*ONE,t.vy+6144);if(t.vy>0){VibhutiTerrain.Collision c=probe(a,e,t.x>>16,t.y>>16);if((c.effects&1)!=0){t.delay=0;t.y=writeHigh(t.y,(t.y>>16)+c.up-(i%3+1));t.width=t.height=3;}}}
            if((a.remaining&7)==i){a.selectedIndex=i;a.x=writeHigh(a.x,t.x>>16);a.y=writeHigh(a.y,t.y>>16);a.hitboxWidth=a.hitboxHeight=(t.drawMode&8)!=0?0:4;a.hitboxOffY=t.delay!=0?0:-6;}
            if(a.hitFlags!=0&&((a.remaining+1)&7)==i){t.drawMode=8;a.hiddenByHitIndex=i;}if(a.remaining-1==i)t.drawMode=8;
        }a.hitFlags=0;break;
        default:throw new IllegalStateException("Invalid Vibhuti step");}
        if(a.active&&((a.x>>16)-cameraX<-32||(a.x>>16)-cameraX>288||(a.y>>16)-cameraY<-16||(a.y>>16)-cameraY>256))destroy(a,"outside-source-camera");
    }
    public String getState(){StringBuilder s=new StringBuilder("[");boolean comma=false;for(Actor a:actors)if(a!=null){if(comma)s.append(',');comma=true;s.append("{\"kind\":\"vibhutiAsh\",\"screenSpace\":false,\"entityId\":12,\"inventoryId\":8,\"slot\":").append(a.slot).append(",\"serial\":").append(a.serial).append(",\"sequence\":").append(a.sequence).append(",\"x\":").append(a.x>>16).append(",\"y\":").append(a.y>>16).append(",\"xFixed\":").append(a.x).append(",\"yFixed\":").append(a.y).append(",\"face\":").append(a.face).append(",\"facingLeft\":").append(a.face<0?1:0).append(",\"step\":").append(a.step).append(",\"remaining\":").append(a.remaining).append(",\"flags\":").append(a.flags).append(",\"selectedIndex\":").append(a.selectedIndex).append(",\"hitboxState\":").append(a.hitboxState).append(",\"hitboxWidth\":").append(a.hitboxWidth).append(",\"hitboxHeight\":").append(a.hitboxHeight).append(",\"hitboxOffY\":").append(a.hitboxOffY).append(",\"hitFlags\":").append(a.hitFlags).append(",\"lastHitFeedback\":").append(a.lastHitFeedback).append(",\"hiddenByHitIndex\":").append(a.hiddenByHitIndex).append(",\"hitTargetCount\":").append(a.targetCount).append(",\"hitCount\":").append(a.hitCount).append(",\"damageRoll\":").append(a.damageRoll).append(",\"queryCalls\":").append(a.queryCalls).append(",\"unsupportedQueries\":").append(a.unsupportedQueries).append(",\"active\":").append(a.active).append(",\"alive\":").append(a.active).append(",\"destroyedReason\":\"").append(a.destroyedReason).append("\",\"tiles\":[");for(int i=0;i<a.tiles.length;i++){if(i>0)s.append(',');Tile t=a.tiles[i];s.append("{\"index\":").append(i).append(",\"x\":").append(t.x>>16).append(",\"y\":").append(t.y>>16).append(",\"xFixed\":").append(t.x).append(",\"yFixed\":").append(t.y).append(",\"vxFixed\":").append(t.vx).append(",\"vyFixed\":").append(t.vy).append(",\"width\":").append(t.width).append(",\"height\":").append(t.height).append(",\"delay\":").append(t.delay).append(",\"landed\":").append(i<12&&t.delay==0).append(",\"rgb\":[255,255,255],\"drawMode\":").append(t.drawMode).append(",\"priority\":150,\"primitiveType\":").append(t.primitiveType).append(",\"visible\":").append(a.active&&(t.drawMode&8)==0).append('}');}s.append("]}");}return s.append(']').toString();}
    public String getStatistics(){return "{\"randomCalls\":"+randomCalls+",\"allocationFailures\":"+allocationFailures+",\"soundEvents\":"+soundEvents+",\"playerTimerRequests\":"+playerTimerRequests+",\"primitiveUsage\":"+pool.usedPrimitives()+",\"primitiveCapacity\":"+pool.primitiveCapacity+",\"peakPrimitiveUsage\":"+peakPrimitiveUsage+"}";}
    Actor actor(int index){return actors[index];}int randomCalls(){return randomCalls;}int allocationFailures(){return allocationFailures;}void seedRandom(int seed){randomState=seed;randomCalls=0;}
}
