<?php
/*
* dnadist_oracle.php
* http://tasuke.dna.affrc.go.jp/
*
* Author: Ryutaro Itoh
*
* Copyright (c) 2015 National Institute of Agrobiological Sciences   
*
* This software is released under the MIT License.
*
* http://opensource.org/licenses/mit-license.php
*/

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

require '../../conf/config.php';

// Connecting with DB
$conn = oci_connect($user, $pswd, $host."/".$db, "AL32UTF8");
if(!$conn) {
    $e = oci_error();
    error_log($e["message"]."\nConnection failed.\n");
    exit;
}

// Parameters
(isset($_POST['prm_phy_chr']) && !empty($_POST['prm_phy_chr'])) ? $prm_chr=$_POST['prm_phy_chr']:_stopError("||Invalid value");
(isset($_POST['prm_phy_s']) && !empty($_POST['prm_phy_s'])) ? $prm_s=$_POST['prm_phy_s']:_stopError("||Invalid value");
(isset($_POST['prm_phy_e']) && !empty($_POST['prm_phy_e'])) ? $prm_e=$_POST['prm_phy_e']:_stopError("||Invalid value");
(isset($_POST['prm_phy_qual'])) ? $prm_qual=$_POST['prm_phy_qual']:_stopError("||Invalid value");
(isset($_POST['prm_phy_dp'])) ? $prm_dp=$_POST['prm_phy_dp']:_stopError("||Invalid value");
(isset($_POST['prm_phy_list']) && !empty($_POST['prm_phy_list'])) ? $prm_list=$_POST['prm_phy_list'] : _stopError("||Invalid value");
(isset($_POST['prm_phy_cri'])) ? $prm_cri=$_POST['prm_phy_cri'] : _stopError("||Invalid value");
(isset($_POST['prm_phy_gene'])) ? $prm_gene=$_POST['prm_phy_gene'] : _stopError("||Invalid value");
(isset($_POST['prm_phy_objid'])) ? $prm_obj_id=$_POST['prm_phy_objid'] : _stopError("||Invalid value");
(isset($_POST['prm_phy_grp'])) ? $prm_phy_grp=$_POST['prm_phy_grp'] : _stopError("||Invalid value");
$list = explode(",",$prm_list);

// Validating
if(isset($_SERVER["HTTP_HOST"])){
    if(!_validate_chrs($conn,$prm_chr)){_stopError("||Invalid value");}
    if(!_validate_num($prm_s)){_stopError("||Invalid value");}
    if(!_validate_num($prm_e)){_stopError("||Invalid value");}
    if(!_validate_num($prm_qual)){_stopError("||Invalid value");}
    if(!_validate_num($prm_dp)){_stopError("||Invalid value");}
    if(!_validate_each_acc($list)){_stopError("||Invalid value");}
    if(_chk_cds($conn, $prm_obj_id) < 1 and $prm_gene == 1){ _stopError("||This object does not have CDS region.");}
    $prm_phy_grp = _validate_group($prm_phy_grp);
}

if(!isset($_SERVER["HTTP_HOST"])){
    $seq = _convCompDelSeq($internal_infile,$prm_cri,$prm_gene,$conn,$prm_obj_id,$prm_s);
    file_put_contents($internal_outfile, $seq."\n");
    exit;
}

if($prm_cri == 1){
    $params = _get_pos_from_id($conn, $prm_obj_id);
    if(count($params) != 3){
        _stopError("||The ID was not found.");
    }
    $prm_chr = $params[0];
    $prm_s = $params[1];
    $prm_e = $params[2];
}

// Getting sequences from fasta.php 
$wkdir = _create_infile_dnadist($prm_chr,$prm_s,$prm_e,$prm_qual,$prm_dp,$prm_list,$prm_cri,$prm_gene,$conn,$prm_obj_id);
_exec_phylip($phylipDir);
$nwk = _get_nwk($wkdir);
$order = _make_order($conn);
$res = _make_results($order, $nwk, $prm_phy_grp, $conn);
_clean_dir($wkdir);
echo $res;

//////////////// functions ////////////////////
function _make_results($order, $nwk, $prm_phy_grp, $conn){
    $trimed_order = array();
    $relation = explode(",", $order);
    $nwk = preg_replace("/\s/", "", $nwk);
    for($i=0; $i<count($relation); $i++){
        $acc_and_grp = "";
        $accession = explode(":",$relation[$i]);
        $acc_and_grp = $accession[0]."_"._get_grp($conn, $accession[0], $prm_phy_grp);
        $patt = "/(\,|\)|\()$accession[1]:/";
        $nwk = preg_replace($patt, '${1}'.$acc_and_grp.":", $nwk);
        array_push($trimed_order, $accession[0]);
    }
    return implode(",",$trimed_order)."|".$nwk;
}

function _clean_dir($wkdir){
    $php_tmp_dir = dirname($wkdir);
    $files = Array("infile_dnadist", "outfile_dnadist", "infile", "outfile", "outtree", "header", "phylip_wo_header", "cmd_dnadist", "cmd_tree");
    for($i=0; $i<count($files); $i++){
        unlink($files[$i]);
    }
    chdir($php_tmp_dir);
    rmdir($wkdir);
    
    // Finding files in the working directory.
    /*
    $crtdir = getcwd();
    if ($dh = opendir($crtdir)) {
        while(false !== ($file = readdir($dh))){
            array_push($files, $file);
        }
        closedir($dh);
    }
    */
}

function _get_nwk($wkdir){
    return file_get_contents($wkdir."/outtree");
}

function _make_order($conn){
    $accessions = Array();
    $order = Array();
    $rtn_string = "";
    
    $IN = fopen("outfile", "r");
    if($IN){
        while(($line = fgets($IN, 4096)) !== false){
            $line = rtrim($line);
            if(preg_match("/^Between\s+And\s+Length/", $line)){break;}
            if(preg_match("/.*\+d\d+/", $line)){
                $line = preg_replace("/.*\+(d\d+)/", '${1}', $line);
                array_unshift($order, $line);
            }
            
        }
    }
    fclose($IN);
    for($i=0; $i<count($order); $i++){
        $str = _get_accessionid($conn, $order[$i]);
        array_push($accessions, $str);
    }
    $rtn_string = implode(",", $accessions);
    return $rtn_string;
}

function _exec_phylip($phylipDir){
    if(!file_exists($phylipDir."/exe/dnadist")){_stopError("||Could not find PHYLIP.");}
    exec(sprintf("%s/exe/dnadist < %s > /dev/null", $phylipDir, "cmd_dnadist"), $resArray, $return);
    if($return !=0){_stopError("||Can not execute PHYLIP:dnadist.");}
    if(rename("infile", "infile_dnadist") == false){_stopError("||Error: Renaming infile for dnadist");}
    if(copy("outfile","infile") == false){_stopError("||Error: preparing infile for neighbor");}
    if(rename("outfile", "outfile_dnadist") == false){_stopError("||Error: Renaming result of dnadist");}
    exec(sprintf($phylipDir."/exe/neighbor < %s > /dev/null", "cmd_tree"), $resArray, $return);
    if($return !=0){_stopError("||Can not execute PHYLIP:neighbor.");}
}

function _create_infile_dnadist($prm_chr,$prm_s,$prm_e,$prm_qual,$prm_dp,$prm_list,$prm_cri,$prm_gene,$conn,$prm_obj_id){
    $bin ="./";
    $args = "";
    $resArray = Array();
    
    if(exec(sprintf("php %s '%s'", 
                    $bin."fasta.php",
                    "prm_fa_chr=".$prm_chr."
                    &prm_fa_s=".$prm_s."
                    &prm_fa_e=".$prm_e."
                    &prm_fa_tgt=1
                    &prm_fa_qual=".$prm_qual."
                    &prm_fa_dp=".$prm_dp."
                    &prm_fa_list=".$prm_list."
                    &prm_fa_name=id
                    &prm_fa_nameS=subvariety
                    &prm_fa_ref=Reference
                    &prm_fa_width=80
                    &prm_fa_avd=0
                    &prm_fa_p3prm=
                    &prm_fa_Ns=0
                    &prm_type=a
                    &path=".$bin
                    ), $resArray, $return) !=0){
        print "Error: Can not execute fasta.php for getting each sequences.\n";
        exit;
    }
    $fp = tmpfile();
    $res = implode("\n", $resArray);
    fwrite($fp, $res);
    rewind($fp);
    $tmparray = stream_get_meta_data($fp);
    $comp_del_seq = _convCompDelSeq($tmparray["uri"],$prm_cri,$prm_gene,$conn,$prm_obj_id,$prm_s);
    fclose($fp);
    $fp_cd = tmpfile();
    fwrite($fp_cd, $comp_del_seq);
    rewind($fp_cd);
    $tmparray_cd = stream_get_meta_data($fp_cd);
    $wkdir = _prep_phyfile($tmparray_cd["uri"]);
    fclose($fp_cd);
    return $wkdir;
}

function _prep_phyfile($path){
    $nuc_length = 0;
    $num_seq = 0;
    $wkdir = $path."_dir";
    
    if(mkdir($wkdir) == false){_stopError("||Can not create temporary directory.");}
    if(chdir($wkdir) == false){_stopError("||Can not move to temporary directory.");}
    $infile = "phylip_wo_header";
    $IN = fopen($path, "r");
    $OUT = fopen($infile, "w");
    if($IN){
        while(($line = fgets($IN, 4096)) !== false){
            $line = rtrim($line);
            if(preg_match("/^>/", $line)){
                $num_seq++;
                $nuc_length = 0;
                $line = preg_replace("/>/", "", $line);
                $line = preg_replace("/REF.*/", "REF", $line);
                strlen($line);
                if(strlen($line) > 10){
                    $line = substr($line, 0, 10);
                }else{
                    for($i=strlen($line)+1; $i<11; $i++){
                        $line .= " ";
                    }
                }
                $line .= " ";
                fwrite($OUT, $line);
            }else{
                $nuc_length += strlen($line);
                fwrite($OUT, $line."\n");
            }
        }
    }
    fclose($IN);
    fclose($OUT);
    file_put_contents("header", $num_seq." ".$nuc_length."\n");
    file_put_contents("cmd_dnadist", "i\nY\n");
    file_put_contents("cmd_tree", "o\n1\nY\n");
    if(exec(sprintf("cat %s %s > %s", "header", "phylip_wo_header", "infile"), $resArray, $return) !=0){
        print "Error: Can not create infile for the PHYLIP.\n";
        exit;
    }
    return $wkdir;
}

function _convCompDelSeq($inseq_path,$prm_cri,$prm_gene,$conn,$prm_obj_id,$prm_s){
    $num_seq = 1;
    $num_line = 0;
    $ins_pos = array();
    $disused_pos = array();
    $cds_region = array();
    $each_seq = "";
    $cdel_seq = "";
    $in_num_header = exec( "grep -c '^>' ".$inseq_path );
    $in_num_seq = exec( "grep -cv '^>' ".$inseq_path );
    $in_num_line = exec( "grep -c '^' ".$inseq_path );
    
    //get insertion and disused sites (deletion, umpapped or filterd) from each seq
    $IN = fopen($inseq_path, "r");
    if($IN){
        while(($line = fgets($IN, 4096)) !== false){
            $num_line++;
            $line = rtrim($line);
            if(preg_match("/^>/", $line) or feof($IN) or $num_line >= $in_num_line){
                if($num_seq == 1){
                    $num = strlen($each_seq);
                    for($a=0;$a<$num;$a++){
                        if($each_seq[$a] == "-"){
                            $ins_pos[$a] = 0;
                        }
                    }
                }elseif($num_seq > 1){
                    $num = strlen($each_seq);
                    for($a=0;$a<$num;$a++){
                        if($each_seq[$a] == "-"){
                            $disused_pos[$a] = 0;
                        }
                    }
                }
                $num_seq++;
                $each_seq = "";
            }else{
                $each_seq .= $line;
            }
        }
    }
    $each_seq = "";
    fclose($IN);
    $num_line = 0;
    
    //get CDS region
    if($prm_cri == 1 and $prm_gene == 1){
        $cds_region = _get_cds_region($conn, $prm_obj_id,$prm_s);
        if(count($cds_region) < 1){
            _stopError("||This object does not have CDS region.");
        }
    }
    
    //remove indels from each seq
    $num_seq = 1;
    $IN2 = fopen($inseq_path, "r");
    if($IN2){
        while(($line = fgets($IN2, 4096)) !== false){
            $num_line++;
            $line = rtrim($line);
            if(preg_match("/^>/", $line) or feof($IN2) or $num_line >= $in_num_line){
                if(feof($IN2) or $num_line >= $in_num_line){$each_seq .= $line;}
                if($num_seq > 1){
                    $num = strlen($each_seq);
                    $pointer = 0;
                    for($a=0;$a<$num;$a++){
                        if(array_key_exists($a, $ins_pos)){
                            $each_seq = substr_replace($each_seq, '', $a+$pointer,1);
                            $pointer--;
                        }
                        if(array_key_exists($a, $disused_pos)){
                            $each_seq = substr_replace($each_seq, '-', $a+$pointer,1);
                        }
                    }
                    if($prm_cri == 1 and $prm_gene == 1){
                        $str = "";
                        for($i=0;$i<count($cds_region);$i++){
                            $array = explode(":", $cds_region[$i]);
                            $str .= substr($each_seq,$array[0],$array[1]);
                        }
                        $each_seq = $str;
                    }
                    $each_seq = preg_replace("/\-/", "", $each_seq);
                    $cdel_seq .= wordwrap($each_seq, 80, "\n", true);
                    rtrim($cdel_seq);
                    $cdel_seq .= "\n";
                    if(!feof($IN2) and $num_line < $in_num_line){$cdel_seq .= $line."\n";}
                    $each_seq = "";
                }else{
                    $cdel_seq .= $line."\n";
                }
                $num_seq++;
            }else{
                $each_seq .= $line;
            }
        }
    }
    $each_seq = "";
    fclose($IN2);
    return $cdel_seq;
}

function _chk_cds($conn, $prm_obj_id){
    $cds = 0;
    $query = "select seq from sys_track where id=:bd_id and (type='CDS' or type='cds') and ROWNUM < 2";
    $stmt = oci_parse($conn, $query);
    oci_bind_by_name($stmt, ':bd_id', $prm_obj_id, -1, SQLT_CHR);
    oci_execute($stmt);
     while (($row = oci_fetch_array($stmt, OCI_NUM + OCI_RETURN_NULLS))!= false) {
        $cds+;
    }
    oci_free_statement($stmt);
    return $cds;
}

function _get_cds_region($conn, $prm_obj_id,$prm_s){
    $pos = array();
    $query = "select pos,end from sys_track where id=:bd_id and (type='CDS' or type='cds') order by pos";
    $stmt = oci_parse($conn, $query);
    oci_bind_by_name($stmt, ':bd_id', $prm_obj_id, -1, SQLT_CHR);
    oci_execute($stmt);
    while (($row = oci_fetch_array($stmt, OCI_NUM + OCI_RETURN_NULLS))!= false) {
        $pos[] = ($row[0]-$prm_s).":".($row[1]-$row[0]+1);
    }
    oci_free_statement($stmt);
    return $pos;
}

function _get_pos_from_id($conn, $prm_obj_id){
    $pos = array();
    $query = "select chr,pos,end from sys_track where id=:bd_id and parent=1 and ROWNUM < 2";
    $stmt = oci_parse($conn, $query);
    oci_bind_by_name($stmt, ':bd_id', $prm_obj_id, -1, SQLT_CHR);
    oci_execute($stmt);
    while (($row = oci_fetch_array($stmt, OCI_NUM + OCI_RETURN_NULLS))!= false) {
        $pos[0] = $row[0];
        $pos[1] = $row[1];
        $pos[2] = $row[2];
    }
    oci_free_statement($stmt);
    return $pos;
}

function _validate_num($num){
    if (preg_match("/^\d+$/i", $num)) {
       return true;
    }else{
       return false;
    }
}

function _validate_chrs($conn, $prm_Chr){
    $flag = false;
    $query = "SELECT seq FROM sys_chromosome WHERE id=:bd_chr AND ROWNUM < 2 ";
    $stmt = oci_parse($conn, $query);
    oci_bind_by_name($stmt, ':bd_chr', $prm_Chr, -1, SQLT_CHR);
    oci_execute($stmt);
    while (($row = oci_fetch_array($stmt, OCI_NUM + OCI_RETURN_NULLS))!= false) {
        $flag = true ;
    }
    oci_free_statement($stmt);
    if($flag){
       return true;
    }else{
        error_log("Invalid sequence name");
       return false;
    }
}

function _validate_each_acc($list){
    $num = count($list);
    for($a=0;$a<$num;$a++){
        if (preg_match("/^d\d+$/i", $list[$a])) {
            //pass
        }else{
           return false;
        }
    }
    return true;
}

function _validate_name($str){
    if($str == "id"){
        $str="accession_id";
    }else if($str == "name"){
        $str="accession_name";
    }else{
        return false;
    }
    return true;
}

function _validate_group($str){
    $keys = array(
        "variaty" => "1",
        "sub_variety" => "1",
        "origin" => "1",
        "origin_2" => "1",
        "type" => "1",
    );
    if($str == "subvariety" ){$str = "sub_variety";}
    if(array_key_exists($str, $keys)){
        return $str;
    }else{
        return false;
    }
}

function _get_accessionid($conn, $internal_id){
    $id = "";
    $query = "select accession_id from sys_accession where accession_db=:bd_acc and ROWNUM<1";
    $stmt = oci_parse($conn, $query);
    oci_bind_by_name($stmt, ':bd_acc', $internal_id, -1, SQLT_CHR);
    oci_execute($stmt);
    while (($row = oci_fetch_array($stmt, OCI_NUM + OCI_RETURN_NULLS))!= false) {
        $id = $row[0];
    }
    oci_free_statement($stmt);
    return substr($id, 0, 10).":".$internal_id;
}

function _get_grp($conn, $id, $grp){
    $ret = "";
    $query = "select ".$grp." from sys_accession where accession_id=:bd_acc ROWNUM<1";
    $stmt = oci_parse($conn, $query);
    oci_bind_by_name($stmt, ':bd_acc', $id, -1, SQLT_CHR);
    oci_execute($stmt);
    while (($row = oci_fetch_array($stmt, OCI_NUM + OCI_RETURN_NULLS))!= false) {
        $ret = $row[0];
    }
    oci_free_statement($stmt);
    return $ret;
}

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

function _stopError($str){
    echo $str;
    exit;
}
?>