# ----------------------------------------------------------------------
# | Module:     atom.pl                                                |
# |                                                                    |
# | Purpose:    Test wine Atom API calls                               |
# |                                                                    |
# |                                                                    |
# |                                                                    |
# ----------------------------------------------------------------------

use wine;
require winerror;

$wine::debug = 0;

################################################################
# Declarations for functions we use in this script

wine::declare( "kernel32",
               GlobalAddAtomA     => "word",
               GlobalAddAtomW     => "word",
               GlobalFindAtomA    => "word",
               GlobalFindAtomW    => "word",
               GlobalDeleteAtom   => "word",
               GlobalGetAtomNameA => "int",
               GlobalGetAtomNameW => "int",
);

# Locals
my  ($buf, $len);
my  $i;
    
my $name = "foobar";

# --------------------------------------------------------------
# | [GlobalAddAtom/GlobalFindAtom]                             |
# |                                                            |
# |                                                            |
# --------------------------------------------------------------

# --------------------------------------------------------------
# | Add an atom                                                |
# --------------------------------------------------------------
my $atom = GlobalAddAtomA( $name );
assert( ($atom >= 0xc000) && ($atom <= 0xffff) && !defined($wine::err) );

# --------------------------------------------------------------
# | Verify that it can be found (or not) appropriately         |
# --------------------------------------------------------------
assert( GlobalFindAtomA($name) == $atom );
assert( GlobalFindAtomA(uc $name) == $atom );
assert( !GlobalFindAtomA( "_" . $name ) );

# --------------------------------------------------------------
# | Add the same atom, specifying string as unicode; should    |
# | find the first one, not add a new one                      |
# --------------------------------------------------------------
my $w_atom = GlobalAddAtomW( wc($name) );
assert( $w_atom == $atom && !defined($wine::err) );

# --------------------------------------------------------------
# | Verify that it can be found (or not) appropriately via     |
# | unicode name                                               |
# --------------------------------------------------------------
assert( GlobalFindAtomW( wc($name)) == $atom );
assert( GlobalFindAtomW( wc(uc($name)) ) == $atom );
assert( !GlobalFindAtomW( wc("_" . $name) ) );

# --------------------------------------------------------------
# | Test integer atoms                                         |
# |                                                            |
# | 0x0000 should be invalid; (0x0001 .. 0xbfff) should be     |
# | valid; (0xc000 .. 0xffff) should be invalid                |
# --------------------------------------------------------------

# test limits
assert( !GlobalAddAtomA(0) && ($wine::err == $ERROR_INVALID_PARAMETER) );
assert( !GlobalAddAtomW(0) && ($wine::err == $ERROR_INVALID_PARAMETER) );
assert( GlobalAddAtomA(1) && !defined($wine::err) );
assert( GlobalAddAtomW(1) && !defined($wine::err) );
assert( GlobalAddAtomA(0xbfff) && !defined($wine::err) );
assert( GlobalAddAtomW(0xbfff) && !defined($wine::err) );
assert( !GlobalAddAtomA(0xc000) && ($wine::err == $ERROR_INVALID_PARAMETER) );
assert( !GlobalAddAtomW(0xc000) && ($wine::err == $ERROR_INVALID_PARAMETER) );
assert( !GlobalAddAtomA(0xffff) && ($wine::err == $ERROR_INVALID_PARAMETER) );
assert( !GlobalAddAtomW(0xffff) && ($wine::err == $ERROR_INVALID_PARAMETER) );

# test some in between
for ($i = 0x0001; ($i <= 0xbfff); $i += 13)
{
    assert( GlobalAddAtomA($i) && !defined($wine::err) );
    assert( GlobalAddAtomW($i) && !defined($wine::err) );
}
for ($i = 0xc000; ($i <= 0xffff); $i += 13)
{
    assert( !GlobalAddAtomA($i) && ($wine::err == $ERROR_INVALID_PARAMETER) );
    assert( !GlobalAddAtomW($i) && ($wine::err == $ERROR_INVALID_PARAMETER) );
}


# --------------------------------------------------------------
# | [GlobalGetAtomName]                                        |
# |                                                            |
# |                                                            |
# --------------------------------------------------------------

# --------------------------------------------------------------
# | Get the name of the atom we added above                    |
# |                                                            |
# | The second try specifies a buffer size that is too small   |
# | to hold the atom name; I believe the API is supposed to    |
# | copy as much of the name to the buffer as possible, and    |
# | return how much it copied.  But empirically, it seems to   |
# | return 0 instead ...                                       |
# --------------------------------------------------------------
my $buf = "." x 20;
my $len = GlobalGetAtomNameA( $atom, \$buf, length ($buf) );
assert( $len == length ($name) );
assert
(
 $buf eq
 ($name . "\x00" . ("." x (length ($buf) - length ($name) - 1)))
);
$buf = "." x 20;
assert( !GlobalGetAtomNameA( $atom, \$buf, 3 ) );

# --------------------------------------------------------------
# | Repeat, unicode-style                                      |
# --------------------------------------------------------------
$buf = "." x 20;
$len = GlobalGetAtomNameW( $atom, \$buf, length ($buf) / 2 );
assert( $len == wclen (wc ($name)) );
assert
(
 $buf eq (wc ($name) . "\x00\x00" . ("." x (20 - 2 * (wclen (wc ($name)) + 1) )))
);
$buf = "." x 20;
assert( !GlobalGetAtomNameW( $atom, \$buf, 3 ) );

# --------------------------------------------------------------
# | Check error code returns                                   |
# --------------------------------------------------------------
assert( !GlobalGetAtomNameA( $atom, $buf,  0 ) && $wine::err == $ERROR_MORE_DATA );
assert( !GlobalGetAtomNameA( $atom, $buf, -1 ) && $wine::err == $ERROR_MORE_DATA );
assert( !GlobalGetAtomNameW( $atom, $buf,  0 ) && $wine::err == $ERROR_MORE_DATA );
assert( !GlobalGetAtomNameW( $atom, $buf, -1 ) && $wine::err == $ERROR_MORE_DATA );

# --------------------------------------------------------------
# | Test integer atoms                                         |
# --------------------------------------------------------------
$buf = "a" x 10;
for ($i = 1; ($i <= 0xbfff); $i += 13)
{
    $len = GlobalGetAtomNameA( $i, \$buf, length ($buf) );
    assert( ($len > 1) && ($len < 7) );
    assert( $buf eq ("#$i\x00" . "a" x (10 - $len - 1) ));
}



# --------------------------------------------------------------
# | [GlobalDeleteAtom]                                         |
# |                                                            |
# |                                                            |
# --------------------------------------------------------------
$name = "barkbar";
$n = 5;

# First make sure it doesn't exist
$atom = GlobalAddAtomA( $name );
while (!GlobalDeleteAtom( $atom )) { }

# Now add it a number of times
for (1 .. $n) { $atom = GlobalAddAtomA( $name ); }

# Make sure it's here
assert( GlobalFindAtomA( $name ));
assert( GlobalFindAtomW( wc($name) ));

# That many deletions should succeed
for (1 .. $n) { assert( !GlobalDeleteAtom( $atom ) ); }

# It should be gone now
assert( !GlobalFindAtomA($name) );
assert( !GlobalFindAtomW( wc($name) ));

# So this one should fail
assert( GlobalDeleteAtom( $atom ) == $atom && $wine::err == $ERROR_INVALID_HANDLE );

# --------------------------------------------------------------
# | Test error handling                                        |
# |                                                            |
# |                                                            |
# --------------------------------------------------------------

# --------------------------------------------------------------
# | ASCII                                                      |
# --------------------------------------------------------------
assert( ($atom = GlobalAddAtomA("a" x 255)) );
assert( !GlobalDeleteAtom($atom) );
assert( !GlobalAddAtomA( "a" x 256 ) && $wine::err == $ERROR_INVALID_PARAMETER );
assert( !GlobalFindAtomA( "b" x 256 ) && $wine::err == $ERROR_INVALID_PARAMETER );

# --------------------------------------------------------------
# | Unicode                                                    |
# --------------------------------------------------------------
assert( ($atom = GlobalAddAtomW( wc("a" x 255) )) );
assert( !GlobalDeleteAtom($atom) );
assert( !GlobalAddAtomW( wc("a" x 256) ) && $wine::err == $ERROR_INVALID_PARAMETER );
assert( !GlobalFindAtomW( wc("b" x 256) ) && $wine::err == $ERROR_INVALID_PARAMETER );
