#!/usr/bin/perl -s

use POSIX ":sys_wait_h";
use File::Copy;
use File::Basename;
use File::Path;
use FileHandle;

$main::PID = $$ if (! $main::PID);

$UserName = getpwuid($<);
$Default_TMPDIR = "/tmp/$UserName/mqblast.$main::PID";

$main::DIR_nqs_pub = '/scratch' if (! $main::DIR_nqs_pub);
$Default_TMPDIR2 = "$main::DIR_nqs_pub/$UserName/mqblast.$main::PID";
$Default_DBNAME = "seqdb";
$Default_BLASTFMTOPT = "-m 9";
$Default_BLASTTHREADOPT = "-a 2";
##$Default_BLASTOPT = "-Y 1e9 -v 20000 -b 20000 -e 1 -X 25 -f 10";
$Default_BLASTOPT = "-Y 1e9 -v 40000 -b 40000 -e 1";
$Default_PROCNUM = 8;
## NOTE: In ver. 2.2.10, too large BLKSIZ causes untolerable slow down,
## probably because of the cache overflow for getting ObjMgrNextAvailEntityID.
$Default_BLKSIZ = 50;
$main::CMD_qsub  = '/usr/sge/bin/qsub'  if (! $main::CMD_qsub);
if ($main::QUEUE_nqs !~ /^\s*$/) {
    $main::CMD_qsub .= " -q $main::QUEUE_nqs ";
}
$main::CMD_qstat = '/usr/sge/bin/qstat' if (! $main::CMD_qstat);

$main::Qsystem = 'PBS' if (! $main::Qsystem);

#
$main::STA_QSTAT_RUNNING  = 1;
$main::STA_QSTAT_FINISHED = 0;

$BLASTPATH = "/bio/bin/" if (! $BLASTPATH);
$BLASTPATH .= "/" if (substr($BLASTPATH, -1, 1) ne '/');

###$CMD_legacy_blast="legacy_blast.pl";
if ($main::CMD_legacy_blast) {
	$BLAST_CMDHEAD="$main::CMD_legacy_blast ";
} else {
	$BLAST_CMDHEAD="${BLASTPATH}";
}

$main::CMD_blastall = "${BLAST_CMDHEAD}blastall" if (! $main::CMD_blastall);
$main::CMD_blastpgp = "${BLAST_CMDHEAD}blastpgp" if (! $main::CMD_blastpgp);
$main::CMD_rpsblast = "${BLAST_CMDHEAD}rpsblast" if (! $main::CMD_rpsblast);
$main::CMD_formatdb = "${BLAST_CMDHEAD}formatdb" if (! $main::CMD_formatdb);

$PROCNUM = $Default_PROCNUM if (! $PROCNUM);
$CMDNAME = "blastall" if (! $CMDNAME);
$PROGNAME = 'blastp' if (! $PROGNAME);
$BLASTOPT = "$Default_BLASTOPT" if (! $BLASTOPT);
$BLASTFMTOPT = "$Default_BLASTFMTOPT" if (! $BLASTFMTOPT);
$BLASTTHREADOPT = "$Default_BLASTTHREADOPT" if (! $BLASTTHREADOPT);
$BLASTOPT .= " $BLASTFMTOPT $BLASTTHREADOPT";

if ($PROGNAME =~ /blastp|blastn|blastx|tblastn|tblastx/) {
	$BLASTCMD = "$main::CMD_blastall -p $PROGNAME $BLASTOPT";
} elsif ($PROGNAME =~ /blastpgp/) {
	$BLASTCMD = "$main::CMD_blastpgp $BLASTOPT";
} elsif ($PROGNAME =~ /rpsblast/) {
	$BLASTCMD = "$main::CMD_rpsblast $BLASTOPT";
}

if (! $TMPDIR) {
	if ($mode eq 'qsub') {
		$TMPDIR = $Default_TMPDIR2;
	} else {
		$TMPDIR = $Default_TMPDIR;
	}
}

$CommandPath = $0;
$CWD = $ENV{PWD};
if ($CopyAll) {
	$WorkDir = $TMPDIR;
} else {
	$WorkDir = $CWD;
}


if ($main::Qsystem eq' SGE') {
	$QsubCommentHead = '#$';
} elsif ($main::Qsystem eq 'PBS') {
	$QsubCommentHead = '#PBS';
} else {
	$QsubCommentHead = '#';
}

if ($QLIST) {
	# read query files from $QLIST and execute, and then exit
	my(%Found);
	my(@QfileList);
	open(F, $QLIST) || die "Can't open file: $QLIST\n";
	while(<F>){
		my($fname) = split(/[: \t]+/);
		next if ($Found{$fname});
		$Found{$fname} = 1;
		my $odir = dirname($fname);
		my $qname = basename($fname);
		if (! $OUTDIR) {
			$OUTDIR = $odir;
			$TMPDIR = dirname($OUTDIR);
			$QDIR = "$TMPDIR/qdir" if (! $QDIR);
			if (! $DB) {
				$DB = "$TMPDIR/$Default_DBNAME";
			}
		} elsif ($OUTDIR ne $odir) {
			die "OUTDIRs does not match\n";
		}
		push(@QfileList, $qname);
	}
	close(F);
	while ($qname = shift(@QfileList)) {
		&execBlast($qname);
	}
	exit 0;
}

if (! @ARGV) {
	die qq{
Usage:	$0 -TMPDIR=dir [options] DB [QFILE]
	options: -TMPDIR=dir -BLKSIZ=num -DIVNUM=num -PROCNUM=num
		 -BLASTOPT='opt string' -PROGNAME=blastprog
		 -OUTDIR=dir -OUTFILE=file 
		 -mode='qsub' -retainTmp -CopyAll

};
}
$DBNAME = $Default_DBNAME if (! $DBNAME);
$TMP_QLOGDIR = "$TMPDIR/qsublog";
$LOCKFILE = "$TMPDIR/lock";

if ($CopyAll) {
	$CopyDB = 1 if (! defined $CopyDB);
	$CommandPath = "$TMPDIR/" . basename($0);
}

$retainTmp=1;
$DB = $ARGV[0];
$QFILE = $ARGV[1];
if (! defined $OUTFILE && ! $retainTmp) {
	$OUTFILE = "&STDOUT";
}
if ($retainTmp) {
	$flag_delete_tmpdir = 0;
#} elsif ($deleteTmp) {
} else {
	$flag_delete_tmpdir = 1;
}

if (! $DB) {
	$DB = "$TMPDIR/$DBNAME";
	$flag_delete_tmpdir = 0;
} elsif ($CopyDB) {
	$OrigDB = $DB;
	$DB = "$TMPDIR/$DBNAME";
}
$QDIR = "$TMPDIR/qdir" if (! $QDIR);
$LDIR = "$TMPDIR/ldir" if (! $LDIR);
$OUTDIR = "$TMPDIR/odir" if (! $OUTDIR);
$QFILE = $DB if (! $QFILE);
if ($CHECK) {
	$CHECKDIR = "$TMPDIR/cdir";
	if ($CHECK_NAMELEN) {
		$CHECK_NAMELEN1 = $CHECK_NAMELEN2 = $CHECK_NAMELEN;
	}
}


if (! -d $TMPDIR) {
	mkpath($TMPDIR) || die "Unable to create tmpdir $TMPDIR\n";
	if (! defined $flag_delete_tmpdir) {
		$flag_delete_tmpdir = 1;
	}

#	if ($mode eq 'qsub') {
#		mkdir $TMP_QLOGDIR, 0755;
#	}
}

if (! -d $QDIR) {
	mkpath($QDIR) || die "Unable to create $QDIR\n";
}
if (! -d $LDIR) {
	mkpath($LDIR) || die "Unable to create $LDIR\n";
}
if (! -d $OUTDIR) {
	mkpath($OUTDIR) || die "Unable to create $OUTDIR\n";
}
if ($CHECKDIR && ! -d $CHECKDIR) {
	mkpath($CHECKDIR) || die "Unable to create $CHECKDIR\n";
}
if ($CopyAll && dirname($QFILE) ne $TMPDIR) {
	$OrigQFILE = $QFILE;
	$QFILE = "$TMPDIR/" . basename($OrigQFILE);
	if ($ADD && -f $QFILE) {
		## skip copying qfile
	} else {
		print STDERR "copying query..\n";
		copy($OrigQFILE, $QFILE);
	}
}

while (-f $LOCKFILE) {
	print STDERR "Lockfile($LOCKFILE) found, sleeping...\n"
		if (! $sleepcnt++);
	sleep(5);
}
if ($CopyDB && $OrigDB) {
	open(L,">$LOCKFILE"); print L "$QFILE"; close(L);
	$flag_createLock = 1;
	if ( -f $DB && (($CopyDB eq 'no_ovwrt') || $ADD) ) {
		## skip copying db
	}
	else {
		print STDERR "copying db...\n";
		if ($PROGNAME =~ /rpsblast/) {
			foreach my $ext (qw(aux loo phr pin pn psd psi psq rps)) {
					#Cdd.aux Cdd.loo Cdd.phr Cdd.pin
					#Cdd.pn
					#Cdd.psd Cdd.psi Cdd.psq
					#Cdd.rps
				copy("$OrigDB.$ext", "$DB.$ext");
			}
		}
		else {
			copy($OrigDB, $DB) || die "Unable to copy db\n";
		}
	}
}
if (&cmpr_mdate("$DB.psq", "$DB") < 0) {
	if (! -f $LOCKFILE) {
		open(L,">$LOCKFILE"); print L "$QFILE"; close(L);
		$flag_createLock = 1;
	}
	print STDERR "formatting db...\n";
    my($cmd) = "$main::CMD_formatdb -i $DB -o T -p T";
	print STDERR "$cmd\n";
    if($mode eq 'qsub') {
        my($file_job) = "$TMPDIR/formatdb.job";
        my($fh_job) = FileHandle->new(">$file_job");

        $fh_job->print("$QsubCommentHead -N formatdb\n");
        $fh_job->print("$QsubCommentHead -o $LDIR/formatdb.stdout\n");
        $fh_job->print("$QsubCommentHead -e $LDIR/formatdb.stderr\n");
        $fh_job->print("cd $WorkDir\n");
        $fh_job->print("$cmd\n");
        $fh_job->close();
		my($JOBID_FILE) = "jobid.$$";
        system("$main::CMD_qsub $file_job > $JOBID_FILE");
        unlink($file_job);
        open(J, $JOBID_FILE);
        $jobid = <J>;
        ($jobid) = ($jobid =~ /(\d+)/);
        die "no job id ?\n" if (! $jobid);
        print STDERR "jobid: $jobid; [formatdb]\n";
        close(J);
        for(;;) {
            my$stat_ref = getQstat();
            if (!exists($stat_ref->{"$jobid"})) {
                last;
            }
            elsif ($stat_ref->{"$jobid"} =~ /^Eqw$/) {
                my($cmd) = "$CMD_qmod -cj $jobid";
                print STDERR "WARNING :: $cmd\n";
                system("$cmd");
            }
            sleep 5;
        }
    }
    else {
        system("$cmd");
    }

}
if ($flag_createLock) {
	unlink $LOCKFILE;
}
if ($CopyAll && (! $ADD || ! -f $CommandPath) ) {
	## copy the command into $TMPDIR
	copy($0, $CommandPath);
	chmod 0755, $CommandPath;
}


if (! $BLKSIZ) {
	if (! $DIVNUM) {
		$DIVNUM = $PROCNUM;
	}
	$cnt = 0;
	open(QFILE,$QFILE) || die("Can not open $QFILE($!)");
	while (<QFILE>) {
		if (/^>/) {
			$cnt++;
		}
	}
	close(QFILE);
	$BLKSIZ = int($cnt / $DIVNUM);
	if (! ($QNAME && $DIVNUM == 1) ) {
		$BLKSIZ = $Default_BLKSIZ if ($BLKSIZ > $Default_BLKSIZ);
	}

	$BLKSIZ = 1 if ($BLKSIZ == 0);
}


if ($QNAME && $DIVNUM == 1) {
	$flag_div = 0;
	$QDIR = dirname($QFILE);	# do not create new file
} else {
	$flag_div = 1;
}
if ($ADD) {
	&check_FinishedResults;
}

DoBlastAll:	## execute blast
print STDERR "START\n";

$qname = '';
$qnum = 0;
$divcnt = 0;
@Qnames = ();
if ($flag_div) {
	my $exec_flag = 0;
	my $cnt = $BLKSIZ;
	open(QFILE, $QFILE) || die("Can not open $QFILE($!)");
	while (<QFILE>) {
		if (/^>\s*(\S+)/) {
			if ($cnt >= $BLKSIZ) {
				if ($qname) {
					if ($exec_flag) {
						close O;
						&execBlast($qname);
						$exec_flag = 0;
					}
					push(@Qnames, $qname);
					$divcnt++;
				}
				++$qnum;
				if ($QNAME) {
					$qname = "${QNAME}_q" . $qnum;
				} else {
					$qname = "q" . $qnum;
				}
				if ($ADD && -f "$QDIR/$qname") {
					if (&check_Finished($qname)) {
					} else {
						$exec_flag = 1;
					}
				} else {
					open(O, ">$QDIR/$qname") || die("Can not open $QDIR/$qname($!)");
					$exec_flag = 1;
				}
				$cnt = 0;
			}
			$cnt++;
			print O $_ if ($exec_flag);
		} elsif ($exec_flag) {
			print O $_;
		}
	}
	close(O) if ($exec_flag);
	close(QFILE);
	if ($exec_flag){
		&execBlast($qname);
	}
	$LastName = $qname;
	$LastCount = $cnt;
	&store_lastfile($LastName, $LastCount);
} else {
	$qname = "${QNAME}";
	&execBlast($qname);
}
push(@Qnames, $qname);
$divcnt++;
$DIVNUM = $divcnt if (! $DIVNUM);

if($mode eq 'qsub') {
	foreach my $jobid (sort {$a<=>$b} keys %JobIDs) {
		next if (! $jobid);
		print STDERR "waiting for terminating the job $jobid ...";
		for(;;) {
#            my($cmd) = "$main::CMD_qstat $jobid";
#			system("$cmd >/dev/null 2>&1");
#			if ($?) {
#				## job not found
#				delete $JobIDs{$jobid};
#				$stat = 0;
#			}
            my$sta_ref = getQstat($jobid);
            if (!exists($sta_ref->{"$jobid"})) {
                print STDERR "Done\n";
				delete $JobIDs{$jobid};
				$stat = 0;
                last;
            }
            elsif ($sta_ref->{"$jobid"} =~ /^Eqw$/) {
                my($cmd) = "$CMD_qmod -cj $jobid";
print STDERR "WARNING :: $cmd\n";
                system("$cmd");
            }
			sleep 5;
		}
	}
} else {
	while (wait > 0) {
        ## waiting until all of the child processes are terminated
        undef(%PIDs);
	}
    undef(%PIDs);
}

if ($CHECK) {
	my $errcnt = &check_FinishedResults;
	if ($errcnt) {
		print STDERR "The number of errors: $errcnt: ";
		if ($MAXERR && $errcnt >= $MAXERR) {
			die "  exceeds the limit -- die\n";
		} else {
			print STDERR " try again\n";
		}
		$MAXERR = $errcnt;	## set limit
		$ADD = 1;
		goto DoBlastAll;	## retry
	}
}

if ($OUTFILE) {
	## concatenating all results
	if ($OUTFILE ne "$OUTDIR/$Qnames[0]") {
		open(O, ">$OUTFILE");
		foreach $fn (@Qnames) {
			open(R, "$OUTDIR/$fn") || die "Can't open $OUTDIR/$fn\n";
			while(<R>){
				print O $_;
			}
			close(R);
		}
		close(O);
	}
}
if ($flag_delete_tmpdir) {
    rmtree("$TMPDIR");
}
#print STDERR "Done\n";
exit(0);


sub execBlast {
	my($name) = @_;
	my($JOBID_FILE) = "jobid.$$";
	&waitBlast($PROCNUM);
	if ($mode eq 'qsub') {
		$DIVNUM2 = 1 if (! $DIVNUM2);

		if ($TMPDIR2) {
			if ($TMPDIR2 == 1) {
				$TMPDIR2 = "/tmp/$UserName/mqblast.$$";
			}
			$CMDOPT = "-TMPDIR=${TMPDIR2} -QNAME=${name}" .
				" -CopyDB=no_ovwrt -retainTmp" .
				" -DIVNUM=$DIVNUM2 -OUTFILE=${OUTDIR}/$name";
		} else {
			$OUTDIR2 = $OUTDIR if (! $OUTDIR2);
			$CMDOPT = "-QDIR=${QDIR} -QNAME=${name}" .
				" -OUTDIR=${OUTDIR2} -DIVNUM=$DIVNUM2 " .
				" -OUTFILE=${OUTDIR}/$name";
		}
		$CMDOPT .= " -BLASTPATH=$BLASTPATH" if ($BLASTPATH);
		$CMDOPT .= " -PROGNAME=$PROGNAME" if ($PROGNAME);
		$CMDOPT .= " -CMD_legacy_blast=$CMD_legacy_blast" if ($CMD_legacy_blast);
        if ($DOMTOP && $PROGNAME =~ /rpsblast/) {
            $CMDOPT .= " -DOMTOP";
        }
		$CMD = "$CommandPath $CMDOPT $DB $QDIR/$name";
		if ($DEBUG) {
			print "$CMD\n";
			return;
		}
		if ($CHECKDIR && -e "$CHECKDIR/$name") {
			print "SKIP :: Found checkfile $CHECKDIR/$name\n";
			return;
		}

        my($filename_job) = "$TMPDIR/mqb.$name.job";
        my($fh_job) = FileHandle->new(">$filename_job");
        $fh_job->print("$QsubCommentHead -N $name\n");
        $fh_job->print("$QsubCommentHead -o $LDIR/$name.stdout\n");
        $fh_job->print("$QsubCommentHead -e $LDIR/$name.stderr\n");
        $fh_job->print("$QsubCommentHead $OtherQsubOpt\n") if ($OtherQsubOpt);
        $fh_job->print("cd $WorkDir\n") if ($WorkDir);
        $fh_job->print("$CMD\n");
        $fh_job->close();
		chdir $WorkDir if ($WorkDir);
		system("$main::CMD_qsub $filename_job > $JOBID_FILE ");
#        unlink($filename_job);
		open(J, $JOBID_FILE);
		$jobid = <J>;
		($jobid) = ($jobid =~ /(\d+)/);
		die "no job id ?\n" if (! $jobid);
		print STDERR "jobid: $jobid; qname: $name\n";
		$JobIDs{$jobid} =1;
		close(J);
		chdir $CWD;
	} else {
#		my $CMD = "$BLASTCMD -d $DB -i $QDIR/$name -o $OUTDIR/$name";
		my $CMD = "$BLASTCMD -d $DB -i $QDIR/$name";
		print STDERR "EXEC :: $CMD\n";
		if ($DEBUG) {
			return;
		}
		if (my $pid = fork) {
			$PIDs{$pid} = 1;
		} else {
#			exec($CMD);
#			## should not come here
#			die "BLAST failed: $BLASTCMD\n";

            my($fileLocal) = "/tmp/mqblast.out.$$";
            my($fhw) = new FileHandle(">$fileLocal") or die "Can not open $fileLocal($!)\n";
            my($fhr) = new FileHandle("$CMD|") or die "BLAST failed: $BLASTCMD\n";
            if ($DOMTOP) {
                domtopOut($fhr, $fhw);
            }
            else {
                copy($fhr, $fhw);
            }
            $fhr->close();
            $fhw->close();

            # MOVE result file :: local HDD to NFS
            my$cmd = "mv $fileLocal $OUTDIR/$name"; 
            system("$cmd");
            exit(0);
		}
	}
}
sub waitBlast {
	my($runnum) = @_;
	if ($mode eq 'qsub') {

        my @jobids  = sort keys %JobIDs;
        for (;;) {
            my$sta_ref = getQstat();
			foreach my $jobid (@jobids) {
                if (!exists($sta_ref->{"$jobid"})) {
					delete $JobIDs{$jobid};
                }
                elsif ($sta_ref->{"$jobid"} =~ /^Eqw$/) {
                    my($cmd) = "$CMD_qmod -cj $jobid";
                    print STDERR "WARNING :: $cmd\n";
                    system("$cmd");
                }
			}

			@jobids  = keys %JobIDs;
    		if (scalar(@jobids) < $runnum) {
                last;
            }
            sleep 5;
		}
	} else {
		my @pids = keys %PIDs;
		while (@pids >= $runnum) {
			sleep 5;
			if ($kid = waitpid(-1, &WNOHANG)) {
				delete $PIDs{$kid};
				@pids = keys %PIDs;
			}
		}
	}

    return;
}
sub check_FinishedResults {
	my($qfile);
	my($errcnt);
	if (! $LastName  && $LastCount) {
		($LastName, $LastCount) = &load_lastfile;
	}

	opendir(D, "$OUTDIR");
	while ($qfile = readdir(D)) {
		my($cnt,$qname);
		my($error_line, @F);
		next if ($qfile =~ /^\./);
		next if (&check_Finished($qfile));

if ($CHECK_COUNT) {
		my($qcnt);
		open(F, "$QDIR/$qfile") || die("Can not open $QDIR/$qfile($!)");
		while(<F>){
			$qcnt ++ if(/^>/);
		}
		close(F);
		if ( ($qcnt == $BLKSIZ) ||
				($qfile eq $LastName && $qcnt == $LastCount) ) {
			# OK
		} else {
			# lack of sequences; possible error -- delete qfile
			print STDERR "Qfile error: $qfile: lack of sequences: $qcnt; $BLKSIZ\n";
			unlink("$QDIR/$qfile");
			next;
		}
}
#		open(F, "$OUTDIR/$qfile") || die("Can not open $OUTDIR/$qfile($!)");
		my($sta) = open(F, "$OUTDIR/$qfile");
        if ($sta) {
    		while(<F>){
	    		if (/^# Query: (\S*)$/) {
		    		$qname = $1;
			    	$cnt++;
    			} elsif (/^#/) {
	    		} elsif ($CHECK) {
		    		@F = split;
			    	if (scalar(@F) != 12) {
				    	$error_line = $_;
					    last;
    				}
	    			if ($CHECK_NAMELEN1 &&
		    		    length($F[0]) != $CHECK_NAMELEN1) {
			    		$error_line = $_; last;
				    }
    				if ($CHECK_NAMELEN2 &&
	    				length($F[1]) != $CHECK_NAMELEN2) {
		    			$error_line = $_; last;
			    	}
                }
			}
    		close(F);
		}
        else {
			print STDERR "$qfile: NG $qname: Can not open : $OUTDIR/$qfile\n";
			$errcnt++;
        }
		if ($error_line) {
			print STDERR "$qfile: NG $qname: corrupt data: $error_line\n";
			$errcnt++;
		} elsif ( $CHECK_COUNT &&
				  ($cnt != $BLKSIZ) && 
			    ! ($qfile eq $LastName && $cnt == $LastCount) ) {
			# incomplete: possible error
			# delete the last entry
			print STDERR "$qfile: incomplete: $cnt, $qname; $BLKSIZ\n";
			$errcnt++;
		} else {
#print STDERR "OK: $qfile\n";
			&mark_Finished($qfile);
		}
	}
	$errcnt;
}
sub mark_Finished {
	my($name) = @_;
	$FinishedFile{$name} = 1;
	if($CHECKDIR) {
        new FileHandle(">$CHECKDIR/$name");
	}
}
sub check_Finished {
	my($name) = @_;
	if($CHECKDIR && -f "$CHECKDIR/$name") {
		$FinishedFile{$name} = 1;
	}
	$FinishedFile{$name};
}
sub store_lastfile {
	my($name, $count) = @_;
	open(LAST, ">$TMPDIR/lastdata") || die("Can not open $TMPDIR/lastdata($!)");
	print LAST "$name $count\n";
	close(LAST);
}
sub exist_lastfile {
	return -e "$TMPDIR/lastdata";
}
sub load_lastfile {
	open(LAST, "$TMPDIR/lastdata") || die("Can not open $TMPDIR/lastdata($!)");
	my($name, $count) = split(/\s+/, <LAST>);
	($name, $count);
}

sub cmpr_mdate {
	## positive if $file1 is newer than $file2
	my($file1,$file2) = @_;
	my @st1 = stat($file1);
	my @st2 = stat($file2);
	$st1[9] <=> $st2[9];
}

sub domtopOut {
	my($fhr) = shift;
	my($fhw) = shift;

	my($refCutoff) = {};
	if ($cutoff_file && -e $cutoff_file) {
		my($fh) = new FileHandle("$cutoff_file") || print STDERR "Can not open $cutoff_file($!)\n";
		if ($fh) {
			while($_ = $fh->getline()) {
				my($name, $cutoff) = split;
				$refCutoff->{"$name"} = $cutoff;
			}
		}
		$fh->close();
	}

	my($rank);
	my($hspdata) = '';
	my($ent);
	my($ent0) = {};
	while($_ = $fhr->getline()) {
		s#[\r\n]*$##;
		if (/^#\s*Query:\s*(\S+)/) {
			$query = $1;
			$refPosList = [];
			$rank = 1;
			$hspnum = 1;
			next;
		}
		elsif (/^#/) {
			next;
		}

		my(@d) = split(/\t/);
		$ent = {};
		$ent->{'query'}            = $d[0];
#		$ent->{'query_length'}     = '';
		$ent->{'entname'}          = conv_entname($d[1]);
		$ent->{'identity'}         = $d[2];
		$ent->{'alignment_length'} = $d[3];
		$ent->{'mismatches'}       = $d[4];
		$ent->{'gap_open'}         = $d[5];
		$ent->{'query_from'}       = $d[6];
		$ent->{'query_to'}         = $d[7];
		$ent->{'sbjct_from'}       = $d[8];
		$ent->{'sbjct_to'}         = $d[9];
		$ent->{'evalue'}           = $d[10];
		$ent->{'score'}            = $d[11];

		$ent->{'evalue'} = "1" . $ent->{'evalue'} if ($ent->{'evalue'} =~ /^e/);
		$ent->{'evalue0'} = $ent->{'evalue'};
		$ent->{'score0'} = $ent->{'score'};
		if (($ent0->{'query'} eq $ent->{'query'}) && ($ent0->{'entname'} eq $ent->{'entname'})) {
			$ent->{'evalue0'} = $ent0->{'evalue0'};
			$ent->{'score0'} = $ent0->{'score0'};
			$hspnum++;
		}
		else {
			$rank = 1;
			$hspnum = 1;
		}
		$ent->{'rank'}             = $rank;
		$ent->{'hspnum'}           = $hspnum;

		my($entname) = $ent->{'entname'};
		if (! exists($refEnt2eval->{"$entname"})) {
			$refEnt2eval->{"$entname"} = $ent->{'evalue'};
		}

		$fhw->print(add_hspdata($ent, $refPosList, $refCutoff));
		if (($ent0->{'query'} eq $ent->{'query'}) &&
			($ent0->{'entname'} ne $ent->{'entname'})) {
			$rank++;
			$fhw->print($hspdata);
			$hspdata = '';
		}
		$ent0 = $ent;
	}
	$fhw->print($hspdata) if ($hspdata);
}

sub add_hspdata {
	my($ent) = shift;
	my($refPosList) = shift;
	my($refCutoff) = shift;
	my($hspdata) = '';

	my($entname) = $ent->{'entname'};
	if ($EVAL_THRE && $ent->{'evalue0'} > $EVAL_THRE) {
		return '';
	}

#if ($hiscore{$entname} < $Cutoff{$entname}) {
#print STDERR "cut>>$entname,$hiscore{$entname},$Cutoff{$entname}\n";
#}

	#
    my($qfrom) = $ent->{'query_from'};
    my($qto)   = $ent->{'query_to'};
	return '' if ($DOMTOP && ! &domCheck($qfrom, $qto, $refPosList));
	return '' if ($ent->{'score0'} < $refCutoff->{$entname});
	if ($qfrom > $qto) {
		$str1 = '-';
		$tmp = $qfrom; $qfrom = $qto; $qto = $tmp;
	} else {
		$str1 = '+';
	}

	#
    my($sfrom) = $ent->{'sbjct_from'};
    my($sto)   = $ent->{'sbjct_to'};
	if ($sfrom > $sto) {
		$str2 = '-';
		$tmp = $sfrom; $sfrom = $sto; $sto = $tmp;
	} else{
		$str2 = '+';
	}

	#
    my($rank)   = $ent->{'rank'};
	return '' if ($TOP && $rank > $TOP);
#print "FFFFF $entname $hiscore{$entname} $Cutoff{$entname}\n" if ($rank > 1);
	$hspdata = join("\t", $ent->{'query'}, $ent->{'query_length'}, $ent->{'entname'}, $ent->{'ent_length'},
					$ent->{'evalue0'},
					$ent->{'hspnum'},
					$ent->{'query_from'}, $ent->{'query_to'},
					$ent->{'sbjct_from'}, $ent->{'sbjct_to'},
					$ent->{'score'}, $ent->{'evalue'},
					$descr) . "\n";

	return $hspdata;
}

sub domCheck {
	my($from, $to, $refPosList) = @_;
	my($flag, $pos, $prevpos, $SegLen, $CovLen, $k, $insF, $insT);
#print "($from,$to)\n";
	foreach my$p (@{$refPosList}) {
#print "p>>$p->{pos},$p->{type}\n";
		if ($p->{pos} > $from && ! $flag) {
			$prevpos = $from;
			$insF = $k;
			$flag = 1;
		}
		if ($flag) {
			if ($p->{pos} > $to) {
				$pos = $to + 1;
			} elsif ($p->{type} eq 'f') {
				$pos = $p->{pos};
			} else {
				$pos = $p->{pos} + 1;
			}
			if ($seg > 0) {
				$CovLen += $pos - $prevpos;
			}
			if ($p->{pos} > $to) {
				$insT = $k;
				last;
			}
			$prevpos = $pos;
		}
		if ($p->{type} eq 'f') {
			$seg++;
		} else {
			$seg--;
		}
		$k++;
	}
	$SegLen = $to - $from + 1;
#print ">>>CovLen=$CovLen; $Len; ";
#print scalar(@{$refPosList}),"\n";
	if ($CovLen < $SegLen * 0.5) {
		splice(@{$refPosList}, $insT, 0, {pos=>$to, type=>'t'});
		splice(@{$refPosList}, $insF, 0, {pos=>$from, type=>'f'});
		return 1;
	}
}

sub conv_entname {
	my($entname) = @_;
	if ($entname =~ /\|/) {
		my(@entparse) = split(/\|/, $entname);
		$entname = $entparse[$#entparse];
	}
	if ($sdb && $entname !~ /:/) {
		$entname = "$sdb:$entname";
	} elsif ($sdb && $entname =~ /gp:(.*)/) {
		$entname = "$sdb:$1";
	}

	return $entname;
}

###############################################################################
#
sub getQstat {
    my($sta_ref) = {};

	my($cmd) = "$main::CMD_qstat";
    my(@stat) = `$cmd`;
    my($id);
    foreach my$line (@stat) {
        $line =~ s#^\s*##;
        $line =~ s#[\r\n]*$##;
        my(@qstat_list) = split(/\s+/, $line);
        my($job_id)  = $qstat_list[0];
        my($job_sta) = $qstat_list[4];
	$job_id =~ s/\..*$//;

        if ($job_id !~ /^\d+$/) {
            print "DBG :: SKIP :: JOBID=$jobid\n" if ($main::DEBUG);
            next;
        }

        $sta_ref->{"$job_id"} = $job_sta;
    }

    return $sta_ref;
}

1;#

