#!perl -w  -- -*- tab-width: 4; mode: perl -*-

# t/00.load.t - check module loading

use strict;
use warnings;

{
## no critic ( ProhibitOneArgSelect RequireLocalizedPunctuationVars )
my $fh = select STDIN; $|++; select STDOUT; $|++; select STDERR; $|++; select $fh;	# DISABLE buffering (enable autoflush) on STDIN, STDOUT, and STDERR (keeps output in order)
}

#use Test::More tests => 4;
use Test::More;

use lib qw{ blib\arch };		# use build directory XS module { NOTE: must rebuild new .DLL before testing }

use_ok( $ENV{_BUILD_module_name} );

diag("Win32::CommandLine::_info_SIZEOF_HANDLE() = ".Win32::CommandLine::_info_SIZEOF_HANDLE());

diag("Win32::CommandLine::_const_MAX_PATH() = ".Win32::CommandLine::_const_MAX_PATH());

use constant TH32CS_SNAPPROCESS =>  0x00000002;
use constant INVALID_HANDLE_VALUE =>  -1;
use constant MAX_PATH =>  260;

is( Win32::CommandLine::_const_MAX_PATH(), MAX_PATH, "Verify MAX_PATH == 260");
is( Win32::CommandLine::_const_INVALID_HANDLE_VALUE(), INVALID_HANDLE_VALUE, "Verify INVALID_HANDLE_VALUE == -1");
is( Win32::CommandLine::_const_TH32CS_SNAPPROCESS(), TH32CS_SNAPPROCESS, "Verify TH32CS_SNAPPROCESS");

my @info_PROCESSENTRY32 = @{Win32::CommandLine::_info_PROCESSENTRY32()};

my $joined_info_PROCESSENTY32;
for my $element ( @info_PROCESSENTRY32 )
	{
	$joined_info_PROCESSENTY32 .= "[ @{$element} ]";
	}
diag("info_PROCESSENTRY32 = $joined_info_PROCESSENTY32");

my @info_PROCESSENTRY32_struct = @info_PROCESSENTRY32[ 1 .. @info_PROCESSENTRY32-1 ];
my $PROCESSENTRY32_template;
for my $element ( @info_PROCESSENTRY32_struct )
	{
	$PROCESSENTRY32_template .= ( $PROCESSENTRY32_template ? q{ } : q{} );
	$PROCESSENTRY32_template .= q{@}.join(q{}, @{$element}[ 2 .. @{$element}-1]);
	}
diag("PROCESSENTRY32_template = $PROCESSENTRY32_template");

# Take a snapshot of all processes in the system.

my $hProcessSnap = Win32::CommandLine::_wrap_CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
#die "CreateToolhelp32Snapshot: $!($^E)" if $hProcessSnap == INVALID_HANDLE_VALUE;

diag("hProcessSnap = $hProcessSnap");

ok( $hProcessSnap != INVALID_HANDLE_VALUE, "Snapshot handle is NOT INvalid" );

# URLrefs: Perl pack Tutorial - Integers [ http://perldoc.perl.org/perlpacktut.html#Integers ; http://www.webcitation.org/5xnyRJ6fv @2011-04-08.2059 ] , MSDN Common Data Types (Definitions) [ http://msdn.microsoft.com/en-us/library/aa505945.aspx ; http://www.webcitation.org/5xnyIZN5p @2011-04-08.2058 ] 

# DWORD == unsigned long => L
# ULONG_PTR == pointer (same size as long long) => Q 	## ?? same on 32-bit platforms? 

#my $pack_template = '@0L! @4L! @8L! @16P @24L! @28L! @32L! @36l! @40L! @44Z260';

my $dwSize = length pack $PROCESSENTRY32_template;  								## no critic ( ProhibitMagicNumbers ) # ToDO: revisit/remove

diag ("PROCESSENTRY32_template = $PROCESSENTRY32_template [length = $dwSize]");

is( Win32::CommandLine::_info_PROCESSENTRY32()->[0]->[1], $dwSize, "Verify PROCESSENTRY32 vs pack template [size]");

# URLref: http://www.perlmonks.org/?node_id=807366

#my $iBytes = length( pack 'I', $dwSize ); diag ("iBytes = $iBytes");
#my $lBytes = length( pack 'L!', $dwSize ); diag ("lBytes = $lBytes");
#my $jBytes = length( pack 'J', $dwSize ); diag ("jBytes = $jBytes");
#my $qBytes = eval { length( pack 'Q', $dwSize ) } || 0; diag ("qBytes = $qBytes");
#my $pBytes = length( pack 'P', $dwSize ); diag ("pBytes = $pBytes");

# see: IV definition (always large enough for pointers) @ http://perldoc.perl.org/perlguts.html#What-is-an-%22IV%22? [http://www.webcitation.org/5xo8LWer1 @2011-04-08.2331]
# see: Perl pack, definition of j and J templates as IV and UV, respectively @ http://perldoc.perl.org/functions/pack.html 
# see: Length of pack 'j/J' @ http://www.perlmonks.org/?node_id=869876 [http://www.webcitation.org/5xo8iKl17 @2011-04-08.2337]

my $ptrUnpack = 'J';
#if ($qBytes && ($pBytes > $lBytes)) { 
#	$ptrUnpack = 'Q'; 
#	## ?? warn if $qBytes < $pBytes
#	}

#my $dwSize = MAX_PATH + 36;  								## no critic ( ProhibitMagicNumbers ) # ToDO: revisit/remove
#my $pe32 = pack 'I9C260', $dwSize, 0 x 8, '0' x MAX_PATH;  	## no critic ( ProhibitMagicNumbers ) # ToDO: revisit/remove
my $pe32 = pack( $PROCESSENTRY32_template, $dwSize, (0) x 2, 0x0, (0) x 3, 0, 0, '0' x MAX_PATH );  	## no critic ( ProhibitMagicNumbers ) # ToDO: revisit/remove
my $lppe32 = unpack( $ptrUnpack, ( pack 'P', $pe32 ));		# URLref: http://perldoc.perl.org/perlpacktut.html#Pointers-for-How-to-Use-Them

# Retrieve information about the first process, and exit if unsuccessful
my %exes;
my %ppids;
my $ret = Win32::CommandLine::_wrap_Process32First( $hProcessSnap, $lppe32 );

do {
	if (not $ret) {
		Win32::CommandLine::_wrap_CloseHandle( $hProcessSnap );
		warn "Process32First: ret=$ret, $!($^E)";
		#last;
		exit;
	}

	# return ppid if pid == my pid
	
	my $th32ProcessID;
	my $th32ParentProcessID;
	my $szEXE;
	
	(undef, undef, $th32ProcessID, undef, undef, undef, $th32ParentProcessID, undef, undef, $szEXE) = unpack( $PROCESSENTRY32_template, $pe32 );

#	my $th32ProcessID = unpack 'I', substr $pe32, 8, 4;			## no critic ( ProhibitMagicNumbers ) # ToDO: revisit/remove
#	my $th32ParentProcessID = unpack 'I', substr $pe32, 24, 4;	## no critic ( ProhibitMagicNumbers ) # ToDO: revisit/remove
#	my $szEXE = q{};
#	my $i = 36;													## no critic ( ProhibitMagicNumbers ) # ToDO: revisit/remove
#	my $c = unpack 'C', substr $pe32, $i, 1;
#	while ($c) { $szEXE .= chr($c); $i++; $c = unpack 'C', substr $pe32, $i, 1; }
	$ppids{$th32ProcessID} = $th32ParentProcessID;
	$exes{$th32ProcessID} = $szEXE;
	
	##diag("$szEXE [id: $th32ProcessID; parent: $th32ParentProcessID]");
	
#	if ($$ == $th32ProcessID)
#		{
#		#print "thisEXE = $szEXE\n";
#		#print "parentPID = $th32ParentProcessID\n";
#		diag("thisEXE = $szEXE ; parentPID = $th32ParentProcessID");
#		return $th32ParentProcessID;
#		}
	#return unpack ('I', substr $pe32, 24, 4) if $$ == $th32ProcessID;

} while (Win32::CommandLine::_wrap_Process32Next( $hProcessSnap, $lppe32 ));

Win32::CommandLine::_wrap_CloseHandle( $hProcessSnap );

if ($ppids{$$}) {
	#print "ENV{CMDLINE} = $ENV{CMDLINE}\n";
	#print "thisEXE = $exes{$$}\n";
	#print "parentEXE = $exes{$ppids{$$}}\n";
	#return $ppids{$$};
	##$parentEXE = $exes{$ppids{$$}};
##	return $exes{$ppids{$$}};
##	diag("ENV{CMDLINE} = $ENV{CMDLINE}");
	diag("thisEXE = $exes{$$} [id: $$]");
	diag("parentEXE = $exes{$ppids{$$}} [id: $ppids{$$}]");
	#return $ppids{$$};
	##$parentEXE = $exes{$ppids{$$}};
	}

#### #### #### ####

done_testing();
