#!/usr/bin/perl

use strict;
no strict 'refs';
use PROP::Conf;
use PROP::DBH;
use PROP::Util;
use PROP::Link;
use Data::Dumper;


my $rdbms = shift(@ARGV);
my $cnxn_conf = shift(@ARGV);
configure_connection($rdbms, $cnxn_conf); # to where and how we connect

my @demos = @ARGV || ('object_demo',
		      'lazy_link_loading_demo',
		      'preemptive_link_loading_demo');

foreach my $demo (@demos) {
    init_tables();    
    print "\n\n";
    my $line = "# running $demo #";
    print '#' x length($line), "\n";
    print $line, "\n";
    print '#' x length($line), "\n";
    &$demo();
}

sub object_demo {
    print "creating object...\n";
    my $foo = Foo->new();

    print "setting field values...\n";
    $foo->set_bar(3);
    $foo->set_baz("meh");

    print "saving object...\n";
    $foo->save();
    my $pk_value = $foo->get_pk_value();

    print "loading object by primary key value (" . $pk_value . ")\n";
    $foo = new Foo($pk_value);
    
    print "retrieving field values...\n";
    print "bar: ", $foo->get_bar(), "\n";
    print "baz: ", $foo->get_baz(), "\n";

    print "changing bar to 5 and saving object...\n";
    $foo->set_bar(5);
    $foo->save();

    print "loading object by field value specification\n";
    $foo =  Foo->new({ bar => 5, baz => 'meh'});

    print "verifying primary key value: ", $pk_value, "\n";

    print "deleting object...\n";
    $foo->delete();

    print "attempting to load deleted object...\n";
    $foo = new Foo({ bar => 3, baz => 'meh' });

    print "primary key value should be unset: ",
	$foo->get_pk_value() ? 'uh-oh' : 'yup', "\n";
}

# 'lazy' as in "load the object, and then load some relatives later
sub lazy_link_loading_demo {
    my $link = new PROP::Link('LinkFooToBiz', 'Foo', 'Biz');

    my $foo = new Foo();
    $foo->set_bar(4);
    $foo->set_baz('score');
    $foo->save();

    for(my $i = 0; $i < 8; ++$i) {
	my $biz = new Biz();
	$biz->set_boz($i);
	$biz->set_buzz('biz' . $i);
	$biz->save();

	$link->insert($foo, $biz, { c1 => $i, c2 => $i * 2, c3 => 'bleh' . $i });
    }

    my $lq = new PROP::Query::Link($link,
					 'children',
					 ['c.boz % ?', 'l.c1 > ?'],
					 [2, 4], ['l.c1 DESC']);

    $foo->load_relatives([$lq]);

    my @children = $foo->get_children('LinkFooToBiz');
    foreach my $biz (@children) {
	print "biz = "  . $biz->get_pk_value() .
	    ", boz = "  . $biz->get_boz() .
	    ", buzz = " . $biz->get_buzz() .
	    " c1 = "    . $biz->get_contextual_value('c1') .
	    " c2 = "    . $biz->get_contextual_value('c2') .
	    " c3 = "    . $biz->get_contextual_value('c3') .
	    "\n";
    }
}

sub preemptive_link_loading_demo {
    my (@bizzes, @fuzzes);

    my $fb_link = new PROP::Link('LinkFooToBiz',  'Foo', 'Biz');
    my $ff_link = new PROP::Link('LinkFooToFuzz', 'Foo', 'Fuzz');

    for(my $i = 0; $i < 16; ++$i) {
	my $biz = new Biz();
	$biz->set_boz($i + 1);
	$biz->set_buzz('meh' . $i);
	$biz->save();
	push(@bizzes, $biz);
    }

    for(my $i = 0; $i < 16; ++$i) {
	my $fuzz = new Fuzz();
	$fuzz->set_fooz($i + 1);
	$fuzz->set_fozz('bleh' . $i);
	$fuzz->save();
	push(@fuzzes, $fuzz);
    }

    for(my $i = 0; $i < 4; ++$i) {
	my $foo = new Foo();
	$foo->set_bar($i);
	$foo->set_baz('feh' . $i);
	$foo->save();

	for(my $j = 0; $j < 4; ++$j) {
	    $fb_link->insert($foo, $bizzes[$i * 4 + $j],
			     { c1 => $j - $i,
			       c2 => $i * $j,
			       c3 => "blah_${i}_${j}" });

	    $ff_link->insert($foo, $fuzzes[$i * 4 + $j],
			     { d1 => 2 * ($j - $i),
			       d2 => 2 * ($i * $j),
			       d3 => "meh_${i}_${j}" });
	}
    }

    my $lq1 = new PROP::Query::Link($fb_link, 'children', ['c.boz % ?'], [2], []);
    my $lq2 = new PROP::Query::Link($ff_link, 'children', ['c.fuzz % ?'], [3], []);

    my $oq  = new PROP::Query::Object('Foo', ['foo % ?'], [2], [], [$lq1, $lq2]);
    my $result_set  = $oq->execute();

    while(my $foo = $result_set->get_next_result()) {
	print "Foo: " . 
	    " foo = " . $foo->get_pk_value() .
	    " bar = " . $foo->get_bar() .
	    " baz = " . $foo->get_baz() .
	    "\n";

	foreach my $biz ($foo->get_children($fb_link->get_table_name())) {
	    print "Biz: " . 
		" biz = " . $biz->get_pk_value() .
		" boz = " . $biz->get_boz() .
		" buzz = " . $biz->get_buzz() .
		" c1 = " . $biz->get_contextual_value('c1') .
		" c2 = " . $biz->get_contextual_value('c2') .
		" c3 = " . $biz->get_contextual_value('c3') .
		"\n";
	}

	foreach my $fuzz ($foo->get_children($ff_link->get_table_name())) {
	    print "Fuzz: " . 
		" fuzz = " . $fuzz->get_pk_value() .
		" fooz = " . $fuzz->get_fooz() .
		" fozz = " . $fuzz->get_fozz() .
		" d1 = " . $fuzz->get_contextual_value('d1') .
		" d2 = " . $fuzz->get_contextual_value('d2') .
		" d3 = " . $fuzz->get_contextual_value('d3') .
		"\n";
	}

	print "------------------------------------------\n";
    }
}


# the library framework requires information on which DBD to use and
# information that specifies the database to which a connection will
# be made
sub configure_connection {
    my ($rdbms, $cnxn_conf) = @_;

    set_rdbms($rdbms);
    set_cnxn_conf($cnxn_conf);
}

# the library framework expects the database to have tables in it...
sub init_tables {
    my @stmts;

    # object tables must have a primary key field, and can have as
    # many other data fields as you like

    $stmts[0] = q{ create table Foo (
                   foo int(10) NOT NULL auto_increment,
                   bar int(10),
                   baz varchar(50),
		   primary key (foo))
		   };
    
    $stmts[1] = q{ create table Biz (
                   biz  int(10) NOT NULL auto_increment,
                   boz  int(10),
                   buzz varchar(50),
                   primary key (biz))
		   };

    $stmts[2] = q{ create table Fuzz (
                   fuzz int(10) NOT NULL auto_increment,
                   fooz int(10),
                   fozz varchar(50),
                   primary key (fuzz))
		   };


    # link tables must have a dual primary key and may optionally have
    # "contextual values"

    $stmts[3] = q{ create table LinkFooToBiz (
                   foo_id int(10) NOT NULL,
                   biz_id int(10) NOT NULL,
                   c1     int(10),
                   c2     int(10),
                   c3     varchar(50),
		   primary key (foo_id, biz_id))
	       };

    $stmts[4] = q{ create table LinkFooToFuzz (
                   foo_id  int(10) NOT NULL,
                   fuzz_id int(10) NOT NULL,
                   d1      int(10),
                   d2      int(10),
                   d3      varchar(50),
		   primary key (foo_id, fuzz_id))
	       };

    # drop the tables, if case they are already there
    drop_tables();

    # grab the handle that the framework uses to create our tables
    my $dbh = PROP::DBH::get_handle();

    eval {
	foreach (@stmts) {
	    my $sth = $dbh->prepare($_);
	    $sth->execute();
	}
    };
    if(@$) {
	die "oops, we had a problem creating the tables: $@\n";
    }
}

sub drop_tables {
    PROP::Util::drop_table('Foo');
    PROP::Util::drop_table('Biz');
    PROP::Util::drop_table('Fuzz');
    PROP::Util::drop_table('LinkFooToBiz');
    PROP::Util::drop_table('LinkFooToFuzz');
}


package Foo;
use base qw/PROP::Object/;

sub get_table_name {
    return 'Foo';
}

package Biz;
use base qw/PROP::Object/;

sub get_table_name {
    return 'Biz';
}

package Fuzz;
use base qw/PROP::Object/;

sub get_table_name {
    return 'Fuzz';
}
