#!perl -w
use strict;
use Test::More tests => 94;

BEGIN { use_ok('Imager::File::ICO'); }

-d 'testout' or mkdir 'testout', 0777;

my $im = Imager->new;
# type=>'ico' or 'cur' and read ico and cur since they're pretty much
# the same
ok($im->read(file => "testimg/rgba3232.ico", type=>"ico"),
   "read 32 bit")
  or print "# ", $im->errstr, "\n";
is($im->getwidth, 32, "check width");
is($im->getwidth, 32, "check height");
is($im->type, 'direct', "check type");
is($im->tags(name => 'ico_bits'), 32, "check ico_bits tag");
is($im->tags(name => 'i_format'), 'ico', "check i_format tag");
my $mask = '.*
..........................******
..........................******
..........................******
..........................******
...........................*****
............................****
............................****
.............................***
.............................***
.............................***
.............................***
..............................**
..............................**
...............................*
...............................*
................................
................................
................................
................................
................................
................................
*...............................
**..............................
**..............................
***.............................
***.............................
****............................
****............................
*****...........................
*****...........................
*****...........................
*****...........................';
is($im->tags(name => 'ico_mask'), $mask, "check ico_mask_tag");

# compare the pixels
# ppm can't store 4 channels
SKIP:
{
  my $work = $im->convert(preset=>'noalpha');
  my $comp = Imager->new;
  $comp->read(file => "testimg/rgba3232.ppm")
    or skip "could not read 24-bit comparison file:". $comp->errstr, 1;
  is(Imager::i_img_diff($comp->{IMG}, $work->{IMG}), 0,
     "compare image data");
}

ok($im->read(file => 'testimg/pal83232.ico', type=>'ico'),
   "read 8 bit")
  or print "# ", $im->errstr, "\n";
is($im->getwidth, 32, "check width");
is($im->getwidth, 32, "check height");
is($im->type, 'paletted', "check type");
is($im->colorcount, 256, "color count");
is($im->tags(name => 'ico_bits'), 8, "check ico_bits tag");
is($im->tags(name => 'i_format'), 'ico', "check i_format tag");
SKIP:
{
  my $comp = Imager->new;
  $comp->read(file => "testimg/pal83232.ppm")
    or skip "could not read 8-bit comparison file:". $comp->errstr, 1;
  is(Imager::i_img_diff($comp->{IMG}, $im->{IMG}), 0,
     "compare image data");
}
$im->write(file=>'testout/pal83232.ppm');

ok($im->read(file => 'testimg/pal43232.ico', type=>'ico'),
   "read 4 bit")
  or print "# ", $im->errstr, "\n";
is($im->getwidth, 32, "check width");
is($im->getwidth, 32, "check height");
is($im->type, 'paletted', "check type");
is($im->colorcount, 16, "color count");
is($im->tags(name => 'ico_bits'), 4, "check ico_bits tag");
is($im->tags(name => 'i_format'), 'ico', "check i_format tag");
SKIP:
{
  my $comp = Imager->new;
  $comp->read(file => "testimg/pal43232.ppm")
    or skip "could not read 4-bit comparison file:". $comp->errstr, 1;
  is(Imager::i_img_diff($comp->{IMG}, $im->{IMG}), 0,
     "compare image data");
}

$im->write(file=>'testout/pal43232.ppm');
ok($im->read(file => 'testimg/pal13232.ico', type=>'ico'),
   "read 1 bit")
  or print "# ", $im->errstr, "\n";
is($im->getwidth, 32, "check width");
is($im->getwidth, 32, "check height");
is($im->type, 'paletted', "check type");
is($im->colorcount, 2, "color count");
is($im->tags(name => 'cur_bits'), 1, "check ico_bits tag");
is($im->tags(name => 'i_format'), 'cur', "check i_format tag");
$im->write(file=>'testout/pal13232.ppm');

# combo was created with the GIMP, which has a decent mechanism for selecting
# the output format
# you get different size icon images by making different size layers.
my @imgs = Imager->read_multi(file => 'testimg/combo.ico', type=>'ico');
is(scalar(@imgs), 3, "read multiple");
is($imgs[0]->getwidth, 48, "image 0 width");
is($imgs[0]->getheight, 48, "image 0 height");
is($imgs[1]->getwidth, 32, "image 1 width");
is($imgs[1]->getheight, 32, "image 1 height");
is($imgs[2]->getwidth, 16, "image 2 width");
is($imgs[2]->getheight, 16, "image 2 height");
is($imgs[0]->type, 'direct', "image 0 type");
is($imgs[1]->type, 'paletted', "image 1 type");
is($imgs[2]->type, 'paletted', "image 2 type");
is($imgs[1]->colorcount, 256, "image 1 colorcount");
is($imgs[2]->colorcount, 16, "image 2 colorcount");

is_deeply([ $imgs[0]->getpixel(x=>0, 'y'=>0)->rgba ], [ 231, 17, 67, 255 ],
	  "check image data 0(0,0)");
is_deeply([ $imgs[1]->getpixel(x=>0, 'y'=>0)->rgba ], [ 231, 17, 67, 255 ],
	  "check image data 1(0,0)");
is_deeply([ $imgs[2]->getpixel(x=>0, 'y'=>0)->rgba ], [ 231, 17, 67, 255 ],
	  "check image data 2(0,0)");

is_deeply([ $imgs[0]->getpixel(x=>47, 'y'=>0)->rgba ], [ 131, 231, 17, 255 ],
	  "check image data 0(47,0)");
is_deeply([ $imgs[1]->getpixel(x=>31, 'y'=>0)->rgba ], [ 131, 231, 17, 255 ],
	  "check image data 1(31,0)");
is_deeply([ $imgs[2]->getpixel(x=>15, 'y'=>0)->rgba ], [ 131, 231, 17, 255 ],
	  "check image data 2(15,0)");

is_deeply([ $imgs[0]->getpixel(x=>0, 'y'=>47)->rgba ], [ 17, 42, 231, 255 ],
	  "check image data 0(0,47)");
is_deeply([ $imgs[1]->getpixel(x=>0, 'y'=>31)->rgba ], [ 17, 42, 231, 255 ],
	  "check image data 1(0,31)");
is_deeply([ $imgs[2]->getpixel(x=>0, 'y'=>15)->rgba ], [ 17, 42, 231, 255 ],
	  "check image data 2(0,15)");

is_deeply([ $imgs[0]->getpixel(x=>47, 'y'=>47)->rgba ], [ 17, 231, 177, 255 ],
	  "check image data 0(47,47)");
is_deeply([ $imgs[1]->getpixel(x=>31, 'y'=>31)->rgba ], [ 17, 231, 177, 255 ],
	  "check image data 1(31,31)");
is_deeply([ $imgs[2]->getpixel(x=>15, 'y'=>15)->rgba ], [ 17, 231, 177, 255 ],
	  "check image data 2(15,15)");

$im = Imager->new(xsize=>32, ysize=>32);
$im->box(filled=>1, color=>'FF0000');
$im->box(filled=>1, color=>'0000FF', xmin => 6, ymin=>0, xmax => 21, ymax=>15);
$im->box(filled=>1, color=>'00FF00', xmin => 10, ymin=>16, xmax => 25, ymax=>31);

ok($im->write(file=>'testout/t10_32.ico', type=>'ico'),
   "write 32-bit icon");

my $im2 = Imager->new;
ok($im2->read(file=>'testout/t10_32.ico', type=>'ico'),
   "read it back in");

is(Imager::i_img_diff($im->{IMG}, $im2->{IMG}), 0,
   "check they're the same");
is($im->bits, $im2->bits, "check same bits");

{
  my $im = Imager->new(xsize => 32, ysize => 32);
  $im->box(filled=>1, color=>'#FF00FF');
  my $data;
  ok(Imager->write_multi({ data => \$data, type=>'ico' }, $im, $im),
     "write multi icons");
  ok(length $data, "and it wrote data");
  my @im = Imager->read_multi(data => $data);
  is(@im, 2, "got all the images back");
  is(Imager::i_img_diff($im->{IMG}, $im[0]{IMG}), 0, "check first image");
  is(Imager::i_img_diff($im->{IMG}, $im[1]{IMG}), 0, "check second image");
}

{ # 1 channel image
  my $im = Imager->new(xsize => 32, ysize => 32, channels => 1);
  $im->box(filled=>1, color => [ 128, 0, 0 ]);
  my $data;
  ok($im->write(data => \$data, type=>'ico'), "write 1 channel image");
  my $im2 = Imager->new;
  ok($im2->read(data => $data), "read it back");
  is($im2->getchannels, 4, "check channels");
  my $imrgb = $im->convert(preset => 'rgb')
    ->convert(preset => 'addalpha');
  is(Imager::i_img_diff($imrgb->{IMG}, $im2->{IMG}), 0,
     "check image matches expected");
}

{ # 2 channel image
  my $base = Imager->new(xsize => 32, ysize => 32, channels => 2);
  $base->box(filled => 1, color => [ 64, 192, 0 ]);
  my $data;
  ok($base->write(data => \$data, type=>'ico'), "write 2 channel image");
  my $read = Imager->new;
  ok($read->read(data => $data), "read it back");
  is($read->getchannels, 4, "check channels");
  my $imrgb = $base->convert(preset => 'rgb');
  is(Imager::i_img_diff($imrgb->{IMG}, $read->{IMG}), 0,
     "check image matches expected");
}

{ # 4 channel image
  my $base = Imager->new(xsize => 32, ysize => 32, channels => 4);
  $base->box(filled=>1, ymax => 15, color => [ 255, 0, 255, 128 ]);
  $base->box(filled=>1, ymin => 16, color => [ 0, 255, 255, 255 ]);
  my $data;
  ok($base->write(data => \$data, type=>'ico'), "write 4 channel image");
  my $read = Imager->new;
  ok($read->read(data => $data, type=>'ico'), "read it back")
    or print "# ", $read->errstr, "\n";
  is(Imager::i_img_diff($base->{IMG}, $read->{IMG}), 0,
     "check image matches expected");
}

{ # mask handling
  my $base = Imager->new(xsize => 16, ysize => 16, channels => 3);
  $base->box(filled=>1, xmin => 5, xmax => 10, color => '#0000FF');
  $base->box(filled=>1, ymin => 5, ymax => 10, color => '#0000FF');
  my $mask = <<EOS; # CR in this to test it's skipped correctly
01
0000011111100000
00000111111 00000xx
00000111111000  
00000111111000
0000011111100000
1111111111111111
1111111111111111
1111111111111111
1111111111111111
1111111111111111
1111111111111111
1010101010101010
1010101010101010
1010101010101010
1010101010101010
1010101010101010
EOS
  $mask =~ s/\n/\r\n/g; # to test alternate newline handling is correct
  $base->settag(name => 'ico_mask', value => $mask);
  my $saved_mask = $base->tags(name => 'ico_mask');
  my $data;
  ok($base->write(data => \$data, type => 'ico'),
     "write with mask tag set");
  my $read = Imager->new;
  ok($read->read(data => $data), "read it back");
  my $mask2 = $mask;
  $mask2 =~ tr/01/.*/;
  $mask2 =~ s/\n$//;
  $mask2 =~ tr/\r x//d;
  $mask2 =~ s/^(.{3,19})$/$1 . "." x (16 - length $1)/gem;
  my $read_mask = $read->tags(name => 'ico_mask');
  is($read_mask, $mask2, "check mask is correct");
}

{ # mask too short to handle
  my $mask = "xx";
  my $base = Imager->new(xsize => 16, ysize => 16, channels => 3);
  $base->box(filled=>1, xmin => 5, xmax => 10, color => '#0000FF');
  $base->box(filled=>1, ymin => 5, ymax => 10, color => '#0000FF');
  $base->settag(name => 'ico_mask', value => $mask);
  my $data;
  ok($base->write(data => \$data, type=>'ico'),
     "save icon with short mask tag");
  my $read = Imager->new;
  ok($read->read(data => $data), "read it back");
  my $read_mask = $read->tags(name => 'ico_mask');
  my $expected_mask = ".*" . ( "\n" . "." x 16 ) x 16;
  is($read_mask, $expected_mask, "check the mask");

  # mask that doesn't match what we expect
  $base->settag(name => 'ico_mask', value => 'abcd');
  ok($base->write(data => \$data, type => 'ico'), 
     "write with bad format mask tag");
  ok($read->read(data => $data), "read it back");
  $read_mask = $read->tags(name => 'ico_mask');
  is($read_mask, $expected_mask, "check the mask");

  # mask with invalid char
  $base->settag(name => 'ico_mask', value => ".*\n....xxx..");
  ok($base->write(data => \$data, type => 'ico'), 
     "write with unexpected chars in mask");
  ok($read->read(data => $data), "read it back");
  $read_mask = $read->tags(name => 'ico_mask');
  is($read_mask, $expected_mask, "check the mask");
}

{ # check handling of greyscale paletted
  my $base = Imager->new(xsize => 16, ysize => 16, channels => 1, 
                         type => 'paletted');
  my @grays = map Imager::Color->new($_),
    "000000", "666666", "CCCCCC", "FFFFFF";
  ok($base->addcolors(colors => \@grays), "add some colors");
  $base->box(filled => 1, color => $grays[1], xmax => 7, ymax => 7);
  $base->box(filled => 1, color => $grays[1], xmax => 7, ymin => 8);
  $base->box(filled => 1, color => $grays[1], xmin => 8, ymax => 7);
  $base->box(filled => 1, color => $grays[1], xmin => 8, ymax => 8);
  my $data;
  ok($base->write(data => \$data, type => 'ico'),
     "write grayscale paletted");
  my $read = Imager->new;
  ok($read->read(data => $data), "read it back")
    or print "# ", $read->errstr, "\n";
  is($read->type, 'paletted', "check type");
  is($read->getchannels, 3, "check channels");
  my $as_rgb = $base->convert(preset => 'rgb');
  is(Imager::i_img_diff($base->{IMG}, $read->{IMG}), 0,
     "check the image");
}
