The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Linux::DVB::DVBT::Apps::QuartzPVR::Base::DbgTrace ;

=head1 NAME

Linux::DVB::DVBT::Apps::QuartzPVR::Base::DbgTrace - TVGuide debug trace

=head1 SYNOPSIS

use Linux::DVB::DVBT::Apps::QuartzPVR::Schedule ;


=head1 DESCRIPTION

Trace through the recording IDs (RIDs) and each of the processing steps that are taken for them. This allows me to see
where/why a recording request does not make it into the cron job.

Trace HASH is of the form:

	'state'	=> {							# Useful (?) state info
		
		'fn'		=> current function name
	},
	
	'rids'	=> {							# Trace of all the record IDs
		
		$rid	=> {						# info for this rid
			'info'	=> $record_href,		# HASH ref for this recording
			'trace'	=> [					# Trace details for each function
			
				{							# A HASH entry for function call
					'fn'	=> $fn,
					'info'	=> [			# List of information for this function
						{ ... },
						{ ... },
					],
				},
				...		
			],
			
		},
		...
	},

=head1 DIAGNOSTICS

Setting the debug flag to level 1 prints out (to STDOUT) some debug messages, setting it to level 2 prints out more verbose messages.

=head1 AUTHOR

Steve Price 

=head1 BUGS

None that I know of!

=head1 INTERFACE

=over 4

=cut

use strict ;
use Carp ;

our $VERSION = "1.002" ;

#============================================================================================
# USES
#============================================================================================
use Data::Dumper ;

use Linux::DVB::DVBT::Apps::QuartzPVR::Base::Constants ;

#============================================================================================
# OBJECT HIERARCHY
#============================================================================================

#============================================================================================
# GLOBALS
#============================================================================================

our $debug=0;
our $trace_flag ;
our %dbgtrace ;


#============================================================================================
# CONSTRUCTOR 
#============================================================================================

BEGIN
{
	$trace_flag = 0 ;
	%dbgtrace = () ;
}



#============================================================================================
# OBJECT DATA METHODS 
#============================================================================================

#-----------------------------------------------------------------------------

=item C<trace_flag($trace_flag)>

Set of clear the trace flag

=cut

sub trace_flag
{
	my ($flag) = @_ ;
	
	$trace_flag = $flag if (defined($flag)) ;
	
	return $trace_flag ;
}
	

#============================================================================================
# OBJECT METHODS 
#============================================================================================

#---------------------------------------------------------------------------------------------------
# Display all recordings
sub dbg_rids
{
	my ($recording_aref) = @_ ;
	
	foreach my $record_href (@$recording_aref)
	{
		printf "%5d : %s\n", 
			$record_href->{'rid'},
			format_rec($record_href) ;
	}

}

#---------------------------------------------------------------------------------------------------
# Clear out all entries
sub trace_clear
{
print "trace_clear() flag=$trace_flag\n" if $debug ;
	return unless $trace_flag ;
	
	$dbgtrace{'state'} = {
		'fn'	=> undef,
	} ;
	$dbgtrace{'rids'} = {
	} ;
}


#---------------------------------------------------------------------------------------------------
# Create entries for all recordings
sub trace_init
{
	my ($recording_aref) = @_ ;
	
print "trace_init() flag=$trace_flag\n" if $debug ;
	return unless $trace_flag ;
	
#	$dbgtrace{'state'} = {
#		'fn'	=> undef,
#	} ;
#	$dbgtrace{'rids'} = {
#	} ;

	foreach my $record_href (@$recording_aref)
	{
		new_rid($record_href->{'rid'}, $record_href) ;
	}

}


#---------------------------------------------------------------------------------------------------
# Create new rid entry
sub new_rid
{
	my ( $rid, $record_href) = @_ ;

print "new_rid(rid=$rid) flag=$trace_flag\n" if $debug ;
	return unless $trace_flag ;
	
	$dbgtrace{'rids'}{$rid} = {
		'info'	=> $record_href,
		'trace'	=> [],
	} ;

}

#---------------------------------------------------------------------------------------------------
# In new function
sub startfn
{
	my ($fn) = @_ ;

print "startfn($fn) flag=$trace_flag\n" if $debug ;

	return unless $trace_flag ;
	
	$dbgtrace{'state'}{'fn'} = $fn ;

	foreach my $rid (keys %{$dbgtrace{'rids'}})
	{
		push @{$dbgtrace{'rids'}{$rid}{'trace'}}, {
			'fn'	=> $fn,
			'info'	=> [],
		} ;
	}
}

#---------------------------------------------------------------------------------------------------
# Add some more trace info
sub add_trace
{
	my ($rid, $info) = @_ ;

print "add_trace($rid, $info) flag=$trace_flag\n" if $debug ;

	return unless $trace_flag ;
	
	if (exists($dbgtrace{'rids'}{$rid}))
	{
print "Trace[rid=$rid] " . Data::Dumper->Dump([$dbgtrace{'rids'}{$rid}{'trace'}]) if $debug ;
		#? does trace have any entries ?.....
		push @{$dbgtrace{'rids'}{$rid}{'trace'}[-1]{'info'}}, $info ;
	}
	else
	{
print "rid=$rid does not exist\n" if $debug ;
print "RIDS= " . Data::Dumper->Dump([$dbgtrace{'rids'}]) if $debug ;
		
	}
}

#---------------------------------------------------------------------------------------------------
# Add some more trace info using the recording HASH
sub add_rec
{
	my ($rec_href, $info) = @_ ;

	# Treat text as a message
	if (! ref($info) )
	{
		
		$info = {
			'msg'	=> $info,
		} ;
	}

	# Add this recording to the info if we can
	if (ref($info) eq 'HASH')
	{
		$info->{'recording'} = $rec_href ;
	}
	add_trace($rec_href->{'rid'}, $info) ;
}

#---------------------------------------------------------------------------------------------------
# Create trace
sub create_trace_report
{
	my $trace = "" ;
	return $trace unless $trace_flag ;
	
	my $now_string = localtime;
	
	foreach my $rid (keys %{$dbgtrace{'rids'}})
	{
		if ( ($trace_flag eq 'all') || ($trace_flag == $rid) )
		{
			$trace .= sprintf "\n\n== RID %5d : %s '%s' [%s] ==\n", 
				$rid,
				$dbgtrace{'rids'}{$rid}{'info'}{'channel'},
				$dbgtrace{'rids'}{$rid}{'info'}{'title'},
				Linux::DVB::DVBT::Apps::QuartzPVR::Base::Constants::record_types($dbgtrace{'rids'}{$rid}{'info'}{'record'}) ;
			
			# show each function call in turn
			foreach my $trace_href (@{$dbgtrace{'rids'}{$rid}{'trace'}})
			{
				# check we've got some entries
				if (scalar(@{$trace_href->{'info'}}))
				{
					# function
					$trace .= "\n  -- $trace_href->{'fn'} --\n" ;
					
					# entries
					my %grouped ;
					foreach my $info_href (@{$trace_href->{'info'}})
					{
						my $reckey ;
						if (exists($info_href->{'recording'}))
						{
							# Handle recording - save for later
							$reckey = sprintf "rid %5d : %s ", 
								$info_href->{'recording'}{'rid'},
								format_rec($info_href->{'recording'}) ;
							$grouped{$reckey} ||= [] ;
							if (exists($info_href->{'msg'}))
							{
								push @{$grouped{$reckey}}, $info_href->{'msg'} ;
							}
						}
						else
						{
							# Immediately show key/value
							foreach my $key (sort keys %$info_href)
							{
								$trace .=  "     " ;
								if ($key eq 'msg')
								{
									$trace .= "# $info_href->{'msg'}" ;
								}
								elsif ($key eq 'listings')
								{
									$trace .= "Listings:" ;
									foreach my $list_href (@{$info_href->{'listings'}})
									{
										$trace .= sprintf  "\n       pid %s : %s",
											 $list_href->{'pid'},
											 format_rec($list_href) ;
									}
								}
								else
								{
									if (ref($info_href->{$key}) eq 'HASH')
									{
										#????	
									}
									elsif (ref($info_href->{$key}) eq 'ARRAY')
									{
										#????	
									}
									elsif (ref($info_href->{$key}) eq 'SCALAR')
									{
										$trace .= "%key => ${$info_href->{$key}}" ;	
									}
									else
									{
										$trace .= "%key = $info_href->{$key}" ;	
									}
								}	
								$trace .= "\n" ;
							}
						}
					}

					# show grouped info
					foreach my $reckey (sort keys %grouped)
					{
						$trace .=  "     $reckey\n" ;
						foreach my $msg (@{$grouped{$reckey}})
						{
							$trace .=  "         # $msg\n" ;
						}
					}
				}	
			}	
		}
	}

	$trace .= "\n\n" ;

	return $trace ;
}


#---------------------------------------------------------------------------------------------------
# Display trace
sub display
{
	return unless $trace_flag ;
	
	my $trace = create_trace_report() ;
	print $trace ;
}


#---------------------------------------------------------------------------------------------------
# Display all recordings
sub format_rec
{
	my ($record_href) = @_ ;
	
	return sprintf "%s '%s' @ %s %s .. %s (pid %s)", 
		$record_href->{'channel'}, 
		$record_href->{'title'}, 
		$record_href->{'date'},
		$record_href->{'start'},
		$record_href->{'end'},
		$record_href->{'pid'} ;

}



#============================================================================================
# DEBUG
#============================================================================================
#


# ============================================================================================
# END OF PACKAGE
1;

__END__