#!/bin/bash -e

Target="$1"
: ${SrcHost:=localhost}
: ${SrcDB:=$PWD/gdb}
: ${DBUser:=sysdba}
: ${DBPass:=sysdba}
: ${Method:=direct}
: ${SSHUser:=$USER}

if [ -z "$Target" -a "$Arcs" = "" -a -e .arcs ]; then
    Arcs=1
fi
if [ "$Arcs" = 0 ]; then
    Arcs=
fi

datetime() { exec date +%Y-%m-%d_%H-%M-%S_%a; }

if [ -z "$Target" ]; then
    if [ -n "$Arcs" ]; then
        Target=.
    else
        Target=`datetime`
    fi
fi

if [ -n "$Arcs" ]; then
    lockfile .lock
    trap 'rm -f .lock' EXIT
    if [ "$Arcs" = 1 ]; then
        Arcs="arcs -q -N -i -T"
    fi
    DumpOpts="--wipe"
else
    set -o noclobber
fi

Target=`readlink -m "$Target"`
mkdir -p "$Target"

DB="$Target/gdb"
Gbak="$Target/gbak"
SQL="$Target/sql"
Dat="$Target/dat"
DatView="$Target/dat-view"
DatKVP="$Target/dat-kvp"
DatViewKVP="$Target/dat-view-kvp"
Gen="$Target/gen"

Auth="-user $DBUser -password $DBPass"
SrcHostDB="$SrcHost:$SrcDB"

DumpFromHost=localhost
DumpFromDB="$DB"

# for verbose mode, run with V=v
v() {
    echo "$@" >&2
    "$@"
}

# check what method/s to use
# $Method can be: direct, gbak, scp, here, direct-scp, direct-gbak, scp-only, gbak-only

case "$Method" in
*direct*)
    DumpFromHost="$SrcHost"
    DumpFromDB="$SrcDB"
    ;;
*)
    NeedLocalDB=1
    ;;
esac

case "$Method" in
*gbak*) 
    $V gbak $Auth "$SrcHost:$SrcDB" "$Gbak"
    ;;
esac

case "$Method" in
*scp*)
    $V scp "$SSHUser@$SrcHost:$SrcDB" "$DB"
    ;;
esac

if [ "$NeedLocalDB" = 1 -a ! -e "$DB" -a -e "$Gbak" ]; then
    $V gbak $Auth -CREATE_DATABASE "$Gbak" "$DB"
fi

if [ -e "$DB" ]; then
    chmod g+w "$DB"
fi

hide "$Gbak" "$DB"

case "$Method" in
*only*)
    exit
    ;;
esac


DumpFrom="$DumpFromHost:$DumpFromDB"


# dump the schema
$V isql $Auth -a "$DumpFrom" >"$SQL"

# dump the data
if [ -z "$NoDump" -a -z "$NoDumpTables" ]; then
    $V dbidump -q -S $DumpOpts -D "$Dat" -u "$DBUser" -p "$DBPass" -d "dbi:InterBase:database=_;host=$DumpFromHost;ib_dialect=3" dump "$DumpFromDB"
    if [ -n "$ProcessDump" ]; then
        $ProcessDump
    fi
fi

# dump generator values
$V echo 'show generators;' | isql $Auth "$DumpFrom" | sed 's/^Generator *//; s/, current value is /	/;' >"$Gen"

# remove unneeded stuff
if [ -n "$Clean" ]; then
    rm -f "$Gbak" "$DB" ."$Gbak" ."$DB"
fi

get_metadata() {

    # get a list of objects in the database, and a list per type, and each object's definition
    # does not include constraints / indexes defined with the table
    rm -rf obj list comment
    mkdir obj list comment
    <sql perl -ne '
        use IO::File;
        BEGIN { $term = ";"; }
        /set term (\S) (\S)/i and $term = $1;
        if (!$copy_to && /^(create|alter|declare|comment on) +(?:external )?(\w+)(?: index)? +"?(\w+)"?(?: (?:on|for) (\w+))?/i) {
            my ($cmd, $type, $name, $on) = map { lc } $1, $2, $3;
            if ($type eq "column") {
                # this test for view columns is dodgy!
                $type = $name =~ /^v_/i ? "view" : "table";
            }
            print "$type\t$name\t$on\n";
            mkdir "obj/$type";
            $type_fh{$type} ||= IO::File->new("| sort -u >list/$type");
            $type_fh{$type}->print("$name\t$on\n");
            my $obj = "obj";
            if ($cmd =~ /comment/i) {
                $obj = "comment";
                mkdir "comment/$type";
            }
            $copy_to = IO::File->new("$obj/$type/$name", "a");
        }
        if ($copy_to) {
            $copy_to->print($_);
            if (/\Q$term\E$/) {
                undef $copy_to;
            }
        } else {
            print STDERR $_;
        }
    ' 2>sql-unparsed | sort -u >list/all

    # list of table fields
    cat obj/table/* |
    perl -ne '
        if (/^create table (\w+)/i) {
            $table = $1
        } elsif (!/^ /) {
            undef $table;
        }
        if ($table) {
            s/,$//;
            ($field, $type, $opt) = /^(?: +|.*?\()(\S+) (\S+)(?: (.*))?/;
            print lc "$table\t$field\t$type\n";
        }
    ' >fields

    # list of tables and domains in the environment
    export Tables=`ls obj/table`
    export Domains=`ls obj/domain`

    # dependencies
    (

    # table dependencies - foreign keys
    cat obj/table/* |
    perl -ne '/ALTER TABLE (\S+) ADD CONSTRAINT \S+ FOREIGN KEY \(.*?\) REFERENCES (\S+)/ and print lc "$1 $2\n";'

    # table dependencies - domains
    <fields perl -ne '
        BEGIN { %domain = map {lc($_)=>1} split /\s+/, $ENV{Domains}; }
        chomp;
        ($table, $field, $type) = split /\t/, $_;
        print "$table\tdomain.$type\n" if $domain{$type};
    '

    # domain dependencies
    cat obj/domain/* |
    perl -ne '
        BEGIN { %table = map {lc($_)=>1} split /\s+/, $ENV{Tables}; }
        /^create domain (\w+)/i and $obj = lc $1;
        if (/from (.*)/) { my $x = lc $1; my @matches = $x =~ /(?:^|,)\s*(\w+)/g; print "domain.$obj $_\n" for grep {$table{lc $_}} @matches; }
        if (/join\s*(\w+)/) { print lc "domain.$obj $1\n" if $table{lc $1}; }
    '

    # table check dependencies (like domains)
    cat obj/table/* |
    perl -ne '
        BEGIN { %table = map {lc($_)=>1} split /\s+/, $ENV{Tables}; }
        /^create table (\w+)/i and $obj = lc $1;
        if (/from (.*)/) { my $x = lc $1; my @matches = $x =~ /(?:^|,)\s*(\w+)/g; print "$obj $_\n" for grep {$table{lc $_}} @matches; }
        if (/join\s*(\w+)/) { print lc "$obj $1\n" if $table{lc $1}; }
    '

    # table dependencies - self - to ensure all tables appear in the output of tsort
    for table in $Tables; do echo $table $table; done

    ) | sort -u >dep

    # sort tables in dependency order
    <dep tsort | grep -v '^domain\.' | tac >table-order

}

$V get_metadata

# dump views
if [ -z "$NoDump" -a -z "$NoDumpViews" ]; then
    dump_views() {
        dbidump -q -S $DumpOpts -D "$DatView" -u "$DBUser" -p "$DBPass" -d "dbi:InterBase:database=_;host=$DumpFromHost;ib_dialect=3" dump "$DumpFromDB" -t `ls obj/view`
    }
    $V dump_views
fi

# convert dumped data to kvp format, for easier diffing (takes more space though)
if [ -z "$NoKVP" -a -z "$NoDump" ]; then
    dump_to_kvp() {
        mkdir -p "$DatKVP" "$DatViewKVP"
        (cd "$Dat"; for A in *; do <"$A" dbidump-to-kvp >"$DatKVP/${A%.dump}.kvp"; done)
        if [ -z "$NoDumpViews" ]; then
            (cd "$DatView"; for A in *; do <"$A" dbidump-to-kvp >"$DatViewKVP/${A%.dump}.kvp"; done)
        fi
    }
    $V dump_to_kvp
fi

# run arcs
if [ -n "$Arcs" ]; then
    sleep 1.1
    $V $Arcs
    rm -f .lock
fi

