########################################################################
#
# Test of Win32::OLE::Variant
#
########################################################################
# If you rearrange the tests, please renumber:
# perl -i.bak -pe "++$t if !$t || s/^# \d+\./# $t./" 2_variant.t
########################################################################

use strict;
use FileHandle;
use Win32::OLE::NLS qw(:DEFAULT :LANG :SUBLANG :DATE :TIME);
use Win32::OLE::Variant qw(:DEFAULT CP_ACP nothing nullstring);

$^W = 1;
STDOUT->autoflush(1);
STDERR->autoflush(1);

open(ME,$0) or die $!;
my $TestCount = grep(/\+\+\$Test/,<ME>);
close(ME);

my $Test = 0;
print "1..$TestCount\n";

my $lcidEnglish = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_NEUTRAL));
my $lcidGerman = MAKELCID(MAKELANGID(LANG_GERMAN, SUBLANG_NEUTRAL));

Win32::OLE->Option(CP => CP_ACP, LCID => $lcidEnglish);
printf "# LCID is %d\n", Win32::OLE->Option('LCID');
printf "# CP is %d\n", Win32::OLE->Option('CP');

# 1. Create a simple numeric variant
my $v = Variant(VT_R8, 3.1415);
print "not " unless UNIVERSAL::isa($v, 'Win32::OLE::Variant');
printf "ok %d\n", ++$Test;

# 2. Verify type and value of variant
printf "# Type is %d and Value is %f\n", $v->Type, $v->Value;
print "not " unless $v->Type == VT_R8 && $v->Value == 3.1415;
printf "ok %d\n", ++$Test;

# 3. Retrieve value as VT_BSTR value
printf "# As(VT_BSTR) is \"%s\"\n", $v->As(VT_BSTR);
print "not " unless $v->As(VT_BSTR) eq "3.1415";
printf "ok %d\n", ++$Test;

# 4. Change locale to "German" (uses ',' as decimal point)
Win32::OLE->Option(LCID => $lcidGerman);
printf "# As(VT_BSTR) in lcid=$lcidGerman is \"%s\"\n", $v->As(VT_BSTR);
print "not " unless $v->Value == 3.1415 && $v->As(VT_BSTR) eq "3,1415";
printf "ok %d\n", ++$Test;

# 5. Backward compatibility: direct access to class variables
printf "# Win32::OLE::LCID=%d\n", $Win32::OLE::LCID;
print "not " unless $Win32::OLE::LCID == Win32::OLE->Option('LCID');
printf "ok %d\n", ++$Test;

# 6. Test overloaded conversion to string
printf "# String value is \"$v\"\n";
print "not " unless "$v" eq "3,1415";
printf "ok %d\n", ++$Test;

# 7. Test overloaded conversion to number
printf "# Numeric (0) value is %f\n", $v-3.1415;
print "not " unless abs($v-3.1415) < 0.00001;
printf "ok %d\n", ++$Test;

# 8. Change locale to "English" and convert VARIANT to VT_BSTR
Win32::OLE->Option(LCID => $lcidEnglish);
$v->ChangeType(VT_BSTR);
printf "# VT_BSTR Value in lcid=$lcidEnglish is \"%s\"\n", $v->As(VT_BSTR);
print "not " unless $v->Type == VT_BSTR && "$v" eq "3.1415";
printf "ok %d\n", ++$Test;

# 9. Try an invalid conversion and test LastError() method
Win32::OLE->Option(Warn => 0);
Win32::OLE->LastError(0);
my $Before = Win32::OLE->LastError;
$v = Variant(VT_BSTR, "Five");
$v->ChangeType(VT_I4);
printf "# Before: $Before After: %x\n", Win32::OLE->LastError;
print "not " unless $Before == 0 && Win32::OLE->LastError != 0;
printf "ok %d\n", ++$Test;
Win32::OLE->Option(Warn => 1);

# 10. Backward compatibility: does Win32::OLE::Variant->LastError() still work?
printf "# Win32::OLE::Variant->LastError: %x\n", Win32::OLE::Variant->LastError;
print "not " unless Win32::OLE->LastError == Win32::OLE::Variant->LastError;
printf "ok %d\n", ++$Test;

# 11. Special case: VT_UI1 with string argument implies VT_ARRAY
$v = Variant(VT_UI1, "Some string");
printf "# Type=%x String=\"%s\"\n", $v->Type, $v->Value;
print "not " unless $v->Type == (VT_UI1|VT_ARRAY) && $v->Value eq "Some string";
printf "ok %d\n", ++$Test;

# 12. A numeric initializer should create a normal VT_UI1 variant
$v = Variant(VT_UI1, ord('A'));
printf "# Type=%x Value='%c'\n", $v->Type, $v->Value;
print "not " unless $v->Type == VT_UI1 && $v->Value == ord('A');
printf "ok %d\n", ++$Test;

# 13. Test assignment to specific type: float to I2
$v = Variant(VT_I2, 42);
printf "# Value (42) is %g\n", $v->Value;
$v->Put(3.1415);
printf "# Value (3.1415) is %g\n", $v->Value;
print "not " unless $v->Value == 3;
printf "ok %d\n", ++$Test;

# 14. Test assignment to specific type: large integer to I2
$v->Put(70_000);
printf "# Value (70_000) is %g\n", $v->Value;
print "not " unless $v->Value == 70_000-2**16;
printf "ok %d\n", ++$Test;

# 15. Test VT_BYREF using an alias pointing to the same VARIANT
my $t = Variant(VT_I4|VT_BYREF, 42);
$v = $t->Value;
printf "# Ref=%s Value=%s\n", ref($v), $v;
$v = $t->_Clone; # NB: Undocumented and unsupported function for testing only!
printf "# Ref=%s Value=%s\n", ref($v), $v;
$t->Put(13);
printf "# Ref=%s Value=%s\n", ref($v), $v;
print "not " unless $v->Value == 13;
printf "ok %d\n", ++$Test;
undef $v;
undef $t;

# 16. Copy() method should make a *real* copy
$t = Variant(VT_BYREF|VT_ARRAY|VT_I4, 2);
$t->Put(0,2);
$t->Put(1,3);
$v = $t->Copy;
$t->Put(1,4);
printf "# v(%x)=%d t(%x)=%d\n", $v->Type, $v->Get(1), $t->Type, $t->Get(1);
print "not " unless $v->Type == (VT_ARRAY | VT_I4) &&
                    $v->Get(1) == 3 && $t->Get(1) == 4;
printf "ok %d\n", ++$Test;

# 17. Test various VT_UI1 manipulations
$v = Variant(VT_ARRAY|VT_UI1|VT_BYREF, 8);
$v->Put("1234567890");
$v->Put(1,'');
$v->Put(3,'ABC');
$v->Put(6,32);
printf "# String=\"%s\"\n", $v->Value;
print "not " unless $v->Value eq "1\0003A56 8";
printf "ok %d\n", ++$Test;

# 18. Assignment by string should be '\0' padded
$v->Put("ABCD");
printf "# String=\"%s\"\n", $v->Value;
print "not " unless $v->Value eq "ABCD"."\0" x 4;
printf "ok %d\n", ++$Test;

# 19. Test non-0 lower bound and Get() method
$v = Variant(VT_ARRAY|VT_UI1, [10,13]);
$v->Put("123");
printf "# String=\"%s\", Get(11)=%d\n", $v->Get, $v->Get(11);
print "not " unless $v->Get eq "123\0" && $v->Get(11) == ord('2');
printf "ok %d\n", ++$Test;

# 20. Test multidimensional array
$v = Variant(VT_ARRAY|VT_BYREF|VT_VARIANT, 3, [1,2]);
my @dim = $v->Dim;
printf "# Dim: %s\n", join(', ', map {'['.join(',', @$_).']'} @dim);
print "not " unless $dim[0][0] == 0 && $dim[0][1] == 2 &&
                    $dim[1][0] == 1 && $dim[1][1] == 2;
printf "ok %d\n", ++$Test;

# 21. Assignment to VT_VARIANT array
$v->Put(0, 1, "Perl");
$v->Put(1, 2, 3.1415);
printf "# String=\"%s\" Number=%s\n", $v->Get(0,1), $v->Get(1,2);
print "not " unless $v->Get(0,1) eq 'Perl' && $v->Get(1,2) == 3.1415;
printf "ok %d\n", ++$Test;

# 22. Get() applied to VT_VARIANT array should return a value, *not* an object
printf "# ref=\"%s\"\n", ref($v->Get(0,1));
print "not " if ref($v->Get(0,1));
printf "ok %d\n", ++$Test;

# 23. Copy() can be used to retrieve an element as a Variant object
$t = $v->Copy(0,1);
printf "# Type=%x Value=\"%s\"\n", $t->Type, $t->Value;
print "not " unless $t->Type == VT_BSTR and $t->Value eq 'Perl';
printf "ok %d\n", ++$Test;

# 24. Put() returns reference to $self
$v->Put(0,1,'One')->Put(1,1,2);
printf "# One=\"%s\" Two=%s\n", $v->Get(0,1), $v->Get(1,1);
print "not " unless $v->Get(0,1) eq 'One' && $v->Get(1,1) == 2;
printf "ok %d\n", ++$Test;

# 25. Put(ARRAYREF) sets SAFEARRAY
#$v = Variant(VT_ARRAY|VT_I4, 2, 2)->Put([[11, 12], [21, 22]]);
$v = Variant(VT_ARRAY|VT_I4, 2, 2)->Put([[11, 12], [21, 22]]);
printf "# Dim: %s\n", join(', ', map {'['.join(',', @$_).']'} $v->Dim);
printf "# (0,0)=%d (0,1)=%d (1,0)=%d (1,1)=%d\n", $v->Get(0,0), $v->Get(0,1),
                                                  $v->Get(1,0), $v->Get(1,1);
print "not " unless $v->Get(0,0) == 11 && $v->Get(1,1) == 22;
printf "ok %d\n", ++$Test;

# 26. Float -> CURRENCY conversion in non-english locale
Win32::OLE->Option(LCID => $lcidGerman);
my $cy = Variant(VT_CY, 1.2345);
printf "# VT_CY String is '%s' Number is '%f'\n", $cy, $cy;
print "not " unless $cy == 1.2345;
printf "ok %d\n", ++$Test;

# 27. GetDateFormat with formating options
Win32::OLE->Option(LCID => $lcidEnglish);
$v = Variant(VT_DATE, "1 may 1999 17:00");
my $str = $v->Date(DATE_LONGDATE);
print "# LONGDATE is '$str'\n";
print "not " unless $str =~ /^Saturday, May 0?1, 1999$/;
printf "ok %d\n", ++$Test;

# 28. GetDateFormat with formating string
$str = $v->Date('dd-MMM-yyyy');
print "# dd-MMM-yyyy is '$str'\n";
print "not " unless $str eq '01-May-1999';
printf "ok %d\n", ++$Test;

# 29. GetDateFormat with locale id
$str = $v->Date(DATE_LONGDATE, $lcidGerman);
print "# German LONGDATE is '$str'\n";
print "not " unless $str eq 'Samstag, 1. Mai 1999';
printf "ok %d\n", ++$Test;

# 30. Currency variant with maximum negative value
my $val = "-922337203685477.5808";
$v = Variant(VT_CY, $val);
print "# Big currency value as BSTR: $v\n";
print "not " unless $v eq $val;
printf "ok %d\n", ++$Test;

# 31. R8 doesn't have enough precission to accurately hold the CY value
printf "# Big currency value as R8: %.4f\n", $v;
print "not " if $v->As(VT_R8) eq $val;
printf "ok %d\n", ++$Test;

# 32. Format as currency with 4 decimal places
$str = $v->Currency({NumDigits      => 4,
		     Grouping       => 3,
		     NegativeOrder  => 0,
		     DecimalSep     => '.',
		     ThousandSep    => ',',
		     CurrencySymbol => '$',
		    });
printf "# Big currency value as CY: $str\n";
print "not " unless $str eq '($922,337,203,685,477.5808)';
printf "ok %d\n", ++$Test;

# 33. Use both a CURRENCYFMT hash *and* a locale id
$str = $v->Currency({CurrencySymbol => "Tuits"}, $lcidGerman);
printf "# Big currency value as tuits: $str\n";
print "not " unless $str eq '-922.337.203.685.477,58 Tuits';
printf "ok %d\n", ++$Test;

# 34. Test VARIANT->Put(ARRAYREF)
$v = Variant(VT_ARRAY|VT_I4, 2, 2);
$v->Put([[1,2],[3,4]]);
$v = Variant(VT_BYREF|VT_VARIANT, $v);
printf "# v(0,0)=%d v(1,1)=%d\n", $v->Get(0,0), $v->Get(1,1);
print "not " unless $v->Get(0,0) == 1 && $v->Get(1,1) == 4;
printf "ok %d\n", ++$Test;

# 35. Test SAFEARRAY of BSTRs
$v = Variant(VT_ARRAY|VT_BSTR, 2);
$v->Put(0,'Hello')->Put(1,'World');
printf "# v(0)=%s\n", $v->Get(0);
print "not " unless $v->Get(0) eq 'Hello';
printf "ok %d\n", ++$Test;

# 36. Test NULL BSTR value (vbNullString)
$v = nullstring();
printf "# Type=%s NullString=%s\n", $v->Type, $v->IsNullString ? "yes" : "no";
print "not " unless $v->Type == VT_BSTR && $v->Value eq "" && $v->IsNullString;
printf "ok %d\n", ++$Test;

# 37. Test "" BSTR value
$v = Variant(VT_BSTR, "");
printf "# Type=%s NullString=%s\n", $v->Type, $v->IsNullString ? "yes" : "no";
print "not " unless $v->Type == VT_BSTR && $v->Value eq "" && !$v->IsNullString;
printf "ok %d\n", ++$Test;

# 38. Test NULL DISPATCH value
$v = nothing();
printf "# Type=%s Nothing=%s\n", $v->Type, $v->IsNothing ? "yes" : "no";
print "not " unless $v->Type == VT_DISPATCH && $v->IsNothing;
printf "ok %d\n", ++$Test;

# 39. Test SAFEARRAY f VARIANTs
#$v = Variant(VT_ARRAY|VT_VARIANT, 2);
#$v->Put(0,Variant(VT_CY, 4.23))->Put(1,Variant(VT_I2, 42));
# TODO: Get() doesn't return Variant objects here
#printf "# vt(0)=%d v(1)==%d\n", $v->Get(0)->Type, $v->Get(1)->Type;
#print "not " unless $v->Get(0) eq 'Hello';
