/******************************************************************************
 *
 * Project:  GDAL SWIG Interface declarations for Perl.
 * Purpose:  GDAL declarations.
 * Author:   Ari Jolma and Kevin Ruland
 *
 ******************************************************************************
 * Copyright (c) 2007, Ari Jolma and Kevin Ruland
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *****************************************************************************/

%init %{
  /* gdal_perl.i %init code */
  UseExceptions();
  if ( GDALGetDriverCount() == 0 ) {
    GDALAllRegister();
  }
%}

%inline %{
    typedef struct
    {
	SV *fct;
	SV *data;
    } SavedEnv;
    int callback_d_cp_vp(double d, const char *cp, void *vp)
    {
	int count, ret;
	SavedEnv *env_ptr = (SavedEnv *)vp;
	dSP;
	ENTER;
	SAVETMPS;
	PUSHMARK(SP);
	XPUSHs(sv_2mortal(newSVnv(d)));
	XPUSHs(sv_2mortal(newSVpv(cp, 0)));
	if (env_ptr->data)
	    XPUSHs(env_ptr->data);
	PUTBACK;
	count = call_sv(env_ptr->fct, G_SCALAR);
	SPAGAIN;
	if (count != 1) {
	    fprintf(stderr, "The callback must return only one value.\n");
	    return 0; /* interrupt */
	}
	ret = POPi;
	PUTBACK;
	FREETMPS;
	LEAVE;
	return ret;
    }
%}

%include cpl_exceptions.i

%rename (GetMetadata) GetMetadata_Dict;
%ignore GetMetadata_List;

%import typemaps_perl.i

%import destroy.i

/*ALTERED_DESTROY(GDALRasterBandShadow, GDALc, delete_Band) !does not work! */
ALTERED_DESTROY(GDALColorTableShadow, GDALc, delete_ColorTable)
ALTERED_DESTROY(GDALConstShadow, GDALc, delete_Const)
ALTERED_DESTROY(GDALDatasetShadow, GDALc, delete_Dataset)
ALTERED_DESTROY(GDALDriverShadow, GDALc, delete_Driver)
ALTERED_DESTROY(GDAL_GCP, GDALc, delete_GCP)
ALTERED_DESTROY(GDALMajorObjectShadow, GDALc, delete_MajorObject)
ALTERED_DESTROY(GDALRasterAttributeTableShadow, GDALc, delete_RasterAttributeTable)

%rename (_GetDataTypeSize) GetDataTypeSize;
%rename (_DataTypeIsComplex) DataTypeIsComplex;

%rename (_GetDriver) GetDriver;

%rename (_Open) Open;
%newobject _Open;

%rename (_OpenShared) OpenShared;
%newobject _OpenShared;

/* those that need callback_data check: */

%rename (_ComputeMedianCutPCT) ComputeMedianCutPCT;
%rename (_DitherRGB2PCT) DitherRGB2PCT;
%rename (_ReprojectImage) ReprojectImage;
%rename (_ComputeProximity) ComputeProximity;
%rename (_RasterizeLayer) RasterizeLayer;
%rename (_Polygonize) Polygonize;
%rename (_SieveFilter) SieveFilter;
%rename (_RegenerateOverviews) RegenerateOverviews;
%rename (_RegenerateOverview) RegenerateOverview;

%rename (_AutoCreateWarpedVRT) AutoCreateWarpedVRT;
%newobject _AutoCreateWarpedVRT;

%rename (_Create) Create;
%newobject _Create;

%rename (_GetRasterBand) GetRasterBand;
%rename (_AddBand) AddBand;

%rename (_GetPaletteInterpretation) GetPaletteInterpretation;
%rename (_GetHistogram) GetHistogram;

%rename (_SetColorEntry) SetColorEntry;

%rename (_GetUsageOfCol) GetUsageOfCol;
%rename (_GetColOfUsage) GetColOfUsage;
%rename (_GetTypeOfCol) GetTypeOfCol;
%rename (_CreateColumn) CreateColumn;

%rename (Stat) VSIStatL;

%perlcode %{
    use strict;
    use Carp;
    use Encode;
    use Geo::GDAL::Const;
    use Geo::OGR;
    use Geo::OSR;
    our $VERSION = '0.23';
    our $GDAL_VERSION = '1.8.1';
    use vars qw/
	%TYPE_STRING2INT %TYPE_INT2STRING
	%ACCESS_STRING2INT %ACCESS_INT2STRING
	%RESAMPLING_STRING2INT %RESAMPLING_INT2STRING
	%NODE_TYPE_STRING2INT %NODE_TYPE_INT2STRING
	/;
    for my $string (qw/Unknown Byte UInt16 Int16 UInt32 Int32 Float32 Float64 CInt16 CInt32 CFloat32 CFloat64/) {
	my $int = eval "\$Geo::GDAL::Constc::GDT_$string";
	$TYPE_STRING2INT{$string} = $int;
	$TYPE_INT2STRING{$int} = $string;
    }
    for my $string (qw/ReadOnly Update/) {
	my $int = eval "\$Geo::GDAL::Constc::GA_$string";
	$ACCESS_STRING2INT{$string} = $int;
	$ACCESS_INT2STRING{$int} = $string;
    }
    for my $string (qw/NearestNeighbour Bilinear Cubic CubicSpline/) {
	my $int = eval "\$Geo::GDAL::Constc::GRA_$string";
	$RESAMPLING_STRING2INT{$string} = $int;
	$RESAMPLING_INT2STRING{$int} = $string;
    }
    {
	my $int = 0;
	for my $string (qw/Element Text Attribute Comment Literal/) {
	    $NODE_TYPE_STRING2INT{$string} = $int;
	    $NODE_TYPE_INT2STRING{$int} = $string;
	    $int++;
	}
    }
    sub RELEASE_PARENTS {
    }
    sub NodeType {
	my $type = shift;
	return $NODE_TYPE_INT2STRING{$type} if $type =~ /^\d/;
	return $NODE_TYPE_STRING2INT{$type};
    }
    sub NodeData {
	my $node = shift;
	return (Geo::GDAL::NodeType($node->[0]), $node->[1]);
    }
    sub Children {
	my $node = shift;
	return @$node[2..$#$node];
    }
    sub Child {
	my($node, $child) = @_;
	return $node->[2+$child];
    }
    sub GetDataTypeSize {
	my $t = shift;
	$t = $TYPE_INT2STRING{$t} if exists $TYPE_INT2STRING{$t};
	return _GetDataTypeSize($t);
    }
    sub DataTypeValueRange {
    	my $t = shift;
	# these values are from gdalrasterband.cpp
	return (0,255) if $t =~ /^Byte$/;
	return (0,65535) if $t =~ /^UInt16$/;
	return (-32768,32767) if $t =~ /Int16$/; # also CInt
	return (0,4294967295) if $t =~ /^UInt32$/;
	return (-2147483648,2147483647) if $t =~ /Int32$/; # also CInt
	return (-4294967295.0,4294967295.0) if $t =~ /Float32$/; # also CFloat
	return (-4294967295.0,4294967295.0) if $t =~ /Float64$/; # also CFloat
	croak "unsupported data type: $t";
    }
    sub DataTypeIsComplex {
	my $t = shift;
	$t = $TYPE_INT2STRING{$t} if exists $TYPE_INT2STRING{$t};
	return _DataTypeIsComplex($t);
    }
    sub PackCharacter {
	my $t = shift;
	$t = $TYPE_INT2STRING{$t} if exists $TYPE_INT2STRING{$t};
	my $is_big_endian = unpack("h*", pack("s", 1)) =~ /01/; # from Programming Perl
	return 'C' if $t =~ /^Byte$/;
	return ($is_big_endian ? 'n': 'v') if $t =~ /^UInt16$/;
	return 's' if $t =~ /^Int16$/;
	return ($is_big_endian ? 'N' : 'V') if $t =~ /^UInt32$/;
	return 'l' if $t =~ /^Int32$/;
	return 'f' if $t =~ /^Float32$/;
	return 'd' if $t =~ /^Float64$/;
	croak "unsupported data type: $t";
    }
    sub Drivers {
	my @drivers;
	for my $i (0..GetDriverCount()-1) {
	    push @drivers, _GetDriver($i);
	}
	return @drivers;
    }
    sub GetDriver {
	my $driver = shift;
	return _GetDriver($driver) if $driver =~ /^\d/;
	return GetDriverByName($driver);
    }
    *Driver = *GetDriver;
    sub Open {
	my @p = @_;
	$p[1] = $ACCESS_STRING2INT{$p[1]} if $p[1] and exists $ACCESS_STRING2INT{$p[1]};
	return _Open(@p);
    }
    sub OpenShared {
	my @p = @_;
	$p[1] = $ACCESS_STRING2INT{$p[1]} if $p[1] and exists $ACCESS_STRING2INT{$p[1]};
	return _OpenShared(@p);
    }
    sub ComputeMedianCutPCT {
    	$_[6] = 1 if $_[5] and not defined $_[6];
	_ComputeMedianCutPCT(@_);
    }
    sub DitherRGB2PCT {
    	$_[6] = 1 if $_[5] and not defined $_[6];
	_DitherRGB2PCT(@_);
    }
    sub ComputeProximity {
    	$_[4] = 1 if $_[3] and not defined $_[4];
	_ComputeProximity(@_);
    }
    sub RasterizeLayer {
    	$_[8] = 1 if $_[7] and not defined $_[8];
	_RasterizeLayer(@_);
    }
    sub Polygonize {
        my @params = @_;
        $params[6] = 1 if $params[5] and not defined $params[6];
        $params[3] = $params[2]->GetLayerDefn->GetFieldIndex($params[3]) unless $params[3] =~ /^\d/;
	_Polygonize(@params);
    }
    sub SieveFilter {
    	$_[7] = 1 if $_[6] and not defined $_[7];
	_SieveFilter(@_);
    }
    sub RegenerateOverviews {
    	$_[4] = 1 if $_[3] and not defined $_[4];
	_RegenerateOverviews(@_);
    }
    sub RegenerateOverview {
    	$_[4] = 1 if $_[3] and not defined $_[4];
	_RegenerateOverview(@_);
    }
    sub ReprojectImage {
    	$_[8] = 1 if $_[7] and not defined $_[8];
	$_[4] = $RESAMPLING_STRING2INT{$_[4]} if $_[4] and exists $RESAMPLING_STRING2INT{$_[4]};
	return _ReprojectImage(@_);
    }
    sub AutoCreateWarpedVRT {
	$_[3] = $RESAMPLING_STRING2INT{$_[3]} if $_[3] and exists $RESAMPLING_STRING2INT{$_[3]};
	return _AutoCreateWarpedVRT(@_);
    }
    sub FindFile {
	my $a = _FindFile(@_);
	$a = decode('utf8', $a); # GDAL returns utf8
	return $a;
    }
    sub ReadDir {
	return unless defined wantarray;
	my $a = _ReadDir(@_);
	for (@$a) {
	    $_ = decode('utf8', $_); # GDAL returns utf8
	}
	return wantarray ? @$a : $a;
    }

    package Geo::GDAL::MajorObject;
    use vars qw/@DOMAINS/;
    use strict;
    sub Domains {
	return @DOMAINS;
    }
    sub Description {
	my($self, $desc) = @_;
	SetDescription($self, $desc) if defined $desc;
	GetDescription($self) if defined wantarray;
    }
    sub Metadata {
	my $self = shift;
	my $metadata = shift if ref $_[0];
	my $domain = shift;
	$domain = '' unless defined $domain;
	SetMetadata($self, $metadata, $domain) if defined $metadata;
	GetMetadata($self, $domain) if defined wantarray;
    }

    package Geo::GDAL::Driver;
    use vars qw/@CAPABILITIES @DOMAINS/;
    use strict;
    @CAPABILITIES = ('Create', 'CreateCopy');
    sub Domains {
	return @DOMAINS;
    }
    sub Name {
	my $self = shift;
	return $self->{ShortName};
    }
    sub Capabilities {
	my $self = shift;
	return @CAPABILITIES unless $self;
	my $h = $self->GetMetadata;
	my @cap;
	for my $cap (@CAPABILITIES) {
	    my $test = $h->{'DCAP_'.uc($cap)};
	    push @cap, $cap if defined($test) and $test eq 'YES';
	}
	return @cap;
    }
    sub TestCapability {
	my($self, $cap) = @_;
	my $h = $self->GetMetadata->{'DCAP_'.uc($cap)};
	return (defined($h) and $h eq 'YES') ? 1 : undef;
    }
    sub Extension {
	my $self = shift;
	my $h = $self->GetMetadata;
	return $h->{DMD_EXTENSION};
    }
    sub MIMEType {
	my $self = shift;
	my $h = $self->GetMetadata;
	return $h->{DMD_MIMETYPE};
    }
    sub CreationOptionList {
	my $self = shift;
	my @options;
	my $h = $self->GetMetadata->{DMD_CREATIONOPTIONLIST};
	if ($h) {
	    $h = Geo::GDAL::ParseXMLString($h);
	    my($type, $value) = Geo::GDAL::NodeData($h);
	    if ($value eq 'CreationOptionList') {
		for my $o (Geo::GDAL::Children($h)) {
		    my %option;
		    for my $a (Geo::GDAL::Children($o)) {
			my(undef, $key) = Geo::GDAL::NodeData($a);
			my(undef, $value) = Geo::GDAL::NodeData(Geo::GDAL::Child($a, 0));
			if ($key eq 'Value') {
			    push @{$option{$key}}, $value;
			} else {
			    $option{$key} = $value;
			}
		    }
		    push @options, \%option;
		}
	    }
	}
	return @options;
    }
    sub CreationDataTypes {
	my $self = shift;
	my $h = $self->GetMetadata;
	return split /\s+/, $h->{DMD_CREATIONDATATYPES};
    }
    sub CreateDataset {
	my @p = @_;
	$p[5] = $Geo::GDAL::TYPE_STRING2INT{$p[5]} if $p[5] and exists $Geo::GDAL::TYPE_STRING2INT{$p[5]};
	return _Create(@p);
    }
    *Create = *CreateDataset;
    *Copy = *CreateCopy;

    package Geo::GDAL::Dataset;
    use strict;
    use vars qw/%BANDS @DOMAINS/;
    @DOMAINS = ("IMAGE_STRUCTURE", "SUBDATASETS", "GEOLOCATION");
    sub Domains {
	return @DOMAINS;
    }
    *GetDriver = *_GetDriver;
    sub Open {
	return Geo::GDAL::Open(@_);
    }
    sub OpenShared {
	return Geo::GDAL::OpenShared(@_);
    }
    sub Size {
	my $self = shift;
	return ($self->{RasterXSize}, $self->{RasterYSize});
    }
    sub Bands {
	my $self = shift;
	my @bands;
	for my $i (1..$self->{RasterCount}) {
	    push @bands, GetRasterBand($self, $i);
	}
	return @bands;
    }
    sub GetRasterBand {
	my($self, $index) = @_;
	my $band = _GetRasterBand($self, $index);
	$BANDS{tied(%{$band})} = $self;
	return $band;
    }
    *Band = *GetRasterBand;
    sub AddBand {
	my @p = @_;
	$p[1] = $Geo::GDAL::TYPE_STRING2INT{$p[1]} if $p[1] and exists $Geo::GDAL::TYPE_STRING2INT{$p[1]};
	return _AddBand(@p);
    }
    sub Projection {
	my($self, $proj) = @_;
	SetProjection($self, $proj) if defined $proj;
	GetProjection($self) if defined wantarray;
    }
    sub GeoTransform {
	my $self = shift;
	SetGeoTransform($self, \@_) if @_ > 0;
	return unless defined wantarray;
	my $t = GetGeoTransform($self);
	return @$t;
    }
    sub GCPs {
	my $self = shift;
	if (@_ > 0) {
	    my $proj = pop @_;
	    SetGCPs($self, \@_, $proj);
	}
	return unless defined wantarray;
	my $proj = GetGCPProjection($self);
	my $GCPs = GetGCPs($self);
	return (@$GCPs, $proj);
    }

    package Geo::GDAL::Band;
    use Carp;
    use UNIVERSAL qw(isa);
    use strict;
    use vars qw/
	%COLOR_INTERPRETATION_STRING2INT %COLOR_INTERPRETATION_INT2STRING @DOMAINS
	/;
    for my $string (qw/Undefined GrayIndex PaletteIndex RedBand GreenBand BlueBand AlphaBand 
		    HueBand SaturationBand LightnessBand CyanBand MagentaBand YellowBand BlackBand/) {
	my $int = eval "\$Geo::GDAL::Constc::GCI_$string";
	$COLOR_INTERPRETATION_STRING2INT{$string} = $int;
	$COLOR_INTERPRETATION_INT2STRING{$int} = $string;
    }
    @DOMAINS = ("IMAGE_STRUCTURE", "RESAMPLING");
    sub Domains {
	return @DOMAINS;
    }
    sub DESTROY {
	my $self;
	if ($_[0]->isa('SCALAR')) {
	    $self = $_[0];
	} else {
	    return unless $_[0]->isa('HASH');
	    $self = tied(%{$_[0]});
	    return unless defined $self;
	}
	delete $ITERATORS{$self};
	if (exists $OWNER{$self}) {
	    delete $OWNER{$self};
	}
	$self->RELEASE_PARENTS();
    }
    sub RELEASE_PARENTS {
	my $self = shift;
	delete $Geo::GDAL::Dataset::BANDS{$self};
    }
    sub Size {
	my $self = shift;
	return ($self->{XSize}, $self->{YSize});
    }
    sub DataType {
	my $self = shift;
	return $Geo::GDAL::TYPE_INT2STRING{$self->{DataType}};
    }
    sub NoDataValue {
	my $self = shift;
	SetNoDataValue($self, $_[0]) if @_ > 0;
	GetNoDataValue($self);
    }
    sub ReadTile {
	my($self, $xoff, $yoff, $xsize, $ysize) = @_;
	$xoff = 0 unless defined $xoff;
	$yoff = 0 unless defined $yoff;
	$xsize = $self->{XSize} - $xoff unless defined $xsize;
	$ysize = $self->{YSize} - $yoff unless defined $ysize;
	my $buf = $self->ReadRaster($xoff, $yoff, $xsize, $ysize);
	my $pc = Geo::GDAL::PackCharacter($self->{DataType});
	my $w = $xsize * Geo::GDAL::GetDataTypeSize($self->{DataType})/8;
	my $offset = 0;
	my @data;
	for (0..$ysize-1) {
	    my $sub = substr($buf, $offset, $w);
	    my @d = unpack($pc."[$xsize]", $sub);
	    push @data, \@d;
	    $offset += $w;
	}
	return \@data;
    }
    sub WriteTile {
	my($self, $data, $xoff, $yoff) = @_;
	$xoff = 0 unless defined $xoff;
	$yoff = 0 unless defined $yoff;
	my $xsize = @{$data->[0]};
	$xsize = $self->{XSize} - $xoff if $xsize > $self->{XSize} - $xoff;
	my $ysize = @{$data};
	$ysize = $self->{YSize} - $yoff if $ysize > $self->{YSize} - $yoff;
	my $pc = Geo::GDAL::PackCharacter($self->{DataType});
	my $w = $xsize * Geo::GDAL::GetDataTypeSize($self->{DataType})/8;
	for my $i (0..$ysize-1) {
	    my $scanline = pack($pc."[$xsize]", @{$data->[$i]});
	    $self->WriteRaster( $xoff, $yoff+$i, $xsize, 1, $scanline );
	}
    }
    sub ColorInterpretation {
	my($self, $ci) = @_;
	if ($ci) {
	    $ci = $COLOR_INTERPRETATION_STRING2INT{$ci} if exists $COLOR_INTERPRETATION_STRING2INT{$ci};
	    SetRasterColorInterpretation($self, $ci);
	    return $ci;
	} else {
	    return $COLOR_INTERPRETATION_INT2STRING{GetRasterColorInterpretation($self)};
	}
    }
    sub ColorTable {
	my $self = shift;
	SetRasterColorTable($self, $_[0]) if @_;
	return unless defined wantarray;
	GetRasterColorTable($self);
    }
    sub CategoryNames {
	my $self = shift;
	SetRasterCategoryNames($self, \@_) if @_;
	return unless defined wantarray;
	my $n = GetRasterCategoryNames($self);
	return @$n;
    }
    sub AttributeTable {
	my $self = shift;
	SetDefaultRAT($self, $_[0]) if @_;
	return unless defined wantarray;
	my $r = GetDefaultRAT($self);
	$Geo::GDAL::RasterAttributeTable::BANDS{$r} = $self;
	return $r;
    }
    sub GetHistogram {
	my $self = shift;
	my %defaults = (Min => -0.5,
			Max => 255.5,
			Buckets => 256, 
			IncludeOutOfRange => 0,
			ApproxOK => 0,
			Progress => undef,
			ProgressData => undef);
	my %params = @_;
	for (keys %params) {
	    croak "unknown parameter: $_" unless exists $defaults{$_};
	}
	for (keys %defaults) {
	    $params{$_} = $defaults{$_} unless defined $params{$_};
	}
	$params{ProgressData} = 1 if $params{Progress} and not defined $params{ProgressData};
	my $h = _GetHistogram($self, $params{Min}, $params{Max}, $params{Buckets},
			      $params{IncludeOutOfRange}, $params{ApproxOK},
			      $params{Progress}, $params{ProgressData});
	return @$h if $h;
    }
    sub Contours {
	my $self = shift;
	my %defaults = (DataSource => undef,
			LayerConstructor => {Name => 'contours'},
			ContourInterval => 100, 
			ContourBase => 0,
			FixedLevels => [], 
			NoDataValue => undef, 
			IDField => -1, 
			ElevField => -1,
			callback => undef,
			callback_data => undef);
	my %params;
	if (!defined($_[0]) or isa($_[0], 'Geo::OGR::DataSource')) {
	    ($params{DataSource}, $params{LayerConstructor},
	     $params{ContourInterval}, $params{ContourBase},
	     $params{FixedLevels}, $params{NoDataValue}, 
	     $params{IDField}, $params{ElevField},
	     $params{callback}, $params{callback_data}) = @_;
	} else {
	    %params = @_;
	}
	for (keys %params) {
	    croak "unknown parameter: $_" unless exists $defaults{$_};
	}
	for (keys %defaults) {
	    $params{$_} = $defaults{$_} unless defined $params{$_};
	}
	$params{DataSource} = Geo::OGR::GetDriver('Memory')->CreateDataSource('ds') 
	    unless defined $params{DataSource};
	my $layer = $params{DataSource}->CreateLayer($params{LayerConstructor});
	my $schema = $layer->GetLayerDefn;
	for ('IDField', 'ElevField') {
	    $params{$_} = $schema->GetFieldIndex($params{ElevField}) unless $params{ElevField} =~ /^[+-]?\d+$/;
	}
	$params{callback_data} = 1 if $params{callback} and not defined $params{callback_data};
	ContourGenerate($self, $params{ContourInterval}, $params{ContourBase}, $params{FixedLevels},
			$params{NoDataValue}, $layer, $params{IDField}, $params{ElevField},
			$params{callback}, $params{callback_data});
	return $layer;
    }
    sub FillNodata {
      croak 'usage: FillNodata($mask)' unless isa($_[1], 'Geo::GDAL::Band');
      $_[2] = 10 unless defined $_[2];
      $_[3] = 0 unless defined $_[3];
      $_[4] = undef unless defined $_[4];
      $_[5] = undef unless defined $_[5];
      $_[6] = undef unless defined $_[6];
      Geo::GDAL::FillNodata(@_);
    }

    package Geo::GDAL::ColorTable;
    use strict;
    use vars qw/
	%PALETTE_INTERPRETATION_STRING2INT %PALETTE_INTERPRETATION_INT2STRING
	/;
    for my $string (qw/Gray RGB CMYK HLS/) {
	my $int = eval "\$Geo::GDAL::Constc::GPI_$string";
	$PALETTE_INTERPRETATION_STRING2INT{$string} = $int;
	$PALETTE_INTERPRETATION_INT2STRING{$int} = $string;
    }
    sub create {
	my($pkg, $pi) = @_;
	$pi = $PALETTE_INTERPRETATION_STRING2INT{$pi} if defined $pi and exists $PALETTE_INTERPRETATION_STRING2INT{$pi};
	my $self = Geo::GDALc::new_ColorTable($pi);
	bless $self, $pkg if defined($self);
    }
    sub GetPaletteInterpretation {
	my $self = shift;
	return $PALETTE_INTERPRETATION_INT2STRING{GetPaletteInterpretation($self)};
    }
    sub SetColorEntry {
	@_ == 3 ? _SetColorEntry(@_) : _SetColorEntry(@_[0..1], [@_[2..5]]);
    }
    sub ColorEntry {
	my $self = shift;
	my $index = shift;
	SetColorEntry($self, $index, @_) if @_ > 0;
	GetColorEntry($self, $index) if defined wantarray;
    }
    sub ColorTable {
	my $self = shift;
	my @table;
	if (@_) {
	    my $index = 0;
	    for my $color (@_) {
		push @table, [ColorEntry($self, $index, @$color)];
		$index++;
	    }
	} else {
	    for (my $index = 0; $index < GetCount($self); $index++) {
		push @table, [ColorEntry($self, $index)];
	    }
	}
	return @table;
    }

    package Geo::GDAL::RasterAttributeTable;
    use strict;
    use vars qw/ %BANDS
	%FIELD_TYPE_STRING2INT %FIELD_TYPE_INT2STRING
	%FIELD_USAGE_STRING2INT %FIELD_USAGE_INT2STRING
	/;
    for my $string (qw/Integer Real String/) {
	my $int = eval "\$Geo::GDAL::Constc::GFT_$string";
	$FIELD_TYPE_STRING2INT{$string} = $int;
	$FIELD_TYPE_INT2STRING{$int} = $string;
    }
    for my $string (qw/Generic PixelCount Name Min Max MinMax 
		    Red Green Blue Alpha RedMin 
		    GreenMin BlueMin AlphaMin RedMax GreenMax BlueMax AlphaMax 
		    MaxCount/) {
	my $int = eval "\$Geo::GDAL::Constc::GFU_$string";
	$FIELD_USAGE_STRING2INT{$string} = $int;
	$FIELD_USAGE_INT2STRING{$int} = $string;
    }
    sub FieldTypes {
	return keys %FIELD_TYPE_STRING2INT;
    }
    sub FieldUsages {
	return keys %FIELD_USAGE_STRING2INT;
    }
    sub RELEASE_PARENTS {
	my $self = shift;
	delete $BANDS{$self};
    }
    sub GetUsageOfCol {
	my($self, $col) = @_;
	$FIELD_USAGE_INT2STRING{_GetUsageOfCol($self, $col)};
    }
    sub GetColOfUsage {
	my($self, $usage) = @_;
	_GetColOfUsage($self, $FIELD_USAGE_STRING2INT{$usage});
    }
    sub GetTypeOfCol {
	my($self, $col) = @_;
	$FIELD_TYPE_INT2STRING{_GetTypeOfCol($self, $col)};
    }
    sub Columns {
	my $self = shift;
	my %columns;
	if (@_) { # create columns
	    %columns = @_;
	    for my $name (keys %columns) {
		$self->CreateColumn($name, $columns{$name}{Type}, $columns{$name}{Usage});
	    }
	}
	%columns = ();
	for my $c (0..$self->GetColumnCount-1) {
	    my $name = $self->GetNameOfCol($c);
	    $columns{$name}{Type} = $self->GetTypeOfCol($c);
	    $columns{$name}{Usage} = $self->GetUsageOfCol($c);
	}
	return %columns;
    }
    sub CreateColumn {
	my($self, $name, $type, $usage) = @_;
	_CreateColumn($self, $name, $FIELD_TYPE_STRING2INT{$type}, $FIELD_USAGE_STRING2INT{$usage});
    }
    sub Value {
	my($self, $row, $column) = @_;
	SetValueAsString($self, $row, $column, $_[3]) if defined $_[3];
	return unless defined wantarray;
	GetValueAsString($self, $row, $column);
    }

 %}

#define GDAL_BINDINGS
%import ogr.i
%{
typedef void OGRLayerShadow;
%}
%extend GDALRasterBandShadow {
    %apply (int nList, double* pList) {(int nFixedLevelCount, double *padfFixedLevels)};
    %apply (int defined, double value) {(int bUseNoData, double dfNoDataValue)};
    CPLErr ContourGenerate(double dfContourInterval, double dfContourBase,
			   int nFixedLevelCount, double *padfFixedLevels,
			   int bUseNoData, double dfNoDataValue, 
			   OGRLayerShadow *hLayer, int iIDField, int iElevField,
			   GDALProgressFunc callback = NULL,
			   void* callback_data = NULL) {
	return GDALContourGenerate( self, dfContourInterval, dfContourBase,
				    nFixedLevelCount, padfFixedLevels,
				    bUseNoData, dfNoDataValue, 
				    hLayer, iIDField, iElevField,
				    callback,
				    callback_data );
    }
    %clear (int nFixedLevelCount, double *padfFixedLevels);
    %clear (int bUseNoData, double dfNoDataValue);
}
