<?php
/*
* data.php
* http://tasuke.dna.affrc.go.jp/
*
* Author: Norio Tabei
*
* Copyright (c) 2025 National Institute of Agrobiological Sciences
*
*/

//TODO Consider order.conf.

require '../conf/config.php';
require './common.php';
require './commonDB.php';
//////////////////////////////////  Connecting with DB

if( !$conn = new mysqli($host, $user, $pswd, $db)) {
	error_log("Connection failed.\n");
	exit;
}

(isset($_POST['prm_Lists']) && !empty($_POST['prm_Lists'])) ? $prm_Lists = $_POST['prm_Lists'] : $prm_Lists = NULL;
(isset($_POST['prm_Zoom']) && !empty($_POST['prm_Zoom'])) ? $prm_Zoom = $_POST['prm_Zoom'] : $prm_Zoom = NULL;

//(isset($_POST['prm_QUAL']) && !empty($_POST['prm_QUAL'])) ? $prm_QUAL = $_POST['prm_QUAL'] : $prm_QUAL = 1;
//(isset($_POST['prm_DP']) && !empty($_POST['prm_DP'])) ? $prm_DP = $_POST['prm_DP'] : $prm_DP = 1;
//(isset($_POST['prm_DP4']) && !empty($_POST['prm_DP4'])) ? $prm_DP4 = $_POST['prm_DP4'] : $prm_DP4 = 1;
(isset($_POST['prm_Chrs']) && !empty($_POST['prm_Chrs'])) ? $prm_Chrs = $_POST['prm_Chrs'] : $prm_Chrs = "";
(isset($_POST['prm_Mode']) && !empty($_POST['prm_Mode'])) ? $prm_Mode = $_POST['prm_Mode'] : $prm_Mode = "";

//TODO Check $prm_Chrs is correct.
//TODO Check order.conf

$accId2Db = getAccdbs($conn, $prm_Lists);

//$chrs = getChrs($conn);
//$lenChrs = count($chrs);

echo "{";

// Admixture
echo "\"admixture\":";
if(file_exists($admixtureIdListPath) && count(array_keys($admixtureK2Path)) > 0) {
	printAdmixture($admixtureIdListPath, $admixtureK2Path);
} else {
	echo "null";
}

// PCA
echo ",\"pca\":";
if(file_exists($pcaVecPath)) {
	printPca($pcaVecPath, $pcaValPath);
} else {
	echo "null";
}

// VariantCount(Density).
echo ",\"varcount\":";
//if(false){
if(file_exists($variantDensPath)) {
	 printVariantCount($variantDensPath);
} else {
	echo "null";
}

echo "}";

function printPca($fileVec,  $fileVal) {
	global $prm_Mode, $accId2Db;
	$packFormat = "f";	// float(generally 4byte)
	$floatByte = strlen(pack($packFormat, (float)-1.0));

	$dbOrder = array();
	$packFormat = "f";	// 4byte float
	$binStr = "";
	$rowCols = 0;

	$valArr = null;
	if(file_exists($fileVal)) {
		$valArr = array();
		$fp = fopen($fileVal, "r");
		while($line = fgets($fp)) {
			if(!preg_match("/\S/", $line)) continue;
			$line = rtrim($line);
			$valArr[] = (float)$line;
		}
		fclose($fp);
	}

	$readHeader = false;
	$fp = fopen($fileVec, "r");
	while($line = fgets($fp)) {
		if(!preg_match("/\S/", $line)) continue;
		$line = rtrim($line);

		$onerowArray = preg_split("/\s+/", $line);
		if(!$readHeader) {
			$rowCols = count($onerowArray);
			if($prm_Mode === "blockSizeOnly") {
				echo  json_encode(array(
					"data" => array(),
					"stat" => array("pcs" => ($rowCols - 2), "floatByte" => $floatByte, "val" => $valArr)
				));
				return;
			}
			$readHeader = true;
		}
		if(preg_match("/FID$/i", $onerowArray[0])) continue;	// Skip header.	

		$accdb = $accId2Db[$onerowArray[1]];
		$dbOrder[] = array("fid" => $onerowArray[0], "db" => $accdb);
		for($i=2; $i<$rowCols; $i++) {
			 $binStr .= pack($packFormat, (float)$onerowArray[$i]);
		}
	}
	fclose($fp);
/*
	$valArr = null;
	if(file_exists($fileVal)) {
		$valArr = array();
		$fp = fopen($fileVal, "r");
		while($line = fgets($fp)) {
			if(!preg_match("/\S/", $line)) continue;
			$line = rtrim($line);
			$valArr[] = (float)$line;
		}
		fclose($fp);
	}
*/
	echo json_encode(array(
		"data" => array("b64" => base64_encode($binStr)),
		"stat" => array("pcs" => ($rowCols - 2), "floatByte" => $floatByte, "dbOrder" =>  $dbOrder, "val" => $valArr)
	));
}

function printAdmixture($idOrderFile, $k2file) {
	global $prm_Mode, $accId2Db;

	// Check if the path is valid.
	$preKs = array_keys($k2file);
	$lenPreKs = count($preKs);
	$ks = array();
	for($i=0; $i<$lenPreKs; $i++) {
		if(!empty($k2file[$preKs[$i]]) && file_exists($k2file[$preKs[$i]])) array_push($ks, $preKs[$i]);
	}
	sort($ks);

	$packFormat = "f";	// float(generally 4byte)
	$floatByte = strlen(pack($packFormat, (float)-1.0));

	if($prm_Mode === "blockSizeOnly") {
		echo json_encode(array(
			"data" => array(),
			"stat" => array("kmers" => $ks, "floatByte" => $floatByte)
		));
		return;
	}

	// Make accId list.
	$idOrder = array();
	$accIdx = -1;
	$fp = fopen($idOrderFile, "r");
	while($line = fgets($fp)) {
		$accIdx++;
		$line = rtrim($line);
		if(!preg_match("/\S/", $line)) continue;

		$onerowArray = preg_split("/\s+/", $line);
		$lenValCols = count($onerowArray);
		if($lenValCols === 1) {
			$idOrder[$accIdx] = $onerowArray[0]; 
		} else {
			// fam file
			//TODO Are there any ID list formats other than "fam"?
			$idOrder[$accIdx] = $onerowArray[1];
		}
	}
	fclose($fp);

	$preData = array();
	$lenKs = count($ks);
	$k2cols = array();
	for($i=0; $i<$lenKs; $i++) {
		$rowIdx = -1;
		$fp = fopen($k2file[$ks[$i]], "r");
		while($line = fgets($fp)) {
			$rowIdx++;
			if($rowIdx>$accIdx) break;
			if(!array_key_exists($rowIdx, $idOrder) || empty($idOrder[$rowIdx])) continue;

			$line = rtrim($line);
			if(!preg_match("/\S/", $line)) continue;

			$onerowArray = preg_split("/\s+/", $line);
			$accid = $idOrder[$rowIdx];
//			$accid = array_shift($onerowArray);

			$accdb = $accId2Db[$accid];
			if(!array_key_exists($accdb, $preData)) $preData[$accdb] = array();
			$preData[$accdb][$ks[$i]] = $onerowArray;
			if(!array_key_exists($ks[$i], $k2cols)) $k2cols[$ks[$i]] = count($onerowArray);
		}
		fclose($fp);
	}

	$data = array();
	$accdbs = array_keys($preData);
	$lenAccdbs = count($accdbs);
	for($i=0; $i<$lenAccdbs; $i++) {
		$accdb = $accdbs[$i];
		$binStr = "";
		for($j=0; $j<$lenKs; $j++) {
			$kmer = $ks[$j];
			$cols =  $k2cols[$kmer];
			if(array_key_exists($kmer, $preData[$accdb])) {
				$lenValCols = count($preData[$accdb][$kmer]);
				for($k=0; $k<$cols; $k++) {
					if($k >= $lenValCols) {
						$binStr .= pack($packFormat, (float)0);
					} else {
						$binStr .= pack($packFormat, (float)$preData[$accdb][$kmer][$k]);
					}
				}
				unset($preData[$accdb][$kmer]);	// Momory reduce
			} else {
				// If Acc is missing from the file, write a binary with a negative number at the beginning.
				$binStr .= pack($packFormat, (float)-1.0);
				$binStr .= str_repeat(pack($packFormat, (float)0), $cols-1);
			}
		}
		$data[$accdb] = array("b64" => base64_encode($binStr));
	}

	$cols = array();	// Make it work even if the kmer and file correspondence is incorrect.
	for($j=0; $j<$lenKs; $j++) {
		$cols[] = $ks[$j];
	}

	echo json_encode(array(
		"data" => $data,
		"stat" => array("kmers" => $ks, "cols" => $cols, "floatByte" => $floatByte)
	));	
}
function printVariantCount($file) {
	global $prm_Chrs, $prm_Zoom, $prm_Mode;

	echo "{\"data\":{";

	$fp = fopen($file, "r");

	$chrBlocks = array();
	$blockW = unpack("L", fread($fp, 4))[1];
	if($prm_Mode === "blockSizeOnly") {
		echo "},\"stat\":". json_encode(array("blockSize" => $blockW)). "}";
		return;
	}

	if(is_null($prm_Zoom)) $prm_Zoom = $blockW;

	$accdbs = decodeHeaderStrs($fp);
	$chrs = decodeHeaderStrs($fp);

	$numAccdbs = count($accdbs);
	$numChrs = count($chrs);

	$validChrMap = array();
	$numValidChrs = 0;
	if($prm_Chrs !== "") {
		$validChrs = explode(",", $prm_Chrs);
		$numValidChrs = count($validChrs);
		for($i=0; $i<$numValidChrs; $i++) {
			$validChrMap[$validChrs[$i]] = true;
		}
	} else {
		$numValidChrs = $numChrs;
	}
	$blockUnit = $prm_Zoom / $blockW;

	$resArr = array();
	$densMap = array();
	for($i=0; $i<$numAccdbs; $i++) {
		$accdb = $accdbs[$i];
		echo "\"$accdb\":[";
		for($j=0; $j<$numChrs; $j++) {
			$chr = $chrs[$j];
			$lenStatArr = unpack("L", fread($fp, 4))[1];
			if(count(array_keys($validChrMap))>0 && !array_key_exists($chr, $validChrMap)) {
				fseek($fp, ftell($fp) + $lenStatArr * 2);
				continue;
			}

			$accChrInfo = array("chr"=>$chr);
			$total = 0;

			$blocks = 0;
			$densBinStr = "";

			if($blockUnit === 1) {
				$blocks = $lenStatArr;
				$densBinStr =  fread($fp, $lenStatArr * 4);
	
				// If output(echo) is too fast, will the response be cached?
				// Responses will be served less frequently and the browser progress bar will become jerky.
				usleep(20);
			} else {
				$cnt = 0;
				for($k=0; $k<$lenStatArr; $k++) {
					$densBinStrPart = fread($fp, 2);
					$cnt += unpack("S", $densBinStrPart)[1];

					$effBinStrPart = fread($fp, 2);
					$eff +=  unpack("S", $effBinStrPart)[1];
					if($blockUnit === 1 || ($k!==0 && ($k + 1) % $blockUnit === 0) || $k>=$lenStatArr-1) {
						$densBinStr .= pack('S', $cnt);
						$densBinStr .= pack('S', $eff);
						$blocks++;
						$cnt = 0;
					}
				}
			}
			$accChrInfo["b64"] = base64_encode($densBinStr);
	
			echo json_encode($accChrInfo);
			if($j<$numValidChrs - 1) echo ",";

			if(!array_key_exists($chr, $chrBlocks)) $chrBlocks[$chr] = $blocks;
		}
		echo "]";
		if($i<$numAccdbs - 1) echo ",";
	}
	fclose($fp);

	echo "},\"stat\":";
	echo json_encode(array(
		"blockSize" => $blockW,
		"stat" => $chrBlocks
	));
	echo "}";
}

function decodeHeaderStrs($fp) {
	$numStrs = unpack("S", fread($fp, 2))[1];
	$strs = array();
	for($i=0; $i<$numStrs; $i++) {
		$lenStr = unpack("S", fread($fp, 2))[1];
		$str = fread($fp, $lenStr);
		$strs[] = $str;
	}
	return $strs;
}
function getAccdbs($conn, $preAccdbs) {
	$defOrder = FALSE;
	if(is_null($preAccdbs)) {
		$defOrder = TRUE;
		$preAccdbs = array();
	}

	$db2id = array();
	$stmt = $conn->stmt_init();
	$stmt->prepare("select accession_id,accession_db from sys_accession");
	$stmt->execute();
	$stmt->store_result();
	$stmt->bind_result($id, $db);
	while ($stmt->fetch()) {
		$db2id[$db] = $id;
		if($defOrder) $preAccdbs[] = $db;
	}
	$stmt->free_result();

	$existOrderAccids = NULL;
	if ( file_exists( "../conf/order.conf" )) {
		$line = file_get_contents("../conf/order.conf");
		$preArray = explode("\n",$line);
		$preMap = array();
		$valid = FALSE;
		$lenPreArray = count($preArray);
		for($i=0; $i<$lenPreArray; $i++) {
			if(!preg_match('/\S/', $preArray[$i])) continue;
			$preMap[$preArray[$i]] = TRUE;
			$valid = TRUE;
		}
		if($valid) $existOrderAccids = $preMap;
	}

	$lenPreAccdbs = count($preAccdbs);
	$id2db = array();
//	$resArr = array();
	for($i=0; $i<$lenPreAccdbs; $i++) {
		$accdb = $preAccdbs[$i];
		$accid = $db2id[$accdb];
		if(is_null($existOrderAccids) || $existOrderAccids[$accid]) $id2db[$accid] = $accdb;
	}
	return $id2db;
//	return $resArr;
}
function getChrs($conn) {
	$resArr = array();
	$stmt = $conn->stmt_init();
	$stmt->prepare("select id,size from sys_chromosome order by seq asc");
	$stmt->execute();
	$stmt->store_result();
	$stmt->bind_result($id, $size);
	while ($stmt->fetch()) {
		$resArr[] = array("id" => $id, "size" => $size);
	}
	$stmt->free_result();
	return $resArr;
}
function returnError($str){
    echo $str;
}
?>
