<?php

//KME-A: 194.105.10.103 port 20251 user root and password 3I2R6HE0Did4Yx
//KME-B: 194.105.10.104 port 20251 user root and password 33UsX10MrEc7o1efhe

function get_random_qustream_block($filepath){
    $blockSize = 2048; // 2048 bytes = 16,384 bits

    if (!is_readable($filepath)) {
        error_log("QRND file not found or not readable: $filepath");
        return false;
    }

    $filesize = filesize($filepath);
    if ($filesize < $blockSize) {
        error_log("QRND file too small: $filepath");
        return false;
    }

    // Pick a random offset so we dont read past end
    $maxOffset = $filesize - $blockSize;
    $offset = random_int(0, $maxOffset);

    $handle = fopen($filepath, 'rb');
    if (!$handle) {
        error_log("Failed to open QRND file: $filepath");
        return false;
    }

    // Seek and read
    fseek($handle, $offset);
    $rawBytes = fread($handle, $blockSize);
    fclose($handle);

    if ($rawBytes === false || strlen($rawBytes) !== $blockSize) {
        error_log("Failed to read full QuStream block from QRND file.");
        return false;
    }

    // Convert to binary string
    $bitstring = '';
    for ($i = 0; $i < $blockSize; $i++) {
        $bitstring .= str_pad(decbin(ord($rawBytes[$i])), 8, '0', STR_PAD_LEFT);
    }

    return $bitstring; // Now a 16,384-bit string
}


$qrnd_path = __DIR__ . '/QRNG1.bin';
$qustream_block = get_random_qustream_block($qrnd_path);

if (!$qustream_block || strlen($qustream_block) !== 16384) {
    die("Failed to generate valid QuStream block from QRND data.");
}

// Now use $qustream_block for display or key embedding



?>


<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <title>ETSI GS QKD 014</title>
  <style>
    :root {
      --bit-size: 5px;
    }
    body {
      font-family: sans-serif;
      margin: 10px;
      background-color: #0B1217;
      color: #ddd;
    }
    #grid_a {
      display: grid;
      grid-template-columns: repeat(256, var(--bit-size));
      grid-template-rows: repeat(64, var(--bit-size));
      gap: 1px;
      margin: auto;
      width: fit-content;
    }
    #grid_b {
      display: grid;
      grid-template-columns: repeat(256, var(--bit-size));
      grid-template-rows: repeat(64, var(--bit-size));
      gap: 1px;
      margin: auto;
      width: fit-content;
    }
    .bit {
      width: var(--bit-size);
      height: var(--bit-size);
      background-color: #2c2c2c;
      position: relative;
    }
    .bit.one { background-color:    #797d7f   ; }
    .bit.one_blue { background-color: #21618c; }
    .bit.qkd_red { background-color: #DE3163; }
    .bit.qkd_green { background-color: #40E0D0; }
    .bit:hover::after {
      content: attr(data-tooltip);
      position: absolute;
      background: #333;
      color: #fff;
      font-size: 0.75em;
      padding: 2px 6px;
      border-radius: 3px;
      white-space: pre;
      top: -30px;
      left: 0;
      z-index: 10;
      pointer-events: none;
    }
    button {
      font-family: monospace;
      font-size: 0.8em;
      padding: 5px 5px;
      margin-right: 5px;
      background: #333;
      border: 1px solid #555;
      color: white;
      border-radius: 4px;
      cursor: pointer;
	  white-space: nowrap;
    }
    button:hover {
      background-color: #444;
    }
    li{
      margin-left: 40px;
    }
    .controls-row {
      display: flex;
      align-items: center;
      margin-top: 10px;
      margin-left: auto;
      margin-right: auto;
      margin-bottom: 10px;
      width: fit-content;
    }
    .label {
      font-weight: normal;
      margin-right: 10px;
    }
    .output {
      font-family: monospace;
      font-size: 1.3em;
      text-align: left;
      background: #2e2e2e;
      padding: 10px 20px;
      border-radius: 5px;
      border: 1px solid #444;
      color: e6e6e6;
      width: 982px;

		display: flex;
		flex-direction: row;
		align-items: center;
		white-space: nowrap;

	  word-break: break-word;           /* Break long strings */
	  white-space: pre-wrap;            /* Preserve spaces and wrap lines */
	  overflow-wrap: break-word;        /* Alternative for better compatibility */    }
    .output2 {
      font-family: monospace;
      font-size: 1.4em;
      text-align: left;
      background: #2e2e2e;
      padding: 20px 20px;
      border-radius: 5px;
      border: 1px solid #444;
      color: e6e6e6;
      width: 982px;

		display: flex;
		flex-direction: row;
		align-items: center;
		white-space: nowrap;

	  word-break: break-word;           /* Break long strings */
	  white-space: pre-wrap;            /* Preserve spaces and wrap lines */
	  overflow-wrap: break-word;        /* Alternative for better compatibility */    }
	.tab {
      font-family: monospace;
      font-size: 1.3em;
	  display:inline-block;
      text-align: left;
      background: #2e2e2e;
      padding: 10px 20px;
      border-radius: 5px;
      border: 1px solid #444;
      color: e6e6e6;
      width: 211px;
	  word-break: break-word;           /* Break long strings */
	  white-space: pre-wrap;            /* Preserve spaces and wrap lines */
	  overflow-wrap: break-word;        /* Alternative for better compatibility */    }
	h3{
      font-family: monospace;
      font-size: 1.50em;
      font-weight: normal;
      margin-top: 30px;
      margin-bottom: 30px;
	}
	a{
      color: #ddd;
	}
    .toggle-buttons {
      //display: flex;
      gap: 5px;
      //padding: 5px;
    }

    .toggle-button {
      //padding: 5px 5px;
      font-size: 0.8em;
      font-weight: bold;
      border: none;
      border-radius: 5px;
      cursor: pointer;
      opacity: 0.4;
      transition: opacity 0.2s ease-in-out;
		white-space: nowrap;
    }
    .qtoggle-button {
      //padding: 5px 5px;
      font-size: 0.8em;
      font-weight: bold;
      border: none;
      border-radius: 5px;
      cursor: pointer;
      opacity: 0.4;
      transition: opacity 0.2s ease-in-out;
		white-space: nowrap;
    }


    .qtoggle-button.prng {
      background-color:  #808b96;
      color: white;
    }

    .qtoggle-button.qrng {
      background-color: #21618c;
      color: white;
    }
    .qtoggle-button.active {
      opacity: 1.0;
    }


    .toggle-button.test {
      background-color: red;
      color: white;
    }

    .toggle-button.live {
      background-color: green;
      color: white;
    }

    .toggle-button.active {
      opacity: 1.0;
    }
   table {
    margin: auto;
  }




  </style>
</head>
<body>
<table style="text-align: top;">
	<tr>
		<td style="vertical-align: middle;" ><center>
			<div id="natopoc" class="output" ><div class="toggle-buttons">ETSI GS QKD 014 Integration with  <button
			id="btnPRNG" class="qtoggle-button prng active">PRNG</button><button id="btnQRNG" class="qtoggle-button qrng">QRNG</button> QuStream-QKD - Proof of Concept                   <button
			id="btnTest" class="toggle-button test active">TEST</button><button id="btnLive" class="toggle-button live">LIVE</button></div></div>
		</td>
	</tr>
	<tr>
		<td style="vertical-align: middle;" height=15><center>
			<div  id="bt_sae_a_div" class="output">SAE A (Master) 194.105.10.103                                                 <button
			id="bt_sae_a" onclick="get_kme_a()"> Get Key [ SAE_ID='B' ] => KME A </button></div>
		</td>
	</tr>
	<tr>
		<td style="vertical-align: middle;" height=15><center>
			<div id="sae_a" class="output">KME A Key ID:<br>KME A QKD Key:</div>
		</td>
	</tr>
	<tr>
		<td style="vertical-align: top;"><div id="grid_a"></div> </td>
	</tr>
	<tr>
		<td style="vertical-align: middle;" height=15><center>
			<div id="bt_sae_b_div" class="output">SAE B (Slave)  194.105.10.104                                         <button id="bt_sae_b" onclick=""> Get Key [ ThisID, KeyID=empty ] => KME B </button></div>
		</td>
	</tr>
	<tr>
		<td style="vertical-align: middle;" height=15><center>
			<div id="sae_b" class="output">KME B Key ID:<br>KME B QKD Key:</div>
		</td>
	</tr>
	<tr>
		<td style="vertical-align: top;"><div id="grid_b"></div> </td>
	</tr>
</table>
  <script>


    const blockSize = 16384;
    const keySize = 256;
    const grid_a = document.getElementById("grid_a");
    const grid_b = document.getElementById("grid_b");
    const bitDivs = [];

    var qkdKey;
	var active_colour = "red";
	var active_grid_colour = "PRNG";
    var qkdOut_sae_a = document.getElementById("sae_a");
    var qkdOut_sae_b = document.getElementById("sae_b");
    var bt_sae_a_div = document.getElementById("bt_sae_a_div");
    var bt_sae_b_div = document.getElementById("bt_sae_b_div");
	var btnTest = document.getElementById('btnTest');
	var btnLive = document.getElementById('btnLive');
	var btnPRNG = document.getElementById('btnPRNG');
	var btnQRNG = document.getElementById('btnQRNG');
	var btnKeyA  = document.getElementById('bt_sae_a');
	var btnKeyB  = document.getElementById('bt_sae_b');

	btnKeyA.style.backgroundColor = active_colour;
	btnKeyB.style.backgroundColor = active_colour;

    function adjustBitSize() {
      const margin = 40;
      const maxWidth = window.innerWidth - margin;
      const bitSize = 5;
      document.documentElement.style.setProperty('--bit-size', `${bitSize-2}px`);

    }
    window.addEventListener("resize", adjustBitSize);
    adjustBitSize();
	function KME_A_getkey(){
		qkdKey = null;
		KEY_ID = null;
    	qkdKey = Array.from({ length: keySize }, () => Math.random() < 0.5 ? 0 : 1);
    	KEY_ID = Array.from({ length: 64 }, () => Math.random() < 0.5 ? 0 : 1);
	}

	async function get_kme_a() {
	  KME_A_getkey();
	  grid_a.innerHTML = "";
	  grid_b.innerHTML = "";
	  qkdOut_sae_a.innerHTML = "KME A Key ID:<br>KME A QKD Key:";
   	  qkdOut_sae_b.innerHTML = "KME B Key ID:<br>KME B QKD Key:";
	  const chunkLengths = splitIntoChunks(keySize);

	  //const blockBits = Array.from({ length: blockSize }, () => Math.random() < 0.5 ? 0 : 1);

	  const blockBits = await fetch("get_qustream_block.php")
		.then(res => res.json())
		.then(data => data.bitstring.split('').map(Number));

	  const used = new Set();
	  const qkdMeta = [];
	  let keyBitIndex = 0;
	  function findContiguousFreeRange(length) {
		for (let tries = 0; tries < 10000; tries++) {
		  const start = Math.floor(Math.random() * (blockSize - length));
		  let collision = false;
		  for (let i = 0; i < length; i++) {
			if (used.has(start + i)) {
			  collision = true;
			  break;
			}
		  }
		  if (!collision) return start;
		}
		throw new Error("Couldn't find contiguous space");
	  }
	  chunkLengths.forEach((length, chunkIndex) => {
		const start = findContiguousFreeRange(length);
		for (let i = 0; i < length; i++) {
		  const pos = start + i;
		  const bit = qkdKey[keyBitIndex];
		  blockBits[pos] = bit;
		  used.add(pos);
		  qkdMeta.push({
			pos,
			chunkIndex,
			chunkLength: length,
			bitValue: bit
		  });
		  keyBitIndex++;
		}
	  });
	  for (let i = 0; i < blockSize; i++) {
		const bit = document.createElement("div");
		bit.classList.add("bit");
		if(active_grid_colour == "PRNG"){
			if (blockBits[i] === 1) bit.classList.add("one");
		}else{
			if (blockBits[i] === 1) bit.classList.add("one_blue");
		}
		const meta = qkdMeta.find(q => q.pos === i);
		let tip = `Index: ${i}\nValue: ${blockBits[i]}`;
		if (meta) {
		  if(active_colour == "red"){
		  	bit.classList.add("qkd_red");
		  }else{
		  	bit.classList.add("qkd_green");
		  }
		  tip += `\nQKD Chunk #${meta.chunkIndex + 1}\nLength: ${meta.chunkLength}`;
		}
		bit.setAttribute("data-tooltip", tip);
		grid_a.appendChild(bit);
	  }
	  qkdOut_sae_a.innerHTML = "KME A Key ID:   " + binToHex(KEY_ID.join(""))  + "<br>KME A QKD Key:  " + binToHex(qkdKey.join(""));




	  if(active_colour == "green"){
		  bt_sae_b_div.innerHTML = "SAE B (Slave)  000.000.000.000                             <button id=\"bt_sae_b\" onclick=\"get_kme_b()\"> Get Key [ ThisID, KeyID='"  +binToHex(KEY_ID.join(""))+  "' ] => KME B </button>";
	  }else{
		  bt_sae_b_div.innerHTML = "SAE B (Slave)  194.105.10.104                              <button id=\"bt_sae_b\" onclick=\"get_kme_b()\"> Get Key [ ThisID, KeyID='"  +binToHex(KEY_ID.join(""))+  "' ] => KME B </button>";
	  }




  	  btnKeyB  = document.getElementById('bt_sae_b');
	  btnKeyB.style.backgroundColor = active_colour;
	}
	async function get_kme_b() {
	  grid_b.innerHTML = ""; // Clear existing grid
	  const chunkLengths = splitIntoChunks(keySize);


	  //const blockBits = Array.from({ length: blockSize }, () => Math.random() < 0.5 ? 0 : 1);
	  const blockBits = await fetch("get_qustream_block.php")
		.then(res => res.json())
		.then(data => data.bitstring.split('').map(Number));


	  const used = new Set();
	  const qkdMeta = [];
	  let keyBitIndex = 0;
	  function findContiguousFreeRange(length) {
		for (let tries = 0; tries < 10000; tries++) {
		  const start = Math.floor(Math.random() * (blockSize - length));
		  let collision = false;
		  for (let i = 0; i < length; i++) {
			if (used.has(start + i)) {
			  collision = true;
			  break;
			}
		  }
		  if (!collision) return start;
		}
		throw new Error("Couldn't find contiguous space");
	  }
	  chunkLengths.forEach((length, chunkIndex) => {
		const start = findContiguousFreeRange(length);
		for (let i = 0; i < length; i++) {
		  const pos = start + i;
		  const bit = qkdKey[keyBitIndex];
		  blockBits[pos] = bit;
		  used.add(pos);
		  qkdMeta.push({
			pos,
			chunkIndex,
			chunkLength: length,
			bitValue: bit
		  });
		  keyBitIndex++;
		}
	  });
	  for (let i = 0; i < blockSize; i++) {
		const bit = document.createElement("div");
		bit.classList.add("bit");
		if(active_grid_colour == "PRNG"){
			if (blockBits[i] === 1) bit.classList.add("one");
		}else{
			if (blockBits[i] === 1) bit.classList.add("one_blue");
		}
		const meta = qkdMeta.find(q => q.pos === i);
		let tip = `Index: ${i}\nValue: ${blockBits[i]}`;
		if (meta) {
		  if(active_colour == "red"){
		  	bit.classList.add("qkd_red");
		  }else{
		  	bit.classList.add("qkd_green");
		  }
		  tip += `\nQKD Chunk #${meta.chunkIndex + 1}\nLength: ${meta.chunkLength}`;
		}
		bit.setAttribute("data-tooltip", tip);
		grid_b.appendChild(bit);
	  }
   	qkdOut_sae_b.innerHTML = "KME B Key ID:   " + binToHex(KEY_ID.join(""))  + "<br>KME B QKD Key:  " + binToHex(qkdKey.join(""));
	}
	function splitIntoChunks(total, min = 4, max = 15) {
	  const chunks = [];
	  let remaining = total;
	  while (remaining > 0) {
		const maxChunk = Math.min(max, remaining);
		const minChunk = Math.min(min, maxChunk);
		const len = Math.floor(Math.random() * (maxChunk - minChunk + 1)) + minChunk;
		chunks.push(len);
		remaining -= len;
	  }
	  return chunks;
	}
	function binToHex2(binStr) {
	  const hexStr = binStr
		.match(/.{1,4}/g)                      // Split into 4-bit chunks
		.map(b => parseInt(b, 2).toString(16)) // Convert to hex
		.join("");                             // Join into full hex string
	  return hexStr.match(/.{1,8}/g).join(" ");
	}
	function binToHex(binStr) {
	  return binStr
		.match(/.{1,4}/g)                        // Split binary into 4-bit groups
		.map(b => parseInt(b, 2).toString(16))   // Convert to hex
		.join("")                                // Join all hex chars
		.match(/.{1,8}/g)                        // Then split hex into 8-char blocks
		.join("-");                              // Join with space
	}
    function binToHex3(binStr) {
      return binStr.match(/.{1,4}/g).map(b => parseInt(b, 2).toString(16)).join("");
    }
	btnTest.addEventListener('click', () => {
	  active_colour = "red";
	  if(active_colour == "green"){
		bt_sae_a_div.innerHTML = "SAE A (Master) 000.000.000.000                                                <button id=\"bt_sae_a\" onclick=\"get_kme_a()\"> Get Key [ SAE_ID='B' ] => KME A </button>";
	  	bt_sae_b_div.innerHTML = "SAE B (Slave)  000.000.000.000                                        <button id=\"bt_sae_b\" onclick=\"\"> Get Key [ ThisID, KeyID=empty ] => KME B </button>";
	  }else{
		bt_sae_a_div.innerHTML = "SAE A (Master) 194.105.10.103                                                 <button id=\"bt_sae_a\" onclick=\"get_kme_a()\"> Get Key [ SAE_ID='B' ] => KME A </button>";
	  	bt_sae_b_div.innerHTML = "SAE B (Slave)  194.105.10.104                                         <button id=\"bt_sae_b\" onclick=\"\"> Get Key [ ThisID, KeyID=empty ] => KME B </button>";
	  }
	  btnTest.classList.add('active');
	  btnLive.classList.remove('active');
	  btnKeyA.style.backgroundColor = "red";
	  btnKeyB.style.backgroundColor = "red";
	  grid_a.innerHTML = ""; // Clear existing grid
	  grid_b.innerHTML = ""; // Clear existing grid
  	  btnKeyA  = document.getElementById('bt_sae_a');
	  btnKeyA.style.backgroundColor = active_colour;
  	  btnKeyB  = document.getElementById('bt_sae_b');
	  btnKeyB.style.backgroundColor = active_colour;

	  qkdOut_sae_a.innerHTML = "KME A Key ID:<br>KME A QKD Key:";
   	  qkdOut_sae_b.innerHTML = "KME B Key ID:<br>KME B QKD Key:";
	});
	btnLive.addEventListener('click', () => {
	  active_colour = "green";
	  if(active_colour == "green"){
		bt_sae_a_div.innerHTML = "SAE A (Master) 000.000.000.000                                                <button id=\"bt_sae_a\" onclick=\"get_kme_a()\"> Get Key [ SAE_ID='B' ] => KME A </button>";
	  	bt_sae_b_div.innerHTML = "SAE B (Slave)  000.000.000.000                                        <button id=\"bt_sae_b\" onclick=\"\"> Get Key [ ThisID, KeyID=empty ] => KME B </button>";
	  }else{
		bt_sae_a_div.innerHTML = "SAE A (Master) 194.105.10.103                                                 <button id=\"bt_sae_a\" onclick=\"get_kme_a()\"> Get Key [ SAE_ID='B' ] => KME A </button>";
	  	bt_sae_b_div.innerHTML = "SAE B (Slave)  194.105.10.104                                         <button id=\"bt_sae_b\" onclick=\"\"> Get Key [ ThisID, KeyID=empty ] => KME B </button>";
	  }
	  btnLive.classList.add('active');
	  btnTest.classList.remove('active');
	  btnKeyA.style.backgroundColor = "green";
	  btnKeyB.style.backgroundColor = "green";
	  grid_a.innerHTML = ""; // Clear existing grid
	  grid_b.innerHTML = ""; // Clear existing grid
  	  btnKeyA  = document.getElementById('bt_sae_a');
	  btnKeyA.style.backgroundColor = active_colour;
  	  btnKeyB  = document.getElementById('bt_sae_b');
	  btnKeyB.style.backgroundColor = active_colour;
	  qkdOut_sae_a.innerHTML = "KME A Key ID:<br>KME A QKD Key:";
   	  qkdOut_sae_b.innerHTML = "KME B Key ID:<br>KME B QKD Key:";
	});


	btnPRNG.addEventListener('click', () => {
	  grid_a.innerHTML = ""; // Clear existing grid
	  grid_b.innerHTML = ""; // Clear existing grid
	  if(active_colour == "green"){
		bt_sae_a_div.innerHTML = "SAE A (Master) 000.000.000.000                                                <button id=\"bt_sae_a\" onclick=\"get_kme_a()\"> Get Key [ SAE_ID='B' ] => KME A </button>";
	  	bt_sae_b_div.innerHTML = "SAE B (Slave)  000.000.000.000                                        <button id=\"bt_sae_b\" onclick=\"\"> Get Key [ ThisID, KeyID=empty ] => KME B </button>";
	  }else{
		bt_sae_a_div.innerHTML = "SAE A (Master) 194.105.10.103                                                 <button id=\"bt_sae_a\" onclick=\"get_kme_a()\"> Get Key [ SAE_ID='B' ] => KME A </button>";
	  	bt_sae_b_div.innerHTML = "SAE B (Slave)  194.105.10.104                                         <button id=\"bt_sae_b\" onclick=\"\"> Get Key [ ThisID, KeyID=empty ] => KME B </button>";
	  }
	  btnPRNG.classList.add('active');
	  btnQRNG.classList.remove('active');
	  active_grid_colour = "PRNG";
  	  btnKeyA  = document.getElementById('bt_sae_a');
	  btnKeyA.style.backgroundColor = active_colour;
  	  btnKeyB  = document.getElementById('bt_sae_b');
	  btnKeyB.style.backgroundColor = active_colour;
	  qkdOut_sae_a.innerHTML = "KME A Key ID:<br>KME A QKD Key:";
   	  qkdOut_sae_b.innerHTML = "KME B Key ID:<br>KME B QKD Key:";
	});
	btnQRNG.addEventListener('click', () => {
	  grid_a.innerHTML = ""; // Clear existing grid
	  grid_b.innerHTML = ""; // Clear existing grid
	  if(active_colour == "green"){
		bt_sae_a_div.innerHTML = "SAE A (Master) 000.000.000.000                                                <button id=\"bt_sae_a\" onclick=\"get_kme_a()\"> Get Key [ SAE_ID='B' ] => KME A </button>";
	  	bt_sae_b_div.innerHTML = "SAE B (Slave)  000.000.000.000                                        <button id=\"bt_sae_b\" onclick=\"\"> Get Key [ ThisID, KeyID=empty ] => KME B </button>";
	  }else{
		bt_sae_a_div.innerHTML = "SAE A (Master) 194.105.10.103                                                 <button id=\"bt_sae_a\" onclick=\"get_kme_a()\"> Get Key [ SAE_ID='B' ] => KME A </button>";
	  	bt_sae_b_div.innerHTML = "SAE B (Slave)  194.105.10.104                                         <button id=\"bt_sae_b\" onclick=\"\"> Get Key [ ThisID, KeyID=empty ] => KME B </button>";
	  }
	  btnQRNG.classList.add('active');
	  btnPRNG.classList.remove('active');
	  active_grid_colour = "QRNG";
  	  btnKeyA  = document.getElementById('bt_sae_a');
	  btnKeyA.style.backgroundColor = active_colour;
  	  btnKeyB  = document.getElementById('bt_sae_b');
	  btnKeyB.style.backgroundColor = active_colour;
	  qkdOut_sae_a.innerHTML = "KME A Key ID:<br>KME A QKD Key:";
   	  qkdOut_sae_b.innerHTML = "KME B Key ID:<br>KME B QKD Key:";
	});


  </script>


</body>
</html>

<?php

?>
