#!/usr/bin/perl -w
#
#
use strict;

use DBIx::DataSource qw( create_database drop_database );
use lib "../lib";
use DBUTIL;
use Netdot::Config;
use Netdot::Meta::SQLT;
$|=1; #unbuffer output.

my %config;

$config{ACTION}       = $ARGV[0] || "";
$config{DEBUG}        = 1;
$config{PROMPT}       = 1; 
$config{CONFIG_DIR}   = '../etc';
$config{SCHEMA_FILE}  = "$config{CONFIG_DIR}/netdot.meta";
$config{DEFAULT_DATA} = "$config{CONFIG_DIR}/default_data";

my $netdot_config = Netdot::Config->new(config_dir => $config{CONFIG_DIR});

foreach my $var ( qw /DB_TYPE DB_HOME DB_HOST DB_PORT DB_DBA DB_DBA_PASSWORD 
		  DB_NETDOT_USER DB_NETDOT_PASS DB_DATABASE/ ){
    $config{$var} = $netdot_config->get($var);
}

$config{BINDIR} = "$config{DB_HOME}/bin";

if ($config{DEBUG}) {
    print "Config parameters:\n";
    foreach my $var ( keys %config ){
	print $var, " = ", $config{$var}, "\n";
    }
}
my $dsn = "dbi:$config{DB_TYPE}:";

$dsn .= "dbname=$config{DB_DATABASE}";
if ($config{DB_HOST}) {
    $dsn .= ";host=$config{DB_HOST}";
}
if ($config{DB_PORT}) {
    $dsn .= ";port=$config{DB_PORT}";
}

if ($config{ACTION} eq 'initdb' ) {
    &init_db();
}elsif ($config{ACTION} eq 'dropdb' ) {
    &drop_db();
}elsif ($config{ACTION} eq 'generate') {
    &generate_schema_file();
}elsif ($config{ACTION} eq 'oui') {
    &insert_oui();
}else {
    die '$config{ACTION} invalid: '.$config{ACTION} ;
}


##################################################
# This does it all at once, in the right order
sub init_db {
    if ( $config{DB_TYPE} eq "mysql" ){
	&create_db();
	&initacls_mysql();
	&generate_schema_file();
	&insert_schema();
	
    }elsif ( $config{DB_TYPE} eq "Pg" ){
	&create_db();
	&generate_schema_file();
	&insert_schema();
	&initacls_Pg();
    }else{
	die '$config{DB_TYPE} invalid: '.$config{DB_TYPE} ;
    }
    &insert_default_data();
    print "Database initialized successfully\n";
}

##################################################
sub drop_db {
    
    if ( $config{PROMPT} ) {
	print <<END;
	
About to drop $config{DB_TYPE} database $config{DB_DATABASE}.
WARNING: This will erase all data in $config{DB_DATABASE}.
END
        exit unless _yesno();
    }
    
    print "\nDropping $config{DB_TYPE} database $config{DB_DATABASE}.\n";
    drop_database( $dsn, $config{DB_DBA}, $config{DB_DBA_PASSWORD} )
	or warn $DBIx::DataSource::errstr;
}

##################################################
sub generate_schema_from_metadata {
    # SQLT uses slightly different names for the db types
    my $dbtype;
    $dbtype = 'MySQL' if ( $config{DB_TYPE} eq 'mysql');
    $dbtype = 'PostgreSQL' if ( $config{DB_TYPE} eq 'Pg');

    my $sqlt   = Netdot::Meta::SQLT->new(meta_file=>"$config{CONFIG_DIR}/$config{SCHEMA_FILE}");
    my $schema = $sqlt->sql_schema($dbtype);
    my @schema = grep /[^\n]/, split /\n/, $schema;
    
    return (@schema);
}


##################################################
sub generate_schema_file {
    my @schema = &generate_schema_from_metadata();
    print "Generating schema for $config{DB_TYPE}...";
    
    my $file = "$config{CONFIG_DIR}/schema.$config{DB_TYPE}";

    system('mv', "$file", "$file.bak") if ( -f $file );

    open(SCHEMA, ">$file");
    foreach ( @schema ) {
	print SCHEMA "$_\n";
    }
    close(SCHEMA);
    print "done.\n";
}


##################################################
sub insert_schema {
    my @schema;
    print "\nCreating database schema.\n";
    my $file = "$config{CONFIG_DIR}/schema.$config{DB_TYPE}";

    if ( -f $file){
	open (SCHEMA, "<$file");
	foreach (<SCHEMA>) {
	    push @schema, $_;
	}	
    }else {
	@schema = &generate_schema_from_metadata();
    }
    
    &db_query(\@schema);
    print "Schema inserted sucessfully\n";
}


##################################################
#
sub create_db {
    print "\nCreating $config{DB_TYPE} database $config{DB_DATABASE}.\n";
    create_database( $dsn, $config{DB_DBA}, $config{DB_DBA_PASSWORD} )
	or die $DBIx::DataSource::errstr;
}


##################################################
#
sub _yesno {
    print "Proceed [y/N]:";
    my $x = scalar(<STDIN>);
    $x =~ /^y/i;
}

##################################################
#
sub initacls_mysql {
    print "Setting up privileges for MySQL.\n";
    my @acl;
    push @acl, "DELETE FROM user WHERE user LIKE '$config{DB_NETDOT_USER}';";
    push @acl, "DELETE FROM db WHERE db LIKE '$config{DB_DATABASE}';";
    push @acl, "GRANT SELECT,INSERT,CREATE,INDEX,UPDATE,DELETE ON $config{DB_DATABASE}.* TO $config{DB_NETDOT_USER}\@$config{DB_HOST} IDENTIFIED BY '$config{DB_NETDOT_PASS}';";
 
   &db_query(\@acl, 'mysql');
    system ("$config{BINDIR}/mysqladmin --host=$config{DB_HOST} --port=$config{DB_PORT} --user=$config{DB_DBA} -p$config{DB_DBA_PASSWORD} reload");
    
}


##################################################
# Generate the ACLs for PostgreSQL
# We don't have wildcards like in MySQL ;-(
# so we have to look in the schema file first
# 
# TODO: Test this.  It might be unnecessary
#
sub initacls_Pg {

    my $schema = "$config{CONFIG_DIR}/schema.Pg";
    my @acl;

    push @acl, "
DROP USER IF EXISTS $config{DB_NETDOT_USER};
CREATE USER $config{DB_NETDOT_USER} WITH PASSWORD '$config{DB_NETDOT_PASS}' NOCREATEDB NOCREATEUSER;
";

    print "Now building ACL's for postgres\n";
    
    open (SCHEMA, $schema)
	or die "Couldn't open $schema: $!\n";
    
    while (<SCHEMA>){
	if ( /CREATE TABLE "(\w+)"/ ){
	    push @acl, "GRANT SELECT, INSERT, UPDATE, DELETE ON $1 TO $config{DB_NETDOT_USER};\n";
	    push @acl, "GRANT SELECT, INSERT, UPDATE, DELETE ON $1_id_seq to $config{DB_NETDOT_USER};\n";
	}
    }

    &db_query(\@acl);
}

##################################################
#
sub insert_default_data{
    my @data;
    my $file = "$config{CONFIG_DIR}/$config{DEFAULT_DATA}";
    if ( -f $file) {
	print "Inserting default data\n";
	open (DEFAULT, "<$file") or die "Can't open $file: $!";
	foreach (<DEFAULT>) {
	    next unless ( /\w+/ );
	    if ( /INSERT INTO (\w+)/ ){
		my $t = $1;
		$t = lc($t);
		s/INSERT INTO \w+/INSERT INTO $t/;
	    }
	    push @data, $_;
	}	
    }else{
	die "Can't find $file";
    }
    &db_query(\@data);
}

##################################################
#
sub insert_oui{
    my $oui_file = "oui.txt";
    unless ( -r $oui_file ){
        print "  $oui_file not found! Using default file in package.\n";
	$oui_file = "oui.txt.default";
    }
    unless ( -r $oui_file ){
        print "  $oui_file not found!\n";
        die "Please run ''make oui'' to download oui.txt.\n";
    }
    my @data;
    print "Removing old contents of oui table in database.\n";
    push @data, "DELETE FROM oui WHERE true;";
    &db_query(\@data);

    my %oui;
    open (OUI, "<:encoding(iso-8859-1)","$oui_file") or die "Can't open $oui_file: $!\n";
    while (my $line = <OUI>){
        chomp $line;
        if ($line =~ /^([0-9A-F]{6})\s+\(base 16\)\s+(.*)\s*$/i){
            $oui{$1} = $2;
        }
    }
    close (OUI);

    my $oui_count = 0;
    @data = ();
    foreach my $oui ( keys %oui ){
        my $vendor = $oui{$oui};
	$vendor =~ s/\'/''/g;
        $oui = uc($oui);
        print "$oui : $vendor\n" if $config{DEBUG};
	push @data, "INSERT INTO oui (oui, vendor) VALUES ('$oui', '$vendor');";
        $oui_count++;
    }
    &db_query(\@data);
    print "Inserted $oui_count entries from oui.txt\n";

}
##################################################
# $query must be an arrayref
# 
sub db_query{
    my ($query, $database) = @_;
    $database ||= $config{DB_DATABASE};
    if ( my $dbh = &dbconnect($config{DB_TYPE}, $config{DB_HOST}, $config{DB_PORT}, 
			      $config{DB_DBA}, $config{DB_DBA_PASSWORD}, $database) ){

	die "Error with db query" unless &processdata($dbh, $query);
	&dbdisconnect($dbh);
    }
}

