The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
use Test::More qw(no_plan);
BEGIN { use_ok('Geo::GDAL') };

use vars qw/%available_driver %test_driver $loaded $verbose @types @fails @tested_drivers/;

$loaded = 1;

$verbose = $ENV{VERBOSE};

# tests:
#
# for pre-tested GDAL drivers: 
#   Create dataset
#   Get/SetGeoTransform
#   Get/SetNoDataValue
#   Colortable operations
#   WriteRaster
#   Open dataset
#   ReadRaster
#   GCPs
#
# not yet tested:
#   Overviews
#
# if verbose = 1, all operations (skip,fail,ok) are printed out

system "rm -rf tmp_ds_*" unless $^O eq 'MSWin32';

%test_driver = ('GTiff' => 1,
		'MEM' => 1,
		'EHdr' => 1,
		);

@types = (qw/Byte UInt16 Int16 UInt32 Int32 Float32 Float64 CInt16 CInt32 CFloat32 CFloat64/);

my %no_colortable = map {$_=>1} ('NITF','ELAS','BMP','ILWIS','BT','RMF','RST');

my %no_nodatavalue = map {$_=>1} ('NITF','HFA','ELAS','BMP','ILWIS','BT','IDA','RMF');

my %no_geotransform = map {$_=>1} ('NITF','PAux','PNM','MFF','ENVI','BMP','EHdr');

my %no_setgcp = map {$_=>1} ('HFA','ELAS','MEM','BMP','PCIDSK','ILWIS','PNM','ENVI',
			     'NITF','EHdr','MFF','MFF2','BT','IDA','RMF','RST');

my %no_open = map {$_=>1} ('VRT','MEM','ILWIS','MFF2');

if (0) {
    {
	my $ds = Geo::GDAL::Dataset::Open('/data/Corine/lceugr100_00/lceugr100_00_pct.tif');
	$b = $ds->GetRasterBand(1);
    }
    $b->GetDefaultRAT();
    exit;
}

{
    # test memory files
    my $fp = Geo::GDAL::VSIFOpenL('/vsimem/x', 'w');
    my $c = Geo::GDAL::VSIFWriteL("hello world!\n", $fp);
    ok($c == 13, 'Wrote 13 characters to a memory file.');
    Geo::GDAL::VSIFCloseL($fp);
    $fp = Geo::GDAL::VSIFOpenL('/vsimem/x', 'r');
    my $b = Geo::GDAL::VSIFReadL(40, $fp);
    ok($b eq "hello world!\n", 'Read back what I Wrote to a memory file.');
    Geo::GDAL::VSIFCloseL($fp);
    Geo::GDAL::Unlink('/vsimem/x');
}

{
    my $driver = Geo::GDAL::GetDriver('MEM');
    my $dataset = $driver->Create('tmp', 10, 10, 3 , 'Int32', []);
    ok($dataset->isa('Geo::GDAL::Dataset'), 'Geo::GDAL::Dataset');
    ok($dataset->{RasterXSize} == 10, "Geo::GDAL::Dataset::RasterXSize $dataset->{RasterXSize}");
    ok($dataset->{RasterCount} == 3, "Geo::GDAL::Dataset::RasterCount $dataset->{RasterCount}");
    my $drv = $dataset->GetDriver;
    ok($drv->isa('Geo::GDAL::Driver'), 'Geo::GDAL::Dataset::GetDriver');
    my @size = $dataset->Size();
    ok(is_deeply([10,10], \@size), "Geo::GDAL::Dataset::Size @size");
    my $r = $dataset->GetRasterBand(1);
    my $g = $dataset->GetRasterBand(2);
    my $b = $dataset->GetRasterBand(3);

    $b->WriteTile([
    [1,2,3,4,5,6,7,8,9,10],
    [1,2,3,4,5,6,7,8,9,10],
    [1,2,3,4,5,6,7,8,9,10],
    [1,2,3,0,0,0,0,8,9,10],
    [1,2,3,0,0,0,0,8,9,10],
    [1,2,3,0,0,0,0,8,9,10],
    [1,2,3,0,0,0,0,8,9,10],
    [1,2,3,4,5,6,7,8,9,10],
    [1,2,3,4,5,6,7,8,9,10],
    [1,2,3,4,5,6,7,8,9,10]
    ]);
    $r->WriteTile([
    [1,2,3,4,5,6,7,8,9,10],
    [1,2,3,4,5,6,7,8,9,10],
    [1,2,3,4,5,6,7,8,9,10],
    [1,2,3,0,0,0,0,8,9,10],
    [1,2,3,0,0,0,0,8,9,10],
    [1,2,3,0,0,0,0,8,9,10],
    [1,2,3,0,0,0,0,8,9,10],
    [1,2,3,4,5,6,7,8,9,10],
    [1,2,3,4,5,6,7,8,9,10],
    [1,2,3,4,5,6,7,8,9,10]
    ]);
    $g->WriteTile($b->ReadTile);
    $b->FillNodata($r);
    #print STDERR "@$_\n" for (@{$b->ReadTile()});

    my $histogram;
    eval {
	@histogram = $b->GetHistogram();
    };
    ok($#histogram == 255, "Histogram: @histogram");
    eval {
	$b->SetDefaultHistogram(1,10,[0..255]);
    };
    my ($min, $max);
    eval {
	($min,$max,$histogram) = $b->GetDefaultHistogram();
    };
    ok(($#$histogram == 255), "Default Histogram $#histogram == 255");
    eval {
	@histogram = $b->GetHistogram(Min=>0, Max=>100, Buckets=>20);
    };
    ok($#histogram == 19, "Histogram with parameters: @histogram");

    my @o;
    for (0..5) {
	my $a = 0.1*$_;
	push @o, "$a Generating Histogram 1";
    }
    my @out;
    $callback = sub {
    	      push @out, "@_";	
    	      return $_[0] < 0.5 ? 1 : 0 };	
    eval {
	Geo::GDAL::ComputeMedianCutPCT($r,$g,$b,5,
				       Geo::GDAL::ColorTable->new,
				       $callback
				       );
    };
    ok(is_deeply(\@out, \@o), "callback without callback_data");
    @o = ();
    for (0..5) {
	my $a = 0.1*$_;
	push @o, "$a Generating Histogram 6";
    }
    @out = ();
    eval {
	Geo::GDAL::ComputeMedianCutPCT($r,$g,$b,5,
				       Geo::GDAL::ColorTable->new,
				       $callback,6);
    };
    ok(is_deeply(\@out, \@o), "callback with callback_data");

    # without callback only implicit test:
    Geo::GDAL::ComputeMedianCutPCT($r,$g,$b,5,Geo::GDAL::ColorTable->new);

    Geo::GDAL::RegenerateOverview($r, $b, 'GAUSS');
    
    my $band = $r;

    $colors = $band->ColorTable(Geo::GDAL::ColorTable->new);
    @table = $colors->ColorTable([10,20,30,40],[20,20,30,40]);
    for (@table) {
	@$_ = (1,2,3,4) if $_->[0] == 10;
    }
    @table2 = $colors->ColorTable(@table);
    ok($table[1]->[1] == 20, "colortable 1");
    ok($table2[0]->[2] == 3, "colortable 2");

    my @data;
    for my $yoff (0..9) {
	push @data, [$yoff..9+$yoff];
    }
    $band->WriteTile(\@data);
    for my $yoff (4..6) {
	for my $xoff (3..4) {
	    $data[$yoff][$xoff] = 0;
	}
    }
    my $data = $band->ReadTile(3,4,2,3);
    for my $y (@$data) {
	for (@$y) {
	    $_ = 0;
	}
    }
    $band->WriteTile($data,3,4);
    $data = $band->ReadTile();
    ok(is_deeply(\@data,$data), "write/read tile");
}

{
    my $r = Geo::GDAL::RasterAttributeTable->new;
    my @t = $r->FieldTypes;
    my @u = $r->FieldUsages;
    for my $u (@u) {
	for my $t (@t) {
	    $r->CreateColumn("$t $u", $t, $u);
	}
    }
    my $n = $r->GetColumnCount;
    my $n2 = @t * @u;
    ok($n == $n2, "create rat column");
    $r->SetRowCount(scalar(@t));
    my $i = 0;
    my $c = 0;
    for (@t) {
	if (/Integer/) {
	    my $v = $r->Value($i, $c, 12);
	    ok($v == 12, "rat int");
	} elsif (/Real/) {
	    my $v = $r->Value($i, $c, 1.23);
	    ok($v == 1.23, "rat int");
	} elsif (/String/) {
	    my $v = $r->Value($i, $c, "abc");
	    ok($v eq 'abc', "rat str");
	}
	$i++;
	$c++;
    }
}

gdal_tests();

$src = Geo::OSR::SpatialReference->new();
$src->ImportFromEPSG(2392);

$xml = $src->ExportToXML();
$a = Geo::GDAL::ParseXMLString($xml);
$xml = Geo::GDAL::SerializeXMLTree($a);
$b = Geo::GDAL::ParseXMLString($xml);
ok(is_deeply($a, $b), "xml parsing");

my @tmp = sort keys %available_driver;

print STDERR "\nGDAL version: ",Geo::GDAL::VersionInfo,"\n";
print STDERR "Unexpected failures:\n",@fails,"\n" if @fails;
print STDERR "Available drivers were ",join(', ',@tmp),"\n";
print STDERR "Drivers used in tests were: ",join(', ',@tested_drivers),"\n";

system "rm -rf tmp_ds_*" unless $^O eq 'MSWin32';

###########################################
#
# only subs below
#
###########################################

sub gdal_tests {

    for my $driver (Geo::GDAL::Drivers) {

	my $name = $driver->{ShortName};
	
	unless (defined $name) {
	    $name = 'unnamed';
	    my $i = 1;
	    while ($available_driver{$name}) {
		$name = 'unnamed '.$i;
		$i++;
	    }
	}

	$available_driver{$name} = 1;
	mytest('skipped: not tested',undef,$name,'test'),next unless $test_driver{$name};

        next if $name eq 'MFF2'; # does not work probably because of changes in hkvdataset.cpp
	
	my $metadata = $driver->GetMetadata();
	
	unless ($metadata->{DCAP_CREATE} and $metadata->{DCAP_CREATE} eq 'YES') {
	    mytest('skipped: no capability',undef,$name,'dataset create');
	    next;
	}
	
	my @create = split /\s+/,$metadata->{DMD_CREATIONDATATYPES};
	
	@create = (qw/Byte Float32 UInt16 Int16 CInt16 CInt32 CFloat32/)
	    if $driver->{ShortName} eq 'MFF2';
	
	unless (@create) {
	    mytest('skipped: no creation datatypes',undef,$name,'dataset create');
	    next;
	}
	
	if ($driver->{ShortName} eq 'PAux') {
	    mytest('skipped: does not work?',undef,$name,'dataset create');
	    next;
	}

	push @tested_drivers,$name;
	
	my $ext = $metadata->{DMD_EXTENSION} ? '.'.$metadata->{DMD_EXTENSION} : '';
	$ext = '' if $driver->{ShortName} eq 'ILWIS';
	
	for my $type (@create) {
	    
	    if (($driver->{ShortName} eq 'MFF2') and ($type eq 'CInt32')) {
		mytest('skipped: does not work?',undef,$name,$type,'dataset create');
		next;
	    }

	    my $filename = "tmp_ds_".$driver->{ShortName}."_$type$ext";
	    my $width = 100;
	    my $height = 50;
	    my $bands = 1;
	    my $options = undef;

	    my $dataset;

	    eval {
		$dataset = $driver->Create($filename, $width, $height, $bands , $type, []);
	    };

	    mytest($dataset,'no error message',$name,$type,'dataset create');
	    next unless $dataset;

	    mytest($dataset->{RasterXSize} == $width,'RasterXSize',$name,$type,'RasterXSize');
	    mytest($dataset->{RasterYSize} == $height,'RasterYSize',$name,$type,'RasterYSize');
	    
	    my $band = $dataset->GetRasterBand(1);
	    
	    if ($no_geotransform{$driver->{ShortName}}) 
	    {
		mytest('skipped',undef,$name,$type,'Get/SetGeoTransform');

	    } else
	    {
		my $transform = $dataset->GetGeoTransform();
		$transform->[5] = 12;
		$dataset->SetGeoTransform($transform);
		my $transform2 = $dataset->GetGeoTransform();
		mytest($transform->[5] == $transform2->[5],
		       "$transform->[5] != $transform2->[5]",$name,$type,'Get/SetGeoTransform');
	    }

	    if ($no_nodatavalue{$driver->{ShortName}}) 
	    {
		mytest('skipped',undef,$name,$type,'Get/SetNoDataValue');
		
	    } else
	    {
		if ($name ne 'GTiff') {
		    $band->ColorInterpretation('GreenBand');
		    my $value = $band->ColorInterpretation;
		    mytest($value eq 'GreenBand',"$value ne GreenBand",$name,$type,'ColorInterpretation');
		}

		$band->SetNoDataValue(5);
		my $value = $band->GetNoDataValue;
		mytest($value == 5,"$value != 5",$name,$type,'Get/SetNoDataValue');
	    }
	    
	    if ($no_colortable{$driver->{ShortName}} 
		or ($driver->{ShortName} eq 'GTiff' and ($type ne 'Byte' or $type ne 'UInt16'))
		)
	    {
		mytest('skipped',undef,$name,$type,'Colortable');
		
	    } else 
	    {
		#my $colortable = Geo::GDAL::ColorTable->create('RGB');
		my $colortable = Geo::GDAL::ColorTable->new($Geo::GDAL::Constc::GPI_Gray);
		my @rgba = (255,0,0,255);
		$colortable->SetColorEntry(0, \@rgba);
		$band->ColorTable($colortable);
		$colortable = $band->ColorTable;
		my @rgba2 = $colortable->GetColorEntry(0);
		
		mytest($rgba[0] == $rgba2[0] and
		       $rgba[1] == $rgba2[1] and
		       $rgba[2] == $rgba2[2] and
		       $rgba[3] == $rgba2[3],"colors do not match",$name,$type,'Colortable');
	    }
	    
	    my $pc;
	    eval {
		$pc = Geo::GDAL::PackCharacter($band->{DataType});
	    };
	    
	    if ($driver->{ShortName} eq 'VRT') 
	    {
		mytest('skipped',"",$name,$type,'WriteRaster');
		
	    } elsif (!$pc) 
	    {
		mytest('skipped',"no packtype defined yet",$name,$type,'WriteRaster');
		
	    } else 
	    {
		$pc = "${pc}[$width]";
		my $scanline = pack($pc,(1..$width));
		
		for my $yoff (0..$height-1) {
		    $band->WriteRaster( 0, $yoff, $width, 1, $scanline );
		}
	    }

	    
	    if ($no_setgcp{$driver->{ShortName}})
	    {
		mytest('skipped',undef,$name,$type,'Set/GetGCPs');
		
	    } else 
	    {
		my @gcps = ();
		push @gcps,new Geo::GDAL::GCP(1.1,2.2);
		push @gcps,new Geo::GDAL::GCP(2.1,3.2);
		my $po = "ho ho ho";
		$dataset->SetGCPs(\@gcps,$po);
		my $c = $dataset->GetGCPCount();
		my $p = $dataset->GetGCPProjection();
		my $gcps = $dataset->GetGCPs();
		my $y1 = $gcps->[0]->{GCPY};
		my $y2 = $gcps->[1]->{GCPY};
		my $y1o = $gcps[0]->{GCPY};
		my $y2o = $gcps[1]->{GCPY};
		mytest(($c == 2 and $p eq $po and $y1 == $y1o and $y2 == $y2o),
		       "$c != 2 or $p ne $po or $y1 != $y1o or $y2 != $y2o",$name,$type,'Set/GetGCPs');
	    }

	    undef $band;
	    undef $dataset;
	    
	    if ($no_open{$driver->{ShortName}} or 
		($driver->{ShortName} eq 'MFF2' and 
		 ($type eq 'Int32' or $type eq 'Float64' or $type eq 'CFloat64')))
	    {
		mytest('skipped',undef,$name,$type,'open');
		
	    } else 
	    {    
		$ext = $metadata->{DMD_EXTENSION} ? '.'.$metadata->{DMD_EXTENSION} : '';
		$filename = "tmp_ds_".$driver->{ShortName}."_$type$ext";
		
		eval {
		    $dataset = Geo::GDAL::Open($filename);
		};
		mytest($dataset,'no message',$name,$type,"open $filename");
		
		if ($dataset) {
		    mytest($dataset->{RasterXSize} == $width,'RasterXSize',$name,$type,'RasterXSize');
		    mytest($dataset->{RasterYSize} == $height,'RasterYSize',$name,$type,'RasterYSize');
		    
		    my $band = $dataset->GetRasterBand(1);

		    {
			my @a = ('abc','def');
			my @b = $band->CategoryNames(@a);
			ok(is_deeply(\@a, \@b,"$name,$type,CategoryNames"));
		    }
		    
		    if ($pc) {
			
			my $scanline = $band->ReadRaster( 0, 0, $width, 1);
			my @data = unpack($pc, $scanline);
			mytest($data[49] == 50,'',$name,$type,'ReadRaster');
			
		    }
		    
		}
		undef $dataset;
	    }    
	}
    }
}

sub cmp_ar {
    my($n,$a1,$a2) = @_;
    return 0 unless $n == @$a1;
    return 0 unless $#$a1 == $#$a2;
    for my $i (0..$#$a1) {
	return 0 unless abs($a1->[$i] - $a2->[$i]) < 0.001;
    }
    return 1;
}

sub mytest {
    my $test = shift;
    my $msg = shift;
    my $context = join(': ',@_);
    ok($test, $context);
    unless ($test) {
	my $err = $msg;
	if ($@) {
	    $@ =~ s/\n/ /g;
	    $@ =~ s/\s+$//;
	    $@ =~ s/\s+/ /g;
	    $@ =~ s/^\s+$//;
	    $err = $@ ? "'$@'" : $msg;
	}
	$msg = "$context: $err: not ok\n";
	push @fails,$msg;
    } elsif ($test =~ /^skip/) {
	$msg = "$context: $test.\n";
    } else {
	$msg = "$context: ok.\n";
    }
    print $msg if $verbose;
    return $msg;
}

sub dumphash {
    my $h = shift;
    for (keys %$h) {
	print "$_ $h->{$_}\n";
    }
}