#!/usr/bin/perl -w

=head1 NAME

dfsqlite-pilottodo - convert a gpe-todo sqlite database into pilot-qof data

=head1 DATAFREEDOM

These scripts developed from the 'pilot-qof' package but now include
support for other packages and formats and will continue to be extended
along the lines of http://www.data-freedom.org/ - liberating user data 
from the application. Therefore, the datafreedom scripts use a 'df' prefix.

The scripts continue to be developed within the pilot-qof CVS until such
time as the scripts are sufficiently cohesive to form a new source package.

Please feel free to contribute any of your own scripts, under the provisions
of the GNU General Public Licence v3 or later, via the QOF-devel mailing list.
http://lists.sourceforge.net/lists/listinfo/qof-devel

=head1 VERSION

Version 0.0.2

=head1 SYNOPSIS

 dfsqlite-pilottodo FILENAME
 dfsqlite-pilottodo --xml
 dfsqlite-pilottodo -h|--help|--version

=head1 DESCRIPTION

dfsqlite-pilottodo read a SQLite file created by gpe-todo
and prepares a sqlite database suitable for pilot-qof. Optionally,
print a QSF XMl file to stdout instead.

If the default SQLite is used and the sqlite database already
exists, data from gtodo will be inserted into the database as
new records.

Currently, there is no method to identify matching records reliably,
so updating an existing SQLite database is not supported.

Depends on libdbd-sqlite2-perl and libdbi-perl

=head1 OBJECTS

L<http://qof.sourceforge.net/>

L<http://pilot-qof.sourceforge.net/>

=head1 AUTHOR

Neil Williams, C<< <codehelp at debian.org> >>

=head1 BUGS

Please report bugs via the pilot-qof package, either
in the Debian BTS or via SourceForge trackers.

=head1 COPYRIGHT & LICENSE

  Copyright 2007 Neil Williams.

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.

=cut

use strict;
use File::HomeDir;
use Date::Format;
use Date::Parse;
use Number::Format qw(:subs);
use XML::QOFQSF qw(QSFWrite);
use DBI;
use DBD::SQLite2;
use Data::Random qw(:all);

my $our_version = "0.0.2";
sub usageversion {
    print(STDERR <<END)
dfsqlite-pilottodo version $our_version

Usage:
 dfsqlite-pilottodo FILENAME
 dfsqlite-pilottodo --xml
 dfsqlite-pilottodo -h|--help|--version

Options:
    --xml:            Print a QSF XML file to stdout instead 
                       of creating a SQLite database.
 -h|--help:           Print this usage message and exit.
 --version:           Print this usage message and exit.

dfsqlite-pilottodo read a SQLite file created by gpe-todo
and prepares a sqlite database suitable for pilot-qof. Optionally,
print a QSF XMl file to stdout instead.

If the default SQLite is used and the sqlite database already
exists, data from gtodo will be inserted into the database as
new records.

END
        || die "$0: failed to write usage: $!\n";
exit 0;
}

my $usexml = "false";
while( @ARGV ) {
    $_= shift( @ARGV );
    last if m/^--$/;
    if (!/^-/) {
        unshift(@ARGV,$_);
        last;
    }
    if (/^(-h|--help|--version)$/) {
        &usageversion();
        exit( 0 );
    }
    elsif (/^(--xml)$/) {
    	$usexml = "true";
    }
}

my $home = File::HomeDir->my_home;
my $hash_ref;
# read the GPE categories
my @categories = ();
push @categories, "Unfiled";
my $catfile = "$home/.gpe/categories";
my $statement = qq/SELECT * FROM category;/;
my $cath = DBI->connect("dbi:SQLite2:dbname=$catfile", "", "");
my $cth = $cath->prepare($statement);
$cth->execute();
while ($hash_ref = $cth->fetchrow_hashref)
{
	push @categories, $hash_ref->{'description'};
}
$cath->disconnect;

my $file = "$home/.gpe/todo";
# find the file automatically in ~/.gpe/todo

my $dbh = DBI->connect("dbi:SQLite2:dbname=$file","","");
my $args = "";
$args = $ARGV[0] if ($ARGV[0]);
&usageversion if (($args eq "") && ($usexml ne "true"));

# get the starting uid value
my $uid = 0;
$statement = qq/select min(uid) from todo;/;
my $sth = $dbh->prepare($statement);
my $rv = $sth->execute;
while ($hash_ref = $sth->fetchrow_hashref)
{
	$uid = $hash_ref->{'min(uid)'};
}

$statement = "select * from todo;";
$sth = $dbh->prepare($statement);
$rv = $sth->execute;
my $exsth;
my $template = "%Y-%m-%dT%H:%M:%SZ";
my $todo_description = "";
my $todo_note = "";
# assume no date unless DUE is set
my $todo_length = 1;
my $todo_priority = 0;
my @todos = ();
my $todo = new ToDo;
$todo->date_due(time2str($template, 0));
$todo->todo_complete(0);

while ($hash_ref = $sth->fetchrow_hashref)
{
	my $due = 0;
	my $tag = $hash_ref->{'tag'};
	my $value = $hash_ref->{'value'};
	$todo->todo_description($value) if ($tag eq 'SUMMARY');
	$value =~ s/\n//g;
	$todo->todo_length($todo_length);
	if ($tag eq 'DUE')
	{
		$due = str2time($value);
		$todo_length = 0 if ($due > 0);
		$todo->date_due(time2str($template, $due));
		$todo->todo_length($todo_length);
	}
	$todo->category($categories[$value]) if ($tag eq 'CATEGORY');
	$todo->todo_note($value) if ($tag eq 'DESCRIPTION');
	$todo->todo_priority($value) if ($tag eq 'PRIORITY');
	$todo->todo_complete(1) if (($tag eq 'STATE') && ($value >= 2));
	if ($uid != $hash_ref->{'uid'})
	{
		$uid = $hash_ref->{'uid'};
		push @todos, $todo;
		$todo = new ToDo;
		$todo->date_due(time2str($template, 0));
		$todo->todo_complete(0);
	}
}

my %obj;
$obj{'pilot_todo'} = \@todos;

my $create = 0;
my $export = "";
$export = $ARGV[0] if ($ARGV[0]);
$create = 1 if ((! -f $export) && ($usexml ne "true"));
my $exdbh = DBI->connect("dbi:SQLite2:dbname=$export", "", "");
&create_todo if ($create == 1);

if ($usexml eq "true")
{
	QSFWrite(\%obj);
	$sth->finish;
	$dbh->disconnect;
	exit;
}

my $book_guid = &create_guid;
foreach my $rec (@todos)
{
	my $note = $rec->todo_note ? $rec->todo_note : "";
	my $desc = $rec->todo_description ? $rec->todo_description : "";
	my $priority = $rec->todo_priority ? $rec->todo_priority : 0;
	my $length = $rec->todo_length ? $rec->todo_length : 0;
	my $complete = $rec->todo_complete ? $rec->todo_complete : 0;
	my $due = $rec->date_due ? $rec->date_due : 0;
	my $category = $rec->category ? $rec->category : "Unfiled";
	my $g = &create_guid;
	my $exsql = qq/INSERT into pilot_todo (todo_note, todo_description, todo_priority, /;
	$exsql .= qq/todo_length, todo_complete, category, date_due, guid, book) /;
	$exsql .= qq/VALUES ("$note", "$desc", "$priority", "$length", "$complete", /;
	$exsql .= qq/"$category", "$due", "$g", "$book_guid");/;
	$exsth = $exdbh->prepare($exsql);
	$exsth->execute;
}

$exsth->finish;
$sth->finish;
$dbh->disconnect;
$exdbh->disconnect;

sub create_todo
{
	# check use of TEXT for date_due
	my $sql = qq/CREATE TABLE pilot_todo ( date_due datetime, todo_note mediumtext, /;
	$sql .= qq/todo_description mediumtext, category mediumtext, todo_priority int, /;
	$sql .= qq/book char(32), guid char(32), todo_complete int, /;
	$sql .= qq/todo_length int, dbversion int );/;
	$exsth = $exdbh->prepare($sql);
	$exsth->execute;
}

sub create_guid
{
	#	Only needed until XML::QOFQSF can make this function available.
	my @random_chars = rand_chars( set => 'numeric', min => 8, max => 8, shuffle => 1 );
	my $r = join("", @random_chars);
	@random_chars = rand_chars( set => 'numeric', min => 8, max => 8, shuffle => 1 );
	$r .= join("", @random_chars);
	@random_chars = rand_chars( set => 'numeric', min => 8, max => 8, shuffle => 1 );
	$r .= join("", @random_chars);
	@random_chars = rand_chars( set => 'numeric', min => 8, max => 8, shuffle => 1 );
	$r .= join("", @random_chars);
	@random_chars = rand_chars( set => 'numeric', min => 8, max => 8, shuffle => 1 );
	$r .= join("", @random_chars);
	my $x = Math::BigInt->new("$r");
	my $g = $x->as_hex();
	$g =~ s/^0x//;
	$g =~ /([0-9a-f]{32})/;
	return $g;
}
