# -*- perl -*-

#
# $Id: DBase.pm,v 1.14 2007/10/13 21:23:09 eserte Exp $
# Author: Slaven Rezic
#
# Copyright (C) 2001,2002,2004,2005 Slaven Rezic. All rights reserved.
#
# Mail: eserte@users.sourceforge.net
# WWW:  http://bbbike.sourceforge.net
#

package ESRI::Shapefile::DBase;

use strict;
use vars qw(@ISA);

use Class::Accessor; # workaround 5.00503 bug (???)
use base qw(Class::Accessor::Fast);

__PACKAGE__->mk_accessors(qw(File Root Data DBH XBase Init Fields FieldsHash));

sub new {
    my $this = shift;
    my $class = ref($this) || $this;
    my $self = {};
    bless $self, $class;

    my(%args) = @_;

    if ($args{-file}) {
	$self->set_file($args{-file});
    }
    if ($args{-root}) {
	$self->Root($args{-root});
    }
    $self->Init(0);

    $self;
}

sub STORABLE_freeze {
    my($self, $cloning) = @_;
    return if $cloning;
    # workaround with "\" to serialize also mere scalars
    ("", \$self->Root, \$self->File, \$self->Data, \$self->Init, \$self->Fields, \$self->FieldsHash);
}

sub STORABLE_thaw {
    my $self = shift;
    return if shift; # $cloning
    shift; # serialized
    $self->Root      (${shift()}); # ${ ... } is the workaround back
    $self->File      (${shift()});
    $self->Data      (${shift()});
    $self->Init      (${shift()});
    $self->Fields    (${shift()});
    $self->FieldsHash(${shift()});
}

sub set_file {
    my($self, $file) = @_;

    $self->File($file);

    open(F, $file) or die "Can't open $file: $!";

    close F;
}

sub init {
    my($self, %args) = @_;
    if (!$self->Init || $args{'-force'}) {
	if ($args{'-nopreload'}) {
	    $self->tie_array;
	} else {
	    $self->preload_records;
	}
	$self->Init(1);
    }
    $self;
}

sub preload_records {
    my $self = shift;
    my $sth = $self->_get_all_sth;
    my @data;
    while(my @row = $sth->fetchrow_array) {
	## Used encoding seems to depend on the application. I saw
	## cp850 and latin1 encoded data.
	#cp850_iso($_) foreach (@row);
	push @data, \@row;
    }
    $self->Data(\@data);
    $self->_finish_db;
}

{
    package ESRI::Shapefile::DBase::TieArray;
    sub TIEARRAY {
	bless [$_[1]], $_[0];
    }
    sub FETCH {
	$_[0]->[0]->get_records($_[1]);
    }
    sub STORE {
	die "Not supported";
    }
    sub FETCHSIZE {
	my $self = shift;
	$self->[0]->last_record + 1;
    }
}

sub tie_array {
    my $self = shift;
    my $xbase = $self->get_xbase_object;
    tie my @data, 'ESRI::Shapefile::DBase::TieArray', $xbase;
}

sub get_xbase_object {
    my $self = shift;
    if (!$self->XBase) {
	require XBase;
	$self->XBase(XBase->new($self->File));
	die "Can't create XBase object from @{[ $self->File ]}"
	    if !$self->XBase;
    }
    $self->XBase;
}

sub get_fields {
    my $self = shift;

    $self->get_xbase_object;

    if (!$self->Fields) {
	$self->Fields([ $self->XBase->field_names ]);
    }

    if (!$self->FieldsHash) {
	my %h;
	for my $i (0 .. scalar @{ $self->Fields }) {
	    $h{$self->Fields->[$i]} = $i;
	}
	$self->FieldsHash(\%h);
    }

    @{ $self->Fields };
}

sub _get_all_sth {
    my $self = shift;

    require File::Basename;
    require DBI;
    $DBI::errstr = $DBI::errstr if 0; # cease -w

    my($name, $dir) = File::Basename::fileparse($self->File, '\..*');
    my $dbh = DBI->connect("DBI:XBase:$dir") or die $DBI::errstr;
    my $sth = $dbh->prepare("select * from $name") or die $dbh->errstr;
    $sth->execute or die $sth->errstr;

    $self->DBH($dbh);

    $sth;
}

sub _finish_db {
    my $self = shift;
    if ($self->DBH) {
	$self->DBH->disconnect;
	$self->DBH(undef);
    }
}

sub cp850_iso {
    $_[0] =~ tr/\204\224\201\216\231\232\341\202\370/äöüÄÖÜßé°/;
}

1;

__END__
