/* eslint-disable */
// @ts-nocheck
import fs from 'fs';

const filePath = 'C:/X/workspace/jumpa.id/vc/lib/xcu-quantum-decoder.ts';
let content = fs.readFileSync(filePath, 'utf-8');

// 1. Add videoEngineMode property
content = content.replace(
    'public participantRole: "PANELIST" | "AUDIENCE" = "PANELIST";',
    'public participantRole: "PANELIST" | "AUDIENCE" = "PANELIST";\n\tpublic videoEngineMode: "canvas" | "webcodecs" = "canvas";\n\tprivate trackProcessor: any = null;\n\tprivate trackGenerator: any = null;\n\tprivate activeCodecStr: string = "avc1.42E01F";'
);

// 2. Add Codec auto-discovery and Dynamic Decoder creation
const decoderLogic = `
	private async detectBestCodec(): Promise<string> {
		const codecs = [
			"av01.0.04M.08", // AV1
			"vp09.00.10.08", // VP9
			"avc1.42E01F"    // H.264
		];
		for (const c of codecs) {
			try {
				const support = await VideoEncoder.isConfigSupported({
					codec: c,
					width: 1280,
					height: 720,
					bitrate: 2_500_000,
					framerate: 30
				});
				if (support.supported) {
					console.log(\`[QUANTUM WEBCODECS] Hardware GPU Codec Terdeteksi: \${c}\`);
					return c;
				}
			} catch (e) {}
		}
		return "avc1.42E01F"; // Fallback H.264
	}

	private createDecoderForParticipant(senderId: number, codecStr: string = "avc1.42E01F") {
		// Jika decoder sudah ada dan codec-nya berbeda, tutup dulu
		if (this.videoDecoders.has(senderId)) {
			try { this.videoDecoders.get(senderId)!.close(); } catch(e){}
			this.videoDecoders.delete(senderId);
		}

		const decoder = new VideoDecoder({
			output: (frame: any) => {
				this.tryAutoRegisterCanvas(senderId);
				const ctx = this.canvasCtxMap.get(senderId);
				if (ctx) {
					ctx.drawImage(frame, 0, 0, ctx.canvas.width, ctx.canvas.height);
				}
				frame.close();
			},
			error: (e: any) => console.error(\`Decoder Error untuk \${senderId}:\`, e),
		});

		try {
			decoder.configure({ codec: codecStr, codedWidth: 1280, codedHeight: 720 });
			this.videoDecoders.set(senderId, decoder);
			this.firstKeyFrameReceived.set(senderId, false);
			console.log(\`[QUANTUM WEBCODECS] Hardware Decoder (\${codecStr}) siap untuk Partisipan \${senderId}\`);
		} catch (e) {
			console.error("[QUANTUM WEBCODECS] Gagal konfigurasi decoder:", e);
		}
	}
`;

content = content.replace(
	/private createDecoderForParticipant\(senderId: number\) \{[\s\S]*?this\.firstKeyFrameReceived\.set\(senderId, false\);\s*?\n\s*?\/\/ Notifikasi React untuk merender tile/,
	decoderLogic + '\n\n\t\t// Notifikasi React untuk merender tile'
);

// 3. Modify `activateUplink` to support WebCodecs
const uplinkOld = `			const videoTrack = this.mediaStream.getVideoTracks()[0];

			// === Canvas JPEG Pipeline: Works on ALL browsers ===
			const captureVideo = document.createElement("video");`;

const uplinkNew = `			const videoTrack = this.mediaStream.getVideoTracks()[0];

			if (this.videoEngineMode === "webcodecs") {
				// === XCU QUANTUM WEBCODECS (HARDWARE GPU PIPELINE) ===
				console.log("[UPLINK] XCU Quantum WebCodecs (GPU) Diaktifkan!");
				this.activeCodecStr = await this.detectBestCodec();
				let codecId = 0;
				if (this.activeCodecStr.startsWith("av01")) codecId = 2;
				else if (this.activeCodecStr.startsWith("vp09")) codecId = 1;
				
				this.videoEncoder = new VideoEncoder({
					output: async (chunk: any, meta: any) => {
						if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return;
						
						const chunkData = new Uint8Array(chunk.byteLength);
						chunk.copyTo(chunkData);
						
						// ENCRYPTION
						let finalPayload = chunkData;
						let isEncrypted = 0;
						let iv = new Uint8Array(12);
						
						if (this.e2eeKey) {
							window.crypto.getRandomValues(iv);
							const cipherBuffer = await window.crypto.subtle.encrypt(
								{ name: "AES-GCM", iv: iv },
								this.e2eeKey,
								chunkData
							);
							finalPayload = new Uint8Array(cipherBuffer);
							isEncrypted = 2;
						}

						const frameType = chunk.type === "key" ? FRAME_VIDEO_KEY : FRAME_VIDEO_DELTA;
						
						// Jika KeyFrame, sisipkan 1 byte Codec ID di awal payload
						let packetLen = 8 + finalPayload.length;
						if (frameType === FRAME_VIDEO_KEY) packetLen += 1;
						if (isEncrypted) packetLen += 12; // tambah IV
						
						const packet = new Uint8Array(packetLen);
						packet[0] = frameType;
						packet[1] = isEncrypted ? 2 : 1;
						
						const view = new DataView(packet.buffer);
						view.setUint16(2, this.participantId, true);
						
						let offset = 8;
						if (isEncrypted) {
							view.setUint32(4, packetLen - 8, true);
							packet.set(iv, offset);
							offset += 12;
						} else {
							view.setUint32(4, packetLen - 8, true);
						}
						
						if (frameType === FRAME_VIDEO_KEY) {
							packet[offset] = codecId; // Sisipkan Codec ID
							offset += 1;
						}
						
						packet.set(finalPayload, offset);
						this.ws!.send(packet);
						this._frameCount++;
					},
					error: (e: any) => console.error("[QUANTUM WEBCODECS] Encoder Error:", e)
				});
				
				this.videoEncoder.configure({
					codec: this.activeCodecStr,
					width: 1280,
					height: 720,
					hardwareAcceleration: "require",
					bitrate: 2_500_000,
					framerate: 30,
					latencyMode: "realtime"
				});
				
				// Extract frames directly from camera using MediaStreamTrackProcessor
				if (typeof (window as any).MediaStreamTrackProcessor !== "undefined") {
					this.trackProcessor = new (window as any).MediaStreamTrackProcessor({ track: videoTrack });
					const reader = this.trackProcessor.readable.getReader();
					
					// Asynchronous background encoding loop
					(async () => {
						while (this.isRunning && this.videoEngineMode === "webcodecs") {
							try {
								const { done, value: frame } = await reader.read();
								if (done || !frame) break;
								if (this.videoEncoder && this.videoEncoder.state === "configured") {
									// Force KeyFrame every 30 frames (1 second) for resilience
									this.videoEncoder.encode(frame, { keyFrame: this._frameCount % 30 === 0 });
								}
								frame.close();
							} catch (e) { break; }
						}
					})();
				}
			}

			// === Canvas JPEG Pipeline: Works on ALL browsers ===
			const captureVideo = document.createElement("video");`;

content = content.replace(uplinkOld, uplinkNew);

// 4. Wrap setInterval to only run when canvas mode
content = content.replace(
	`const jpegInterval = setInterval(() => {`,
	`const jpegInterval = setInterval(() => {\n\t\t\t\tif (this.videoEngineMode === "webcodecs") return; // Bypass if using Quantum Engine`
);

// 5. Update receiver to handle Codec ID for FRAME_VIDEO_KEY and Decryption
const receiverOld = `								if (frameType === FRAME_VIDEO_DELTA || frameType === FRAME_VIDEO_KEY) {
									if (!this.videoDecoders.has(senderId)) {
										this.createDecoderForParticipant(senderId);
									}
									if (frameType === FRAME_VIDEO_DELTA && !this.firstKeyFrameReceived.get(senderId)) {
										continue;
									}
									if (frameType === FRAME_VIDEO_KEY) {
										this.firstKeyFrameReceived.set(senderId, true);
									}
									this.tryAutoRegisterCanvas(senderId);
									const decoder = this.videoDecoders.get(senderId);
									if (decoder && decoder.state === "configured") {
										const chunk = new EncodedVideoChunk({
											type: frameType === FRAME_VIDEO_KEY ? "key" : "delta",
											timestamp: performance.now() * 1000,
											data: payloadData,
										});
										decoder.decode(chunk);
									}
									continue;
								}`;

const receiverNew = `								if (frameType === FRAME_VIDEO_DELTA || frameType === FRAME_VIDEO_KEY) {
									let rawData = payloadData;
									let isDecryptionFailed = false;
									
									if (quality === 2) { // Encrypted WebCodecs chunk
										if (this.e2eeKey && payloadData.length > 12) {
											const iv = payloadData.slice(0, 12);
											const cipher = payloadData.slice(12);
											try {
												const plainBuf = await window.crypto.subtle.decrypt(
													{ name: "AES-GCM", iv: iv },
													this.e2eeKey,
													cipher
												);
												rawData = new Uint8Array(plainBuf);
											} catch (e) {
												isDecryptionFailed = true;
											}
										} else {
											isDecryptionFailed = true;
										}
									}

									if (isDecryptionFailed) continue;

									// Extract Codec ID if KeyFrame
									let chunkData = rawData;
									let codecStr = "avc1.42E01F"; // Default
									
									if (frameType === FRAME_VIDEO_KEY) {
										const codecId = rawData[0];
										chunkData = rawData.slice(1);
										if (codecId === 2) codecStr = "av01.0.04M.08";
										else if (codecId === 1) codecStr = "vp09.00.10.08";
										
										// Re-create decoder if codec changes or not exists
										const existing = this.videoDecoders.get(senderId);
										if (!existing || (existing as any)._currentCodec !== codecStr) {
											this.createDecoderForParticipant(senderId, codecStr);
											const newDec = this.videoDecoders.get(senderId);
											if (newDec) (newDec as any)._currentCodec = codecStr;
										}
										this.firstKeyFrameReceived.set(senderId, true);
									} else {
										if (!this.videoDecoders.has(senderId)) {
											this.createDecoderForParticipant(senderId);
										}
									}

									if (frameType === FRAME_VIDEO_DELTA && !this.firstKeyFrameReceived.get(senderId)) {
										continue;
									}
									
									this.tryAutoRegisterCanvas(senderId);
									const decoder = this.videoDecoders.get(senderId);
									if (decoder && decoder.state === "configured") {
										try {
											const chunk = new (window as any).EncodedVideoChunk({
												type: frameType === FRAME_VIDEO_KEY ? "key" : "delta",
												timestamp: performance.now() * 1000,
												data: chunkData,
											});
											decoder.decode(chunk);
										} catch (e) {
											// Ignore decode errors to ensure Zero Error crash
										}
									}
									continue;
								}`;

content = content.replace(receiverOld, receiverNew);

// 6. Update deactivateUplink to cleanly close trackProcessor
content = content.replace(
	`if (this.videoEncoder && this.videoEncoder.state !== "closed") {`,
	`if (this.videoEncoder && this.videoEncoder.state !== "closed") {\n\t\t\tthis.videoEncoder.close();\n\t\t\tthis.videoEncoder = null;\n\t\t}\n\t\tif (this.trackProcessor) {\n\t\t\ttry { /* Let GC handle trackProcessor */ } catch(e){}\n\t\t\tthis.trackProcessor = null;\n\t\t}\n\t\tif (false) {`
);

// Hot Swap Method
const hotSwap = `
	public async hotSwapVideoEngine(mode: "canvas" | "webcodecs") {
		if (this.videoEngineMode === mode) return;
		console.log(\`[QUANTUM HOT-SWAP] Mengalihkan Engine ke: \${mode}\`);
		this.videoEngineMode = mode;
		
		// Jika kamera sedang nyala, kita matikan lalu nyalakan secara instan tanpa mematikan koneksi WebTransport
		if (this.mediaStream) {
			await this.deactivateUplink();
			await this.activateUplink('camera');
		}
	}
`;
content = content.replace('public unlockAudio() {', hotSwap + '\n\tpublic unlockAudio() {');

fs.writeFileSync(filePath, content, 'utf-8');
console.log('Successfully updated xcu-quantum-decoder.ts');
