The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Lingua::PTD::SQLite;

use strict;
use warnings;

use parent 'Lingua::PTD';
our $VERSION = '1.0';
use DBI;

=encoding UTF-8

=head1 NAME

Lingua::PTD::SQLite - Sub-module to handle PTD files in sqlite format

=head1 SYNOPSIS

  use Lingua::PTD;

  $ptd = Lingua::PTD->new( "file.sqlite" );

=head1 DESCRIPTION

Check L<<Lingua::PTD>> for complete reference.

=head1 SEE ALSO

NATools(3), perl(1)

=head1 AUTHOR

Alberto Manuel Brandão Simões, E<lt>ambs@cpan.orgE<gt>

=head1 COPYRIGHT AND LICENSE

Copyright (C) 2010-2014 by Alberto Manuel Brandão Simões

=cut

sub new {
    my ($class, $filename) = @_;
    my $dbh = DBI->connect("dbi:SQLite:dbname=$filename", "", "",
                          { sqlite_unicode => 1} ) or die "Cant connect to database";
    my $self = {
                dbh => $dbh,
                get_meta => $dbh->prepare("SELECT v FROM meta WHERE k = ?;"),
                get_occs => $dbh->prepare("SELECT occ FROM occs WHERE w = ?;"),
                exists   => $dbh->prepare("SELECT w FROM trans WHERE w = ?;"),
               };
    bless $self => $class # amen
}

sub trans {
    my ($self, $word, $trans) = @_;
    if ($trans) {
        my $sth = $self->{dbh}->prepare("SELECT p FROM trans WHERE w = ? AND t = ?;");
        $sth->execute($word, $trans);
        my @row = $sth->fetchrow_array;
        return (@row)?1:0;
    } else {
        my $sth = $self->{dbh}->prepare("SELECT t FROM trans WHERE w = ?");
        $sth->execute($word);
        my @ans;
        my @row;
        while (@row = $sth->fetchrow_array) { push @ans, $row[0] };
        return @ans;
    }
}


sub prob {
    my ($self, $word, $trans) = @_;
    my $sth = $self->{dbh}->prepare("SELECT p FROM trans WHERE w = ? AND t = ?;");
    $sth->execute($word, $trans);
    my @row = $sth->fetchrow_array;
    return (@row)?$row[0]:0;
}

sub words {
    my $self = shift;
    my $sort = $_[0] ? "ORDER BY w ASC" : "";

    my $sth = $self->{dbh}->prepare("SELECT w FROM occs $sort;");
    $sth->execute;
    my @answer = ();
    my @row;
    while (@row = $sth->fetchrow_array()) { push @answer, $row[0] };
    return @answer;
}

sub exists {
    my ($self, $word) = @_;
    $self->{exists}->execute($word);
    if ($self->{exists}->fetchrow_array()) {
        return 1;
    } else {
        return 0;
    }
}

sub _calculate_sizes {
    my $self = shift;
    my $sth = $self->{dbh}->prepare("SELECT COUNT(w) FROM occs");
    $sth->execute;
    my @row = $sth->fetchrow_array;
    $self->_update_meta("count", $row[0]);

    $sth = $self->{dbh}->prepare("SELECT SUM(occ) FROM occs");
    $sth->execute;
    @row = $sth->fetchrow_array;
    $self->_update_meta("size", $row[0]);
}

sub _set_word_translation {
    my ($self, $w, $t, $p) = @_;
    my $sth = $self->{dbh}->prepare("INSERT OR REPLACE INTO trans VALUES(?,?,?);");
    $sth->execute($w, $t, $p);
}

sub _delete_word_translation {
    my ($self, $w, $t) = @_;
    my $sth = $self->{dbh}->prepare("DELETE FROM trans WHERE w = ? AND t = ?");
    $sth->execute($w, $t);
}

sub _set_word_count {
    my ($self, $w, $c) = @_;
    my $sth = $self->{dbh}->prepare("INSERT OR REPLACE INTO occs VALUES(?,?);");
    $sth->execute($w, $c);
}

sub _update_meta {
    my ($self, $k, $v) = @_;
    my $sth = $self->{dbh}->prepare("INSERT OR REPLACE INTO meta VALUES(?,?);");
    $sth->execute($k,$v);
}


sub size {
    my $self = shift;
    $self->{get_meta}->execute("size");
    my @row = $self->{get_meta}->fetchrow_array;
    return @row ? $row[0] : 0;
}

sub count {
    my ($self, $word) = @_;
    if ($word) {
        $self->{get_occs}->execute($word);
        my @row = $self->{get_occs}->fetchrow_array;
        return (@row)?$row[0]:0;
    } else {
        $self->{get_meta}->execute("count");
        my @row = $self->{get_meta}->fetchrow_array;
        return $row[0];
    }
}

sub _init_transaction {
    my $self = shift;
    $self->{dbh}->begin_work;
}

sub _commit {
    my $self = shift;
    $self->{dbh}->commit;
}

sub _update_word {
    my ($self, $word, $entry) = @_;

    my ($k) = keys %$entry;
    $entry = $entry->{$k};

    my $sth;

    if ($k ne $word) {
        $sth = $self->{dbh}->prepare("DELETE FROM occs WHERE w = ?");
        $sth->execute($word);

        $sth = $self->{dbh}->prepare("DELETE FROM trans WHERE w = ?");
        $sth->execute($word);

        $word = $k;
    }

    $sth = $self->{dbh}->prepare("DELETE FROM occs WHERE w = ?");
    $sth->execute($word);

    $sth = $self->{dbh}->prepare("INSERT INTO occs VALUES(?, ?);");
    $sth->execute($word, $entry->{count}, );

    $sth = $self->{dbh}->prepare("DELETE FROM trans WHERE w = ?");
    $sth->execute($word);

    $sth = $self->{dbh}->prepare("INSERT INTO trans VALUES (?,?,?)");
    for my $t (keys %{$entry->{trans}}) {
        $sth->execute($word, $t, $entry->{trans}{$t});
    }
}

sub _delete_word {
    my ($self, $word) = @_;
    my $sth = $self->{dbh}->prepare("DELETE FROM trans WHERE w = ?");
    $sth->execute($word);
    $sth = $self->{dbh}->prepare("DELETE FROM occs WHERE w = ?");
    $sth->execute($word);
}

sub _trans_hash {
    my ($self, $trans) = @_;
    my $sth = $self->{dbh}->prepare("SELECT t, p FROM trans WHERE w = ?");
    $sth->execute($trans);
    my %t;
    my @row;
    while (@row = $sth->fetchrow_array) {
        $t{$row[0]} = $row[1];
    }
    return %t;
}

sub _save {
    my ($self, $filename) = @_;

    unlink $filename if -f $filename;
    warn "Cant write on $filename" and return 0 if -f $filename;

    my $dbh = DBI->connect("dbi:SQLite:dbname=$filename", "", "", {sqlite_unicode => 1});
    warn "Cant create sqlite file" and return 0 unless $dbh;

    $dbh->do("CREATE TABLE trans (w, t, p REAL);");
    $dbh->do("CREATE TABLE occs  (w, occ INTEGER);");
    $dbh->do("CREATE TABLE meta  (k PRIMARY KEY, v);");

    my $insert_meta  = $dbh->prepare("INSERT INTO meta  VALUES (?, ?);");
    my $insert_trans = $dbh->prepare("INSERT INTO trans VALUES (?,?,?);");
    my $insert_occs  = $dbh->prepare("INSERT INTO occs  VALUES (?, ?);");

    $dbh->begin_work;

    $insert_meta->execute("size",  $self->size);
    $insert_meta->execute("count", $self->count);

    $self->downtr( sub {
                       my ($w, $c, %t) = @_;
                       $insert_occs->execute($w, $c);
                       for my $t (keys %t) {
                           $insert_trans->execute($w, $t, $t{$t});
                       }
                   });

    $dbh->commit;

    $dbh->do("CREATE INDEX transPM ON trans (w,t)");
    $dbh->do("CREATE INDEX occsPM ON occs (w)");

    return 1;
}


"This isn't right.  This isn't even wrong.";
__END__