/**
 * XCU PulsarCodec — TypeScript Port
 * Delta-based pixel compression codec ported from Rust (xcu-wasm-sdk/pulsar.rs)
 * 
 * Protocol: 7 bytes per changed pixel [X_hi, X_lo, Y_hi, Y_lo, R, G, B]
 * 
 * This is the PRIMARY video codec for XCU. H.264 WebCodecs is the fallback.
 * PulsarCodec sends ONLY pixels that changed since the last frame,
 * achieving near-zero bandwidth when the scene is static.
 */

const NEUROMORPHIC_THRESHOLD = 25;

export class XCUPulsarCodec {
	private lastFrame: Uint8Array;
	private canvasBuffer: Uint8Array;
	private width: number;
	private height: number;

	constructor(width: number, height: number) {
		this.width = width;
		this.height = height;
		this.lastFrame = new Uint8Array(width * height * 4);
		this.canvasBuffer = new Uint8Array(width * height * 4);
		// Initialize canvas buffer to black with full alpha
		for (let i = 3; i < this.canvasBuffer.length; i += 4) {
			this.canvasBuffer[i] = 255;
		}
	}

	/**
	 * Encode delta: Compare current RGBA frame to last frame.
	 * Output only changed pixels as 7-byte chunks [X_hi, X_lo, Y_hi, Y_lo, R, G, B]
	 */
	encodeDelta(currentFrame: Uint8Array): Uint8Array {
		const deltaChunks: number[] = [];

		for (let i = 0; i < currentFrame.length; i += 4) {
			const currR = currentFrame[i];
			const currG = currentFrame[i + 1];
			const currB = currentFrame[i + 2];

			const lastR = this.lastFrame[i];
			const lastG = this.lastFrame[i + 1];
			const lastB = this.lastFrame[i + 2];

			const diffTotal = Math.abs(currR - lastR) + Math.abs(currG - lastG) + Math.abs(currB - lastB);

			if (diffTotal > NEUROMORPHIC_THRESHOLD) {
				const pixelIndex = (i / 4) | 0;
				const x = pixelIndex % this.width;
				const y = (pixelIndex / this.width) | 0;

				// 7-byte format: [X_hi, X_lo, Y_hi, Y_lo, R, G, B]
				deltaChunks.push(
					(x >> 8) & 0xFF, x & 0xFF,
					(y >> 8) & 0xFF, y & 0xFF,
					currR, currG, currB,
				);

				// Update last frame (only changed pixels)
				this.lastFrame[i] = currR;
				this.lastFrame[i + 1] = currG;
				this.lastFrame[i + 2] = currB;
			}
		}

		return new Uint8Array(deltaChunks);
	}

	/**
	 * Decode truecolor: Render RGB asli dari delta payload ke canvas buffer.
	 * Berbeda dengan decode_xray di Rust yang render hijau neon.
	 */
	decodeTruecolor(payload: Uint8Array): Uint8Array {
		for (let i = 0; i + 6 < payload.length; i += 7) {
			const x = (payload[i] << 8) | payload[i + 1];
			const y = (payload[i + 2] << 8) | payload[i + 3];
			const r = payload[i + 4];
			const g = payload[i + 5];
			const b = payload[i + 6];

			const bufIdx = (y * this.width + x) * 4;
			if (bufIdx >= 0 && bufIdx + 3 < this.canvasBuffer.length) {
				this.canvasBuffer[bufIdx] = r;
				this.canvasBuffer[bufIdx + 1] = g;
				this.canvasBuffer[bufIdx + 2] = b;
				this.canvasBuffer[bufIdx + 3] = 255; // Full alpha
			}
		}
		return this.canvasBuffer;
	}

	/**
	 * Reset codec state (e.g., when switching video source)
	 */
	reset(): void {
		this.lastFrame.fill(0);
		this.canvasBuffer.fill(0);
		for (let i = 3; i < this.canvasBuffer.length; i += 4) {
			this.canvasBuffer[i] = 255;
		}
	}

	get bufferWidth(): number { return this.width; }
	get bufferHeight(): number { return this.height; }
}
