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

/** Selected Cross geometry expressed as Java arithmetic, not an instruction
 * interpreter. RotMatrix uses the recovered US packed sine/cosine pairs;
 * translation uses the separate recovered rsin quarter table. Projection uses
 * the documented GTE UNR reciprocal and IR/SZ/SXY saturation. The selected
 * coordinates fit signed44-bit MACs; command/FIFO/FLAG registers, color commands
 * and general out-of-range MAC overflow behavior are intentionally not modeled.
 * UNR reference: https://psx-spx.consoledev.net/geometrytransformationenginegte/
 */
public final class CrossMath {
    private CrossMath() {}
    static int sine(int[] quarter,int angle){int a=angle&4095;return a<=1024?quarter[a]:a<=2048?quarter[2048-a]:a<=3072?-quarter[a-2048]:-quarter[4096-a];}
    /** Exact US RotMatrix specialization for source X=0, Z=64. */
    static int[] matrix(int[] packed,int y,int z){int sy=(short)packed[y&4095],cy=(short)(packed[y&4095]>>>16),sz=(short)packed[z&4095],cz=(short)(packed[z&4095]>>>16);return new int[]{(short)((cz*cy)>>12),(short)((-sz*cy)>>12),sy,sz,cz,0,(short)((-cz*sy)>>12),(short)(-((-sz*sy)>>12)),cy};}
    static int reciprocal(int h,int depth){if(depth<=0||h>=depth*2)return 131071;int shift=Integer.numberOfLeadingZeros(depth)-16,n=h<<shift,d=depth<<shift,index=(d-32704)>>7;int table=Math.max(0,((262144/(index+256)+1)/2)-257),u=table+257;int first=(33554560-d*u)>>8,second=(128+first*u)>>8;return(int)Math.min(131071L,(((long)n*second)+32768)>>16);}
    static final class Point {final int x,y,depth;Point(int x,int y,int depth){this.x=x;this.y=y;this.depth=depth;}}
    static Point project(int[] m,int tx,int ty,int tz,int ox,int oy,int vx,int vy,int vz){
        long ax=((long)tx<<12)+(long)m[0]*vx+(long)m[1]*vy+(long)m[2]*vz;
        long ay=((long)ty<<12)+(long)m[3]*vx+(long)m[4]*vy+(long)m[5]*vz;
        long az=((long)tz<<12)+(long)m[6]*vx+(long)m[7]*vy+(long)m[8]*vz;
        int ix=clamp(ax>>12,-32768,32767),iy=clamp(ay>>12,-32768,32767),depth=clamp(az>>12,0,65535),q=reciprocal(320,depth);
        return new Point(clamp((((long)ox<<16)+(long)ix*q)>>16,-1024,1023),clamp((((long)oy<<16)+(long)iy*q)>>16,-1024,1023),depth);
    }
    static int clip(Point a,Point b,Point c){return(int)((long)a.x*b.y+(long)b.x*c.y+(long)c.x*a.y-(long)a.x*c.y-(long)b.x*a.y-(long)c.x*b.y);}
    private static int clamp(long n,int low,int high){return(int)Math.max(low,Math.min(high,n));}
}
