#################################################################################
# ===============================================================================
#            Copyright (C) 2001 GoldPocket Interactive, Inc.
#                      PROPRIETARY and CONFIDENTIAL
# ===============================================================================
# TBSAutoDemo.pl
#
# CREATION DATE
#   08-??-01
#	09-25-01 (modified) 
#
# ORIGINAL AUTHOR
#   Joseph Su, GoldPocket Interactive, Inc.
#   Markus Kangas, GoldPocket Interactive, Inc.
#
# DESCRIPTION
#   1. This program extracts certain input fields from the xml node, in the order of
#      their start times
#	2. Insert ATVEF triggers according to the start time, type, and id specified by 
#      the extracted fields
#
# REFERENCES
#
# NOTES/RESTRICTIONS
#
#################################################################################

require 5.003;

use XML::Parser;
use Win32::SerialPort qw( :STAT 0.19 );
use IO::Socket;


my(
	$POLL_RESULT_SLACK_TIME,    # Delay time from when the last poll message was sent
								# It defines the time (sec) needed to calculate 
								# poll results

	@ALLVALS,					# Array that holds time, type, and id information from 
								# the xml node

	@TIMEVALS,					# Array that holds time 
	@TYPEVALS,					# Array that holds type
	@IDVALS,					# Array that holds id
	$INPUT_FILE,				# File handle 
	$parser,					# Parser object used to parse xml node
	@factkeys,					# Array of hash keys that specifies the funfact node in xml
	@pollkeys,					# Array of hash keys that specifies the poll node in xml
	@triviakeys,				# Array of hash keys that specifies the trivia node in xml
	@leaderboardkeys,			# Array of hash keys that specifies the leaderboard node in xml
	@segmentkeys,				# Array of hash keys that specifies the segment trigger in xml
	@adjurykeys,				# Array of hash keys that specifies the adjury node in xml
			
	%currentAttrs,				# Hash table that holds all attributes at the current 
								# line of xml node

	@fields,					# Array of delimited time, type, and id pertaining 
	                            # to one object (i.e., funfact, leaderboard, etc)

	$begin_time,					# Start time of a trigger event
	$broadcaststring
);


##################################################################################
## Initialize all
##################################################################################
$broadcaststring = "Elimidate Deluxe Interactive";
$atvefURL = "http://eswebc.eventmatrix.com/10011/elimidate/index.asp";
$POLL_RESULT_SLACK_TIME = 7;
@ALLVALS = ();
@TIMEVALS = ();
@TYPEVALS = ();
@IDVALS = ();
@factkeys = ("factitemid", "starttime");
@pollkeys = ("pollitemid", "starttime", "type", "duration", "answertime");
@triviakeys = ("trivia_bwitemid", "starttime", "type", "duration", "answertime");
@leaderboardkeys = ("leaderboarditemid", "starttime");
@segmentkeys = ("timesegmentitemid", "starttime");
@adjurykeys = ("activeaditemid", "starttime");


##################################################################################
## Creating file handle and instantiate a XML parser object using XML::Parser (C-based)
##################################################################################
$INPUT_FILE = 'event.xml' || die "Can't find file \"$file\"";
    
$parser=new XML::Parser(Style=>'Stream');

$parser->parsefile($INPUT_FILE);        # Go ahead and call the setHandler routines
                                            # This will call StartTag, Text (not 
											# implemented here), and lastly EndTag

@ALLVALS = sort sort_by_number @ALLVALS;    # Sort by start time 



##################################################################################
## 1. Split each line using ":" as delimiter, and push each field into its 
##    corresponding name array
## 2. Debug print (time, type, and id arrays)
##################################################################################
my($i);
for ($i=0; $i<@ALLVALS; $i++) 
{
	@fields = split( /:/, @ALLVALS[$i] );
	push( @TIMEVALS, @fields[0] );
	push( @TYPEVALS, @fields[1] );
	push( @IDVALS, @fields[2] );

	#print  @ALLVALS[$i] . "\n";
	print @TIMEVALS[$i] . "\t" . @TYPEVALS[$i] . "\t" . @IDVALS[$i] . "\n";
}


##################################################################################
## Reverse the queues so that the first (earliest) trigger is on top, and will be
## popped off first 
##################################################################################
@TIMEVALS= reverse @TIMEVALS;
@TYPEVALS= reverse @TYPEVALS;
@IDVALS= reverse @IDVALS;


##################################################################################
## -----------------ATVEF trigger insertion starts here --------------------------                                 
##################################################################################


##################################################################################
## Opens up the COM1 port on the local machine, which is connected to the 
## time-code reader
##################################################################################
$readport=new Win32::SerialPort("COM1")
or die "cant open read port";


##################################################################################
## Define the host and port to connect to, in this case, the
## norpak machine
##################################################################################
$remote_host="192.168.0.104";
$remote_port=23;


##################################################################################
## Initialize a socket connection to the Norpak machine, using the TCP/IP
## protocol.
##################################################################################
$sendsocket=IO::Socket::INET->new(PeerAddr=>$remote_host,
			                PeerPort=>$remote_port,
			                Proto=>"tcp",
			                Type=>SOCK_STREAM)
 or die "cant connect to $remote_host:$remote_port : $@\n";

$sendsocket->autoflush(1);             # Flush out the socket, to ensure no garbage 
                                       # remains from some previous usage.

# $URL defines <-->
#$URL=$atvefURL;

# $EVENT_URL defines <-->
$EVENT_URL=$atvefURL;  #url for 10 second ping

# $FREQUENCY defines <--> 
$FREQUENCY=10;

# $START_OFFSET defines the offset from the script start time
$START_OFFSET=0;

# $PREFIX defines <-->
$PREFIX="tv"; #i.e. parent.bulkRequest(...


##################################################################################
## This code chunk initializes the com port for the time-code
## reader and sets the attributes for how to read information
## for the reader
##################################################################################
$readport->handshake("xoff");
$flowcontrol=$readport->handshake;
@opts=$readport->handshake;
$readport->baudrate(9600);
$readport->parity("odd");
$readport->databits(8);
$readport->stopbits(1);
$readport->buffers(4096,4096);
$readport->read_interval(100);


##################################################################################
## This code chunk defines all the message types for the output.txt
## file. The key is the message recieved from the output.txt file, and will
## be intepreted into the key-value as an ATVEF trigger later on
##################################################################################
%MessageTypes=();
$MessageTypes{'PollMessage'}="pollMessage";
$MessageTypes{'PollWithPoints'}="pollMessage";
$MessageTypes{'DelayedHybridPoll'}="pollMessage";
$MessageTypes{'PollResultsMessage'}="pollResults";
$MessageTypes{'PollResults'}="pollResults";
$MessageTypes{'PollWithPointsResults'}="pollResults";

$MessageTypes{'LeaderBoardMessage'}="leaderBoard";
$MessageTypes{'Leaderboard'}="leaderBoard";

$MessageTypes{'QuestionResultsMessage'}="questionResultMessage";
$MessageTypes{'AnswerMessage'}="answerMessage";
$MessageTypes{'TextMessage'}="textMessage";
$MessageTypes{'EventTriggerMessage'}="eventTriggerMessage";
$MessageTypes{'AdJuryResultsMessage'}="adJuryResultsMessage";
$MessageTypes{'AdJuryMessage'}="adJuryMessage";
$MessageTypes{'DisplayAnswer'}="displayAnswer";
$MessageTypes{'IncrementPoints'}="incrementPoints";
$MessageTypes{'Answer'}="displayAnswer";
$MessageTypes{'QuestionMessage'}="questionMessage";
$MessageTypes{'Trivia'}="questionMessage";
$MessageTypes{'FunFact'}="textMessage";

$MessageTypes{'Sync'}="SYNC";

$MessageTypes{'DelayedHybridPollResults'}="delayedHybridPollResults";
$MessageTypes{'DelayedTriviaResults'}="delayedTriviaResults";
$MessageTypes{'RapidDelayedTrivia'}="questionMessage";


#make sure all triggers are in the queue
$done=0;

#replace this with autoread from port and dump

#print "Press Enter to begin event!\n";
#while(($done==0)&&($line=<STDIN>)) {
#  $done=1;
#}

print "Event Started...\n";

(local($hour),local($minute),local($second))=(localtime)[2,1,0];

$begin_time=$second+$minute*60+$hour*3600;

$done=0;
print "Press Ctrl-C anytime to abort\n";


##################################################################################
## Let's try reading readport and dumping its packet info 
##################################################################################
$readport->read_interval(0);
$readport->read_const_time(10000);
$readport->is_read_interval(0xffffffff);


$InBytes=10;
$cnt=0;

my $in = $readport->read_bg(500);
my $done = 0;
my $blk;
my $err;
my $out;

$leftover="";
$oldtime=-1;
$done=0;
$tmp2=0;
$tickcount=0;
$lastvalidtime=0;

while($done==0) {
    $cnt++;
    select(undef, undef, undef, .03); # sleep for 30 milliseconds
    $result=$readport->input;
    $len=length($result);
    $timechange=0;
    if($len == 10) {
          

        $begin=index($result,chr(243));
        if($begin>=0) {$tmp=substr($result,$begin);} else {$tmp="";}
        
        while($begin>=0&&length($tmp)>=4) {
	   $result=substr($result,$begin);
              
   	   $n1=ord(substr($result,0,1));   
	   $n2=ord(substr($result,1,1));
	   $n3=ord(substr($result,2,1));
	   $n4=ord(substr($result,3,1));
	   $n5=ord(substr($result,4,1));
   	   $n6=ord(substr($result,5,1));   
	   $n7=ord(substr($result,6,1));
	   $n8=ord(substr($result,7,1));
	   $n9=ord(substr($result,8,1));
	   $n10=ord(substr($result,9,1));

           $seconds = ($n3 & 0x0f) + ((($n3 & 0xf0) >> 4) * 10);
           $minutes = ($n4 & 0x0f) + ((($n4 & 0xf0) >> 4) * 10);
           $hours   = ($n5 & 0x0f) + ((($n5 & 0xf0) >> 4) * 10);



           if ((($n1&0xf0) == 0xf0) && ($n1 != 0xff))
           {
	           $val=$seconds + $minutes*60 + $hours*3600;
                   $time=$val; 
                   if($oldtime!=$time) 
                   {
		      $tickcount=Win32::GetTickCount();
#since its 10 we use this value 
#if its not we use the gettickcount-lastreadoffset for a given system time 

                      $lastvalidtime=$time;
                      $timechange=1;
                      $oldtime=$time;
                      print("$hours:$minutes:$seconds\n");
                   }
           }
           $begin=index($result,chr(243));
           $tmp=substr($result,$begin);
           $begin=-1;
        }

        $leftover=$leftover.$result;
    } elsif ($tickcount>0&&$lastvalidtime>0) { 
       #now we do a checktickcount. subtract from the last real tickcount and offset the time
	$tickcount2=Win32::GetTickCount();
        $time=$lastvalidtime + int(($tickcount2 - $tickcount)/1000);
	$timechange=0;
        if($time!=$oldtime) 
        {
           $timechange=1;
           print "Artificial time $time\n";
           $oldtime=$time;
        }
    }


##################################################################################
## Now use the time
##################################################################################
if($timechange) {
   if($time % $FREQUENCY ==0) {
        $tmp="";
	for($a=0;$a<$tmp2;$a++) {
	    $tmp=$tmp." ";
	    print "*";
        }
	$URL=$EVENT_URL."?$tmp2";
	$sendstring="<$URL>[n:${broadcaststring}$tmp][v:t]";
	$_=$sendstring;

	$nbytes=length($sendstring);
	$sum=0;
	for($count=0;$count<$nbytes;$count+=2) {
  	  $sum+=ord(substr($_,$count,1)) << 8;
  	  if($count!=$nbytes-1) 
	     {$sum+=ord(substr($_,$count+1,1));}
	  while($sum>=65536) {
             $sum-=65536;
             $sum+=1;
          }

        }
        $sum2=65535-$sum;
        $checksum=sprintf("\[%4X\]" ,$sum2);
	$checksum=~s/A-Z/a-z/g;
	$sendstring=$sendstring.$checksum;
	print $sendsocket "$sendstring\r";
        print "Sent $sendstring\n";
        
   }
   local($ok)=0;
   while($ok==0) {
     $val=pop(@TIMEVALS);
     if($val<=$time) {
        $time=$val;
        $type=pop(@TYPEVALS);
        $id=pop(@IDVALS);
	print("\n--> Inserting $type, id=$id, time=$hours:$minutes:$seconds\n");
	$sendstring="<$EVENT_URL>[v:1][s:bulkRequest('$MessageTypes{$type}',$id)]";
        if($MessageTypes{$type} eq "SYNC") {
 	     $sendstring="<>[gpi-changemode:$id]";
		$tmp2++; # increment the segment count used for announcement URLs
        }
	$_=$sendstring;

	$nbytes=length($sendstring);
	$sum=0;
	for($count=0;$count<$nbytes;$count+=2) {
  	  $sum+=ord(substr($_,$count,1)) << 8;
  	  if($count!=$nbytes-1) 
	     {$sum+=ord(substr($_,$count+1,1));}
	  while($sum>=65536) {
             $sum-=65536;
             $sum+=1;
          }

        }
        $sum2=65535-$sum;
        $checksum=sprintf("\[%4X\]" ,$sum2);
	$checksum=~s/A-Z/a-z/g;

	$sendstring=$sendstring.$checksum;
	print $sendsocket "$sendstring\r";
	print "\n".$sendstring."\n";

     } else {push(@TIMEVALS,$val);$ok=1;}
     if(@TIMEVALS[0] eq "") {
 	$done=1;$ok=1;
	print "ALL Done\n";
     }
   }
   

}
}


$readport->close || die "failed to close";
undef $readport;


##################################################################################
## ------------------------- End of Main -------------------------------------- ##
##################################################################################


##################################################################################
## ----------------------- Start of Subroutines ------------------------------- ##                                
##################################################################################

##################################################################################
## StartTag is called first when parser starts the parsing routine, i.e., when it
## encounters the start of a xml node (root tag)
#  
#---------------------------------------------------------------------------------
#                              Output Format
#---------------------------------------------------------------------------------
# PollMessage:
#		ID:				pollitemid
#		Type:			1
#       Result:			PollResults
#       Trigger time:	"starttime" + $POLL_RESULT_SLACK_TIME
#
# PollWithPoints
#		ID:				pollitemid
#		Type:			2
#       Result:			PollWithPointsResults
#       Trigger time:	"starttime" + $POLL_RESULT_SLACK_TIME
#
# DelayedPoll
#		ID:				pollitemid
#		Type:			3
#		Result:		    DelayedPollResults
#       Trigger time:	"starttime" + $POLL_RESULT_SLACK_TIME
#
# DelayedHybridPoll
#		ID:				pollitemid
#		Type:			4
#	    Result:			DelayedHybridPollResults
#       Trigger time:	"starttime" + $POLL_RESULT_SLACK_TIME
#---------------------------------------------------------------------------------
# Trivia
#		ID:				trivia_bwitemid
#		Type:			1
#	    Result:			TriviaResults
#       Trigger time:	"starttime" + "duration"
#
# DelayedTrivia
#		ID:				trivia_bwitemid
#		Type:			2
#	    Result:			DelayedTriviaResults
#       Trigger time:	"starttime" + "duration"
#
# RapidDelayedTrivia
#		ID:				trivia_bwitemid
#		Type:			3
#		Result:			RapidDelayedTriviaResults
#		Trigger time:	"starttime" + "duration"
#----------------------------------------------------------------------------------
# LeaderBoardMessage
#		ID:				leaderboarditemid      
#----------------------------------------------------------------------------------
# FunFact
#		ID:				factitemid      
#----------------------------------------------------------------------------------
# Sync
#		ID:				(timesegmentitemid+1)*10		
#----------------------------------------------------------------------------------
# AdJuryMessage
#		ID:				activeaditemid      
#----------------------------------------------------------------------------------
###################################################################################
sub StartTag {
	my ($expat,$eltype)=@_;
	#print "StartTag: $_ \n";
	
	%currentAttrs = %_;

	if ( $eltype eq 'factitem' )
    {

		push (@ALLVALS, GetStartTime($factkeys[1]) . ":" . "FunFact" . ":" . $currentAttrs{$factkeys[0]});		

	}
 	elsif ( $eltype eq 'pollitem' )
    {
		if ( $currentAttrs{$pollkeys[2]} == "1" )       # Type = 1 for regular poll
		{
			push (@ALLVALS, GetStartTime($pollkeys[1]) . ":" . "PollMessage" . ":" . $currentAttrs{$pollkeys[0]});		
			push (@ALLVALS, GetStartTime($pollkeys[1])+$currentAttrs{$pollkeys[3]}+$POLL_RESULT_SLACK_TIME . ":" . "PollResults" . ":" . $currentAttrs{$pollkeys[0]});	
		} 
		elsif ( $currentAttrs{$pollkeys[2]} == "2" )    # Type = 2 for poll with points
		{
			push (@ALLVALS, GetStartTime($pollkeys[1]) . ":" . "PollWithPoints" . ":" . $currentAttrs{$pollkeys[0]});		
			push (@ALLVALS, GetStartTime($pollkeys[1])+$currentAttrs{$pollkeys[3]}+$POLL_RESULT_SLACK_TIME . ":" . "PollWithPointsResults" . ":" . $currentAttrs{$pollkeys[0]});	
		} 
		elsif ( $currentAttrs{$pollkeys[2]} == "3" )    # Type = 3 for delayed poll
		{
			push (@ALLVALS, GetStartTime($pollkeys[1]) . ":" . "DelayedPoll" . ":" . $currentAttrs{$pollkeys[0]});		
			push (@ALLVALS, GetStartTime($pollkeys[1])+$currentAttrs{$pollkeys[3]}+$POLL_RESULT_SLACK_TIME . ":" . "DelayedPollResults" . ":" . $currentAttrs{$pollkeys[0]});	
		} 
		elsif ( $currentAttrs{$pollkeys[2]} == "4" )    # Type = 4 for delayed hybrid poll
		{
			push (@ALLVALS, GetStartTime($pollkeys[1]) . ":" . "DelayedHybridPoll" . ":" . $currentAttrs{$pollkeys[0]});
			push (@ALLVALS, GetStartTime($pollkeys[1])+$currentAttrs{$pollkeys[3]}+$POLL_RESULT_SLACK_TIME . ":" . "PollResults" . ":" . $currentAttrs{$pollkeys[0]});	
			push (@ALLVALS, GetStartTime($pollkeys[4]) . ":" . "DelayedHybridPollResults" . ":" . $currentAttrs{$pollkeys[0]});
		}
		 
	}
 	elsif ( $eltype eq 'trivia_bwitem' )
    {		
		#print "trivia type = $currentAttrs{$triviakeys[2]} --------------------------------------- \n";

		if ( $currentAttrs{$triviakeys[2]} == "1" )      # Type = 1 for regular trivia
		{
			push (@ALLVALS, GetStartTime($triviakeys[1]) . ":" . "Trivia" . ":" . $currentAttrs{$triviakeys[0]});		
			push (@ALLVALS, GetStartTime($triviakeys[1])+$currentAttrs{$triviakeys[3]} . ":" . "TriviaResults" . ":" . $currentAttrs{$triviakeys[0]});		

		} 
		elsif ($currentAttrs{$triviakeys[2]} == "2")     # Type = 2 for delayed trivia
		{
			push (@ALLVALS, GetStartTime($triviakeys[1]) . ":" . "DelayedTrivia" . ":" . $currentAttrs{$triviakeys[0]});		
			push (@ALLVALS, GetStartTime($triviakeys[4]) . ":" . "DelayedTriviaResults" . ":" . $currentAttrs{$triviakeys[0]});		

		} 
		elsif ($currentAttrs{$triviakeys[2]} == "3")     # Type = 3 for rapidDelayedTrivia
		{
			push (@ALLVALS, GetStartTime($triviakeys[1]) . ":" . "RapidDelayedTrivia" . ":" . $currentAttrs{$triviakeys[0]});		
			push (@ALLVALS, GetStartTime($triviakeys[1])+$currentAttrs{$triviakeys[3]} . ":" . "RapidDelayedTriviaResults" . ":" . $currentAttrs{$triviakeys[0]});		
		}
	
	} 
	elsif ( $eltype eq 'leaderboarditem' )
    {
		push (@ALLVALS, GetStartTime($leaderboardkeys[1]) . ":" . "LeaderBoardMessage" . ":1");		
	}
	elsif ( $eltype eq 'timesegmentitem' )
    {
		push (@ALLVALS, GetStartTime($segmentkeys[1]) . ":" . "Sync" . ":" . ($currentAttrs{$segmentkeys[0]}+1)*10);		
	}
	elsif ( $eltype eq 'activeaditem' )
    {
		push (@ALLVALS, GetStartTime($adjurykeys[1]) . ":" . "AdJuryMessage" . ":" . $currentAttrs{$adjurykeys[0]});		
	}


}

##################################################################################
## EndTag is called after the parsing routine encounters the end of a xml node (root tag)
## Current this is not used, but can be further implemented depending on applications
##################################################################################
sub EndTag {
  my ($expat,$eltype)=@_;
  if ($eltype eq "root") {
    #print "END \n";
  }

}


##################################################################################
## Sorting an array by number (neither alphabetical nor ASCII-betical sort)
## This routine returns either 1 (a>b), 0 (a=b), or -1 (a<b)
##################################################################################
sub sort_by_number {
	$first = substr($a, 0, index($a, ":"));    #Get the first chunk of substring that ends in ":"
	$second = substr($b, 0, index($b, ":"));   #Get the second chunk of substring that ends in ":"
	if ($first > $second)
	{
		return 1;
	} elsif ($first < $second)
	{
		return -1;
	} else 
	{
		return 0;
	}
}


##################################################################################
# Parse the start time and convert everything to seconds
##################################################################################
sub GetStartTime
{
	#print "starttime: $currentAttrs{@_[0]} \n";		

	local $hour = substr($currentAttrs{@_[0]}, 0, 2);
	local $minute = substr($currentAttrs{@_[0]}, 3, 2);
	local $second = substr($currentAttrs{@_[0]}, 6, 2);
	$begin_time = $second + $minute*60 + $hour*3600;
	
	return $begin_time;
}