#!/usr/local/bin/perl -w
#
# $Id: DM-MCL.pl,v 1.2 2006/11/13 02:32:50 nishi Exp $
#------------------------------------------------------------------
# Dialog Manager Macro Control Layer Ver.0.1
# 
# Filename : DM-MCL.pl
#
# based on AM-MCL.pl coded by Shin-ichi KAWAMOTO (skawa@jaist.ac.jp)
# coded by Takuya NISHIMOTO (nishi@hil.t.u-tokyo.ac.jp)
#
##################################################################
# 標準モジュールの取り込み
use strict;
use IPC::Open2;
use IO::Handle;
use IO::Select;

# by nishi
my( %COUNT );	# counter for various action
my( $debug ) = 0;	# set verbose mode if 1
sub print_debug{
    $COUNT{'debugout'}++;
    print( STDERR "From \@DM-MCL tell debug$COUNT{'debugout'}: @_") if $debug ;	# debug out
}

### 入出力をノンブロッキングにする
#select(STDIN); $| = 1;
#select(STDOUT); $| = 1;
#STDOUT->autoflush(1);
### 入力待ち行列
my @bufline = ();		# コマンド受理部のコマンドバッファ
my @SetRunBufline = ();		# "set Run"コマンド用のバッファ
my @InqBufline = ();		# "inq"コマンド用のバッファ
my @SpeakBufline = ();		# "set Speak"コマンド用のバッファ
### STDIN timeout
my $timeout = 0.1;		# 100ms
### variables for select()
my $read_bits = '';
my $err_bits = '';
my $sel = IO::Select->new();
$sel->add(*STDIN);
my @ready;
### variables for STDIN readlines.
my ($STDINchar, $STDINstr, $readline);
### "set Run"コマンド用変数
my $execFlagSetRun = 0;
### "inq"コマンド用変数
my $execFlagInq = 0;
### "set Speak"コマンド用変数
my $execFlagSpeak = 0;		# 実行フラグ
my $execStateSpeak = 0;		# 状態ID
my $execSpeakPhones = "";	# 発話音素系列
my $execSpeakReadySSM = 0;	# 音声合成部の準備完了フラグ
my $execSpeakReadyFSM = 0;	# 顔画像合成部の準備完了フラグ
my $execSpeaking = 0;		# 音声発話中フラグ
my @tmp2Phone = ();
my @tmp2PhoneA = ();
my @tmp2PhoneB = ();
my $tmp1 = "";
my $tmp2 = "";
my $tmp3 = "";
my $tmp4 = "";
my $tmp1old = "";
my $tmp2old = "";
my $tmpPhoneNum = 0;
my $tmpDuration = 0;
my $SSM_silB_dur = 0;
my $SSM_wait_time = 0;
my $FSM_wait_time = 0;
### MCL_Macro用
my $MacroReadline ='';
### exec*用
my $ExecReadline ='';
my $MacroExecFlag = 0;		# このターンでマクロモジュールを呼び出したか？



### 入力待ち
while (1) {
  $MacroExecFlag = 0;
  STDOUT->flush();
  STDERR->flush();
  # 標準入力から読み込む #################################
  ### select()関数の準備
  $read_bits = '';
  $err_bits = '';
  vec($read_bits, fileno(STDIN), 1) = 1;
  $err_bits=$read_bits;
  select($read_bits, undef, $err_bits, $timeout);
  if (vec($read_bits, fileno(STDIN), 1)) { # STDIN読み込み可能ならば
    # １文字読み込み
    	STDIN->sysread($STDINchar, 1) or die("read-net:$!");
    #	STDIN->sysread($STDINchar, 1) or next;
    	$STDINstr .= $STDINchar;
    #$STDINstr = <STDIN>;
    #$STDINchar = "\n";
    if ($STDINchar =~ /\n/) {	# １行読み込み完了したら
      if ($STDINstr =~ /^\s*\n$/ || $STDINstr eq "") {
	# 空行はSKIP
      } elsif ($STDINstr =~ /^\s*From/) { # moduleからの出力
	MCL_Macro($STDINstr); # 即、マクロ処理部に渡す
	$MacroExecFlag = 1;
      } elsif ($STDINstr =~ /^\s*set\s+Speak\s*=\s*STOP\s*$/) {
	# set Speak = STOP
	MCL_Macro($STDINstr); # 即、マクロ処理部に渡す
      }	else {			# マクロコマンド
	push(@bufline,$STDINstr); # コマンドをバッファに保持
      }
      $STDINstr = '';		# 初期化
    }
  }
  # バッファのコマンド処理 #################################
  if($MacroExecFlag!=1){
    if ($#bufline<0) {		 # バッファが空のとき
      # 処理対象コマンドが無いのでSKIP
    } else {			# バッファにコマンド読み込み済みのとき
      if (Check_MCL_Macro()==0) { # マクロ処理中でなければ
	$readline = shift(@bufline);
	MCL_Macro($readline);	# マクロ処理部にコマンドを渡す
      }
    }
  }
}
exit(1);
##################################################################
# マクロコマンド処理モジュール
my $insideBlock = 0;
my $insideRecogOut = 0;

sub MCL_Macro{
    my $content;

  ($MacroReadline) = @_;
  $_ = $MacroReadline;
  chomp;

    # by nishi
    # "From @MON" ではじまる場合 or "to @???" ではじまる場合
    if (/^From \@MON (.*)$/) {
	print STDOUT "to \@MON set SysLogText = $1\n";
	STDOUT->flush();
    }
    if (/^to /) {
	print STDOUT "to \@MON set SysLogText = $_\n";
	STDOUT->flush();
    }

  if (/^From/) {		# moduleからの出力
    if (Check_SetRun()!=0) {	# "set Run"マクロ処理中ならば
      execSetRun($MacroReadline);
    }
    if (Check_Inq()!=0) {	# "inq"マクロ処理中ならば
      execInq($MacroReadline);
    }

    # by nishi 
    #
    # From @SRM tell <INPUT STATUS="STARTREC" TIME="1051946298"/>
    # From @SRM tell <INPUT STATUS="ENDREC" TIME="1051946300"/>
    # From @SRM tell << EOM
    # From @SRM <RECOGOUT>
    # From @SRM   <SHYPO RANK="1" SCORE="-3556.441895" GRAM="5">
    # From @SRM     <WHYPO WORD="silB" CLASSID="15" PHONE="silB"/>
    # From @SRM     <WHYPO WORD="ありがとう" CLASSID="13" PHONE="a r i g a t o:"/>
    # From @SRM     <WHYPO WORD="silE" CLASSID="16" PHONE="silE"/>
    # From @SRM   </SHYPO>
    # From @SRM </RECOGOUT>
    # From @SRM EOM
    # From @SRM tell <INPUT STATUS="LISTEN" TIME="1051946300"/>

    # From @SRM tell <INPUT STATUS="STARTREC" TIME="1051946298"/>
    # From @SRM tell <INPUT STATUS="ENDREC" TIME="1051946300"/>
    # From @SRM tell <RECOGFAIL>
    # From @SRM tell <INPUT STATUS="LISTEN" TIME="1051946300"/>

    # From @SRM tell grammar send complete

    if ($MacroReadline =~ /^\s*From\s+\@SRM\s+(.*)$/) {
	if ( $1 =~ m/tell\s+<<\s+EOM/ ) {
	    # tell << EOM
	    print_debug("Block = BEGIN\n");
	    $insideBlock = 1;
	} elsif ( $1 =~ m/EOM/ ) {
	    # EOM
	    print_debug("Block = END\n");
	    $insideBlock = 0;
	} elsif ( $1 =~ m/tell\s+<INPUT\s+(.*)\/>$/ ) {
	    # <INPUT STATUS=??
	    print_debug("RecogStatus = $1\n");
	    print STDOUT "to \@SIM set RecogStatus = $1\n";
	    STDOUT->flush();
	} elsif ( $1 =~ m/tell\s+<RECOGFAIL>$/ ) {
	    # <INPUT STATUS=??
	    print_debug("RecogStatus = RECOGFAIL\n");
	    print STDOUT "to \@SIM set RecogStatus = RECOGFAIL\n";
	    STDOUT->flush();
	} elsif ( $1 =~ m/tell grammar conversion complete/ ) {
	    # 
	    print_debug("RecogStatus = GRAMMAR_CONVERSION_COMPLETE\n");
	    print STDOUT "to \@SIM set RecogStatus = GRAMMAR_CONVERSION_COMPLETE\n";
	    STDOUT->flush();
	} elsif ( $1 =~ m/tell grammar send complete/ ) {
	    # 
	    print_debug("RecogStatus = GRAMMAR_SEND_COMPLETE\n");
	    print STDOUT "to \@SIM set RecogStatus = GRAMMAR_SEND_COMPLETE\n";
	    STDOUT->flush();
	} elsif ( $1 =~ m/tell error/ ) {
	    #
	    print_debug("RecogStatus = GRAMMAR_ERROR\n");
	    print STDOUT "to \@SIM set RecogStatus = GRAMMAR_ERROR\n";
	    STDOUT->flush();
	} elsif ( $1 =~ m/tell unsupported/ ) {
	    #
	    print_debug("RecogStatus = GRAMMAR_ERROR\n");
	    print STDOUT "to \@SIM set RecogStatus = GRAMMAR_ERROR\n";
	    STDOUT->flush();
	} elsif ( $1 =~ m/tell grammar conversion failed/ ) {
	    #
	    print_debug("RecogStatus = GRAMMAR_ERROR\n");
	    print STDOUT "to \@SIM set RecogStatus = GRAMMAR_ERROR\n";
	    STDOUT->flush();
	} else {
	    if ($insideBlock) {
		$content = $1; 
		if ( $content =~ m/<RECOGOUT>/ ) {
		    $insideRecogOut = 1;
		    print STDOUT "to \@SIM set RecogOut = $content\n";
		    STDOUT->flush();
		} elsif ( $content =~ m/<\/RECOGOUT>/ ) {
		    print STDOUT "to \@SIM set RecogOut = $content\n";
		    STDOUT->flush();
		    $insideRecogOut = 0;
		} else {
		    if ( $insideRecogOut ) {
			print STDOUT "to \@SIM set RecogOut = $content\n";
			STDOUT->flush();
		    }
		}
	    } else {
		print_debug("RecogOut = IGNORE $1\n");
	    }
	}
    }

    #
    # GUI,MON,SIM からの to @XXX コマンドを再配信する
    #
    if ($MacroReadline =~ /^\s*From\s+\@GUI\s+(.*)$/) {
	print STDOUT "$1\n";
	STDOUT->flush();
    }

    if ($MacroReadline =~ /^\s*From\s+\@MON\s+(.*)$/) {
	print STDOUT "$1\n";
	STDOUT->flush();
    }

    if ($MacroReadline =~ /^\s*From\s+\@SIM\s+(.*)$/) {
	print STDOUT "$1\n";
	STDOUT->flush();
    }

    #
    # PAR と SSM/FSM の相互通信
    #

    if ($MacroReadline =~ /^\s*From\s+\@PAR\s+(.*)$/) {
	print STDOUT "$1\n";
	STDOUT->flush();
    }

    if ($MacroReadline =~ /^\s*From\s+\@SSM\s+rep\s+Speak\.stat\s*=\s*(.*)$/) {
	print STDOUT "to \@PAR set SSMSpeak.stat = $1\n";
	STDOUT->flush();
    }

    if ($MacroReadline =~ /^\s*From\s+\@FSM\s+rep\s+Speak\.stat\s*=\s*(.*)$/) {
	print STDOUT "to \@PAR set FSMSpeak.stat = $1\n";
	STDOUT->flush();
    }

  } else {			# マクロコマンド
    if ($MacroReadline =~ /^\s*macro\s+(.*)/) {	# 先頭の"macro"をcut
      $MacroReadline = $1;
    }

    # "set Run"コマンド
    if ($MacroReadline =~ /set\s+Run\./) {
      if (Check_SetRun()==0) {	# "set Run"マクロ処理中でなければ
	if ($#SetRunBufline<0) {  # バッファが空
	  # 指定コマンドを即実行
	} else {
	  # 指定コマンドはバッファに保持
	  push(@SetRunBufline,$MacroReadline);
	  # バッファの先頭コマンドを実行
	  $MacroReadline = shift(@SetRunBufline);
	}
	execSetRun($MacroReadline); # コマンド実行
      } else {
	push(@SetRunBufline,$MacroReadline); # コマンドをバッファに保持
      }
    }
    # "inq"コマンド
    if ($MacroReadline =~ /inq/) {
      if (Check_Inq()==0) {	# "inq"マクロ処理中でなければ
	if ($#InqBufline<0) { 	# バッファが空
	  # 指定コマンドを即実行
	} else {
	  # 指定コマンドはバッファに保持
	  push(@InqBufline,$MacroReadline);
	  # バッファの先頭コマンドを実行
	  $MacroReadline = shift(@InqBufline);
	}
	execInq($MacroReadline); # コマンド実行
      } else {
	push(@InqBufline,$MacroReadline); # コマンドをバッファに保持
      }
    }
  }
}
##################################################################
# マクロコマンド処理状況チェックモジュール
sub Check_SetRun{		# "set Run"コマンド実行状況
  $execFlagSetRun;
}
sub Check_Inq{			# "inq"コマンド実行状況
  $execFlagInq;
}
sub Check_Speak{		# "set Speak"コマンド実行状況
  $execFlagSpeak;
}
sub Check_MCL_Macro{
  $execFlagSpeak;
  #    if($execFlagSetRun==0
  #       && $execFlagInq==0
  #       && $execFlagSpeak==0){
  #	0;
  #    }else{
  #	1;
  #    }
}
##################################################################
# 各コマンドの実行モジュール
sub execSetRun{			# "set Run"実行コマンド
  ($ExecReadline) = @_;

  $execFlagSetRun = 1;		# 実行中
  #
  # set Run.(モジュール名) = (値)
  #
  $_ = $ExecReadline;
  chomp;
  if (/^set\s+Run\.(\S+)\s*=\s*(\S+)\s*$/) {
    my ($com,$slot,$module,$value);
    $com = "set";
    $slot = "Run";
    $module = $1;
    $value = $2;
    print STDOUT "to \@$module $com $slot = $value\n";
    STDOUT->flush();
  }
  $execFlagSetRun = 0;		# 非実行中
}
#-----------------------------------------------------------------
sub execInq{			# "inq"実行コマンド
  ($ExecReadline) = @_;

  $execFlagInq = 1;		# 実行中
  #
  # inq (Run|ProtocolVersion|ModuleVersion).(モジュール名)
  #
  $_ = $ExecReadline;
  chomp;
  if (/^inq\s+(Run|ProtocolVersion|ModuleVersion)\.(\S+)\s*$/) {
    my ($com,$slot,$module);
    $com = "inq";
    $slot = $1;
    $module = $2;
    print STDOUT "to \@$module $com $slot\n";
    STDOUT->flush();
  }
  elsif (/^inq\s+Speak\.(\S+)\s*$/) {
    my ($com,$slot,$module);
    $com = "inq";
    $slot = "Speak.$1";
    $module = "SSM";		# 問い合わせモジュールは音声合成モジュールに固定
    print STDOUT "to \@$module $com $slot\n";
    STDOUT->flush();
  }
  $execFlagInq = 0;		# 非実行中
}
#-----------------------------------------------------------------

##################################################################
# EOF
##################################################################
