<?php
set_time_limit(1200);

require '../conf/config.php';
require './common.php';
require './commonDB.php';
require './lockfile.php';
require './TranslateDna.php';

// For debug
register_shutdown_function(function() {
        error_log("get_fasta_multi.php call register_shutdown_function");
});

//From http request or command line

if (!isset($_SERVER["HTTP_HOST"])) {
    parse_str($argv[1], $_POST);
    foreach($_POST as $key => $val){
        $_POST[$key] = rtrim($val);
    }
    chdir($_POST['path']);
    set_time_limit(0);
}

(isset($_POST['prm_fa_chr']) && !empty($_POST['prm_fa_chr'])) ? $prm_chr=$_POST['prm_fa_chr']:returnError("Error");
(isset($_POST['prm_fa_s']) && !empty($_POST['prm_fa_s'])) ? $prm_s=$_POST['prm_fa_s']:returnError("Error");
(isset($_POST['prm_fa_e']) && !empty($_POST['prm_fa_e'])) ? $prm_e=$_POST['prm_fa_e']:returnError("Error");
//(isset($_POST['prm_fa_flk']) && !empty($_POST['prm_fa_flk'])) ? $prm_fa_flk=$_POST['prm_fa_flk']:returnError("Error");
(isset($_POST['prm_fa_tgt']) && !empty($_POST['prm_fa_tgt'])) ? $prm_tgt=$_POST['prm_fa_tgt']:returnError("Error");
(isset($_POST['prm_fa_var']) && !empty($_POST['prm_fa_var'])) ? $prm_var=$_POST['prm_fa_var']:$prm_var=".";
(isset($_POST['prm_fa_qual'])) ? $prm_qual=$_POST['prm_fa_qual']:returnError("Error");
(isset($_POST['prm_fa_dp'])) ? $prm_dp=$_POST['prm_fa_dp']:returnError("Error");
(isset($_POST['prm_fa_list']) && !empty($_POST['prm_fa_list'])) ? $prm_list=$_POST['prm_fa_list'] : returnError("Error");
(isset($_POST['prm_fa_name']) && !empty($_POST['prm_fa_name'])) ? $prm_name=$_POST['prm_fa_name'] : returnError("Error");
(isset($_POST['prm_fa_nameS']) && !empty($_POST['prm_fa_nameS'])) ? $prm_nameS=$_POST['prm_fa_nameS'] : returnError("Error");
(isset($_POST['prm_fa_ref']) && !empty($_POST['prm_fa_ref'])) ? $prm_ref=$_POST['prm_fa_ref'] : returnError("Error");
(isset($_POST['prm_fa_width'])) ? $prm_width=$_POST['prm_fa_width'] : returnError("Error");
(isset($_POST['prm_fa_p3prm'])) ? $prm_p3prm=$_POST['prm_fa_p3prm'] : returnError("Error");
(isset($_POST['prm_fa_avd'])) ? $prm_avd=$_POST['prm_fa_avd'] : returnError("Error");
(isset($_POST['prm_fa_Ns'])) ? $prm_Ns=$_POST['prm_fa_Ns'] : returnError("Error");
(isset($_POST['prm_fa_DN'])) ? $prm_DN=$_POST['prm_fa_DN'] : returnError("Error");
(isset($_POST['prm_fa_DNnum'])) ? $prm_DNnum=$_POST['prm_fa_DNnum'] : returnError("Error");
(isset($_POST['prm_type']) && !empty($_POST['prm_type'])) ? $prm_type=$_POST['prm_type'] : returnError("Error");
(isset($_POST['prm_epcrprms'])) ? $prm_epcrprms=$_POST['prm_epcrprms'] : returnError("Error");
//(isset($_POST['prm_numthread'])) ? $prm_numthread=$_POST['prm_numthread'] : returnError("Error");
//(isset($_POST['sid']) && !empty($_POST['sid'])) ? $sid=$_POST['sid'] : returnError("Error");

(isset($_POST['prm_obj_id']) && !empty($_POST['prm_obj_id'])) ? $prm_obj_id = $_POST['prm_obj_id'] : $prm_obj_id = "";
(isset($_POST['prm_fa_gene']) && !empty($_POST['prm_fa_gene'])) ? $prm_cds = $_POST['prm_fa_gene'] : $prm_cds = 'genome';
(isset($_POST['prm_fa_reverse']) && !empty($_POST['prm_fa_reverse'])) ? $prm_reverse = (int) $_POST['prm_fa_reverse'] : $prm_reverse = 0;
(isset($_POST['prm_fa_rep_n']) && !empty($_POST['prm_fa_rep_n'])) ? $prm_rep_n = (int) $_POST['prm_fa_rep_n'] : $prm_rep_n = 0;
(isset($_POST['prm_phpflg']) && !empty($_POST['prm_phpflg'])) ? $php_flg = (int) $_POST['prm_phpflg'] : $php_flg = "";
(isset($_POST['prm_fa_use_amb_base']) && !empty($_POST['prm_fa_use_amb_base'])) ? $prm_fa_use_amb_base = (int) $_POST['prm_fa_use_amb_base'] : $prm_fa_use_amb_base = 0;

$dir = __DIR__;

$prm_numthread = $threadnum;
if(empty($prm_numthread)) $prm_numthread = 4;

if ($php_flg == 1) {
        $headurl = $_POST['prm_headurl'];
} else {
        $headers = apache_request_headers();

        foreach ($headers as $header => $value) {
            if($header == "Referer") {
                $headurl = $value;
            }
        }
}

//if($sid && !isValidSession($tempdir, $sid)) { _stopError("Invalid connection"); }
//$fpath = sprintf("%s/../tmp/%s.lock",$dir,$sid);
//if (!file_exists($fpath) and $php_flg == 1) {
//        _stopError("Invalid connection");
//}

//Varidate params
if($prm_type != "a" && $prm_type != "b" && $prm_type != "c"){
        error_log("Invalid value: type.\n");
        exit;
}
if ($php_flg != 1 && $php_flg != "") {
        error_log("Invalid value: php_flg.\n");
        exit;
}

$prm_DNnum=0;
$prm_DN=0;
if (!(ctype_digit($prm_tgt)) || !(is_numeric($prm_s)) || !(is_numeric($prm_e)) || !(is_numeric($prm_qual)) || !(is_numeric($prm_dp)) || !(is_numeric($prm_width)) || !(is_numeric($prm_avd)) || !(is_numeric($prm_Ns)) || !(is_numeric($prm_DN)) || !(is_numeric($prm_DNnum))) {
        $errmsg = "<div align=\"center\"><font color=\"red\" style=\"font-size:18px;\">Invalid Value!!</font></div>";
        echo $errmsg;
        exit;
}

if ($php_flg == 1) {
/*
        if(($prm_e - $prm_s) > 19999) {
                $errmsg = "<div align=\"center\"><font color=\"red\" style=\"font-size:18px;\">Invalid target length!!</font></div>";
                echo $errmsg;
                exit;
        }
*/
} else if($php_flg == "") {
        if(($prm_e - $prm_s) > 199999) {
                $errmsg = "<div align=\"center\"><font color=\"red\" style=\"font-size:18px;\">Invalid target length!!</font></div>";
                echo $errmsg;
                exit;
        }
}
_pregMatch($prm_name);
_pregMatch($prm_nameS);
_pregMatch($headurl);
_pregMatch($prm_list);
_pregMatch($prm_var);
_pregMatch($prm_p3prm);
_pregMatch($prm_epcrprms);
_pregMatch($prm_chr);

$headurl_grep = preg_replace('/^([^\:]+\:\/\/[^\/]+)\:\d+/', '$1', $headurl);	// remove port.
//if(!empty($headurl_grep)) { $headurl = $headurl_grep; }	// Error occur if Portforwarding!! 
$headurl = preg_replace('/\/index\.html$/i', '', $headurl);	// If the path contains index.html, remove it.
$url = $headurl. '/bin/fasta.php';
$request_content_base = array(
	"prm_fa_chr"	=> $prm_chr,
	"prm_fa_s"	=> $prm_s,
	"prm_fa_e"	=> $prm_e,
	"prm_fa_tgt"	=> $prm_tgt,
//	"prm_fa_flk"	=> $prm_fa_flk,
	"prm_fa_qual"	=> $prm_qual,
	"prm_fa_dp"	=> $prm_dp,
	"prm_fa_list"	=> $prm_list,
	"prm_fa_name"	=> $prm_name,
	"prm_fa_nameS"	=> $prm_nameS,
	"prm_fa_ref"	=> $prm_ref,
	"prm_fa_width"	=> 0,
	"prm_fa_avd"	=> $prm_avd,
	"prm_fa_p3prm"	=> "",
	"prm_fa_Ns"	=> $prm_Ns,
	"prm_fa_DN"	=> $prm_DN,
	"prm_type"	=> $prm_type,
	"prm_phpflg"	=> $php_flg,
	"prm_fa_rep_n"	=> $prm_rep_n,
	"prm_fa_use_amb_base"	=> $prm_fa_use_amb_base,
	"prm_numthread"	=> $prm_numthread	// Needed to calculate Accs allocation, not for parallel processing.
);

// 複数URLを同時に取得する
$timeout = 0;	// 0だと無制限
$mh = curl_multi_init();
$chs = array();
for ($i=0;$i<$prm_numthread;$i++) {
	$ch = curl_init();
	$chs[] = $ch;
	$request_content = $request_content_base;	// deep copy(not reference copy).
	$request_content["pid"] = $i;
	curl_setopt_array($ch, array(
		CURLOPT_URL	=> $url,
		CURLOPT_RETURNTRANSFER	=> 1,
		CURLOPT_POST	=> true,
		CURLOPT_POSTFIELDS	=> http_build_query($request_content),
		CURLOPT_FAILONERROR	=> 1,
		CURLOPT_FOLLOWLOCATION	=> 1,
		CURLOPT_MAXREDIRS	=> 3,

		//SSL証明書を無視
		CURLOPT_SSL_VERIFYPEER	=> false,
		CURLOPT_SSL_VERIFYHOST	=> false,
	));
	if(!empty($_SERVER['PHP_AUTH_USER'])) {
		curl_setopt($ch, CURLOPT_USERPWD, $_SERVER['PHP_AUTH_USER']. ":".  $_SERVER['PHP_AUTH_PW']);
	}

	//タイムアウト
	if ($timeout){ curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); }

	curl_multi_add_handle($mh, $ch);
}

//URLを取得
//すべて取得するまでループ
$active = null;
$send_space_cycle = 10;
$cycle = 0;
do {
    $mrc = curl_multi_exec($mh,$active);
} while ($mrc == CURLM_CALL_MULTI_PERFORM);
do {
    curl_multi_exec($mh, $active);
    curl_multi_select($mh);

    if($cycle >= $send_space_cycle) {
	// For ajax.abort() detection. If there is no response, it cannot be detected.
	detectAborted();
	$cycle = 0;
    }
    $cycle++;
} while ($active > 0);
if ($mrc != CURLM_OK) {
    echo '読み込みエラーが発生しました:'.$mrc;
}

// Merge results of each handler  
$res = array();
$chslen = count($chs);
for($i=0; $i<$chslen; $i++) {
    if (($err = curl_error($chs[$i])) == '') {
        $res[$i] = curl_multi_getcontent($chs[$i]);
    } else {
        //echo '取得に失敗しました:'.$chs[$i].'<br />';
    }
    curl_multi_remove_handle($mh, $chs[$i]);
    curl_close($chs[$i]);
}
curl_multi_close($mh);

$seqs = convFasta2Array($res);

// When GeneID is specified, processing that reflects strand(-/+) always occurs.
if(!empty($prm_obj_id)) {
	$conn = null;
	if( !$conn = new mysqli($host, $user, $pswd, $db)) {
		error_log("Connection failed.\n");
		exit;
	}
	$seqs = convCds($seqs, $conn, $prm_obj_id, $prm_s, $prm_e, $prm_width, $prm_cds);
}

$num_seqs = count($seqs);

// Reverse complement
if($prm_reverse && $prm_cds !== "aa") {
	for($i=0; $i<$num_seqs; $i++) {
		$seqs[$i][1] = reverseNucStr($seqs[$i][1]);
	}
}
        
$resStr = "";
for($i=0; $i<$num_seqs; $i++) {
	$resStr .= ">". $seqs[$i][0]. "\n";
	$fastaSeq = $seqs[$i][1];
	if($prm_width != 0) $fastaSeq = wordwrap($seqs[$i][1], $prm_width, "\n", true);
	$resStr .= $fastaSeq;
	$resStr .= "\n";
	unset($seqs[$i]);       // Release memory.
	//TODO check by memory_get_usage();
}

//error_log("Fasta: End successfly!!");

echo json_encode(array($resStr));

function convFasta2Array($resArray) {
	$seqs = array();

	// Convert FASTA string to object.
	$lenResArray = count($resArray);
	for($i=0; $i<$lenResArray; $i++) {
		$mfasta = str_replace("Error", "", $resArray[$i]);
		$rowArray = explode("\n", $mfasta);
		$lenRowArray = count($rowArray);

		$prev_head = "";
		$each_seq = "";
		for($j=0; $j<$lenRowArray; $j++) {
			$line = rtrim($rowArray[$j]);
			if(preg_match("/^>(.+)$/", $line, $matches)){
				if(strlen($prev_head) > 0) {
					$seqs[] = array($prev_head, $each_seq);
//error_log($prev_head. " | ". $each_seq);
				}
				if($matches) $prev_head = $matches[1];
				$each_seq = "";
			} else {
				$each_seq .= $line;
			}
		}
		if(strlen($prev_head) > 0) $seqs[] = array($prev_head, $each_seq);
//error_log($prev_head. " | ". $each_seq);
	}
	return $seqs;
}
function convCds($seqs, $conn, $prm_obj_id, $prm_s, $prm_e, $prm_width, $prm_cds) {
	global $codontable_type;        // config.php param.

	//get CDS/EXON region and trim each seqs
	$cds_region = _get_cds_region($conn, $prm_obj_id, $prm_s, $prm_e, $prm_cds);	// 0-origin
	if(count($cds_region) < 1){
		_stopError("||This object does not have CDS region.");
	}
	$num_cds_region = count($cds_region);

	// Store all basepos in the CDS region as keys of hash.
	$strand = 1;
	$cds_basepos = array();	// hash, key is pos(int, 0-origin)
	for($i=0; $i<$num_cds_region ;$i++){
		$array = explode(":", $cds_region[$i]);
		$strand = (int) $array[2];
		$cds_end = $array[0] + $array[1] - 1;
//error_log($array[0]. ",". $array[1]. ",". $cds_end. ",". $strand);
		for($cds_pos=$array[0]; $cds_pos<=$cds_end; $cds_pos++) {
			$cds_basepos[$cds_pos] = True;
		}
	}

	$num_seqs = count($seqs);
	if($prm_obj_id) {
		if($prm_cds === "cds" || $prm_cds === "mrna" || $prm_cds === "aa") {
			// Convert CDS-basepos -> Aliment-basepos
			$refseq = $seqs[0][1];
			$lenRefseq = strlen($refseq);
			$refseq_pos = -1;
			$align_cds_basepos = array();	// array. value is CDS pos on aligment(int, 0-origin)
			for($i=0; $i<$lenRefseq; $i++) {
				if($refseq[$i] !== "-") {
					$refseq_pos++;
					if(array_key_exists($refseq_pos, $cds_basepos)) {
						$align_cds_basepos[] = $i;
					}
				}
			}
			unset($cds_basepos);	// Release memory

			// Convert transcript seq -> CDS seq
			for($i=0; $i<$num_seqs; $i++) {
				$cds_seq = "";
				foreach ($align_cds_basepos as $align_pos) {
					$cds_seq .= $seqs[$i][1][$align_pos];
					if($align_pos >= $lenRefseq) break;
				}
				$seqs[$i][1] = $cds_seq;
			}
		}
		if($strand === 0) {
			for($i=0; $i<$num_seqs; $i++) {
				$seqs[$i][1] = reverseNucStr($seqs[$i][1]);
			}
		}
		if($prm_cds === "aa") {
			$trans = new TranslateDna();
			$codonTable = array();
			if(isset($codontable_type) && !empty($codontable_type)) {
				$codonTable = $trans->getCodonTable($codontable_type);
			} else {
				$codonTable = $trans->getCodonTable("standard");
			}
			for($i=0; $i<$num_seqs; $i++) {
				$cdsSeq = TranslateDna::removeNonBaseChar($seqs[$i][1]);
				$seqs[$i][1] = $trans->translate($cdsSeq, $codonTable);
			}
		}
	}
	unset($cds_basepos);	// Release memory

	return $seqs;
/*
	if($prm_reverse && $prm_cds !== "aa") {
		for($i=0; $i<$num_seqs; $i++) {
			$seqs[$i][1] = reverseNucStr($seqs[$i][1]);
		}
	}

	$resStr = "";
	for($i=0; $i<$num_seqs; $i++) {
		$resStr .= ">". $seqs[$i][0]. "\n";
		$fastaSeq = $seqs[$i][1];
		if($prm_width != 0) $fastaSeq = wordwrap($seqs[$i][1], $prm_width, "\n", true);
		$resStr .= $fastaSeq;
		$resStr .= "\n";
		unset($seqs[$i]);	// Release memory.
		//TODO check by memory_get_usage();
	}
	
	return array($resStr);	// return value must be an array.
*/
}
function _get_cds_region($conn, $prm_obj_id, $prm_s, $prm_e, $prm_cds){
    $type = "";
    if($prm_cds === 'cds' || $prm_cds === 'aa') {
        $type = "cds";
    } elseif($prm_cds === 'mrna') {
        $type = "exon";
    } else {
	// genome
    }

    $whreSql = "";
    if(!empty($type)) {
	$whreSql = "and (type='". strtoupper($type). "' or type='". strtolower($type). "')";
    } else {
	// Marker,etc does not have strand. 
	$whreSql = "and strand is not null and strand != ''";
    }

    $pos = array();
    $query = "select pos,end,strand from sys_track where id=? ". $whreSql. " order by pos";
//error_log("Test:". $query);
//    $query = "select pos,end,strand from sys_track where id=? and (type='CDS' or type='cds') order by pos";
    $stmt = $conn->stmt_init();
    $stmt->prepare($query);
    $stmt->bind_param("s", $prm_obj_id);
    $stmt->execute();
    $stmt->store_result();
    $stmt->bind_result($st, $en, $strand);
    while ($stmt->fetch()) {
        $pos[] = ($st - $prm_s). ":". ($en - $st + 1). ":". $strand;
    }
    $stmt->free_result();
    $stmt->close();
    if(!empty($type)) {
//for($i=0; $i<count($pos); $i++) {
//  error_log($prm_cds. $i. ":". $prm_obj_id. ":". $pos[$i]);
//}
	return $pos;
    } else {
	// genome
	// If the strand cannot be obtained(Marker, etc.), use 1.
	if($strand != 0 && empty($strand)) $strand = 1;
//error_log($prm_cds. ":". $prm_obj_id. ":". "0:". ($prm_e - $prm_s + 1). ":". $strand);
	return array("0:". ($prm_e - $prm_s + 1). ":". $strand);
    }
}

function returnError($str){
    echo $str;
}
function _stopError($str){
    echo $str;
    exit;
}
