package Tools;

@ISA = qw(Exporter);

use FindBin qw($RealBin);
use lib sprintf("%s", $RealBin);
use lib sprintf("%s/../lib", $RealBin);

use Data::Dumper;
$Data::Dumper::Indent = 1;
#$Data::Dumper::Maxdepth = 2;

use IO::File;
use Socket;

our $STACK      = 1;
our $VERBOSE    = 0;
our $LOG        = sub{ };

@EXPORT = qw(&datum &stackTrace &lg &debug &error &panic &rep2str &dumper &getFromSocket &fields &load_file &save_file &tableExists &buildsearch &deleteDir &getip &convert);

# ------------------
sub datum {
# ------------------
    my $zeit = shift  || time;
    my $typ  = shift  || 'voll';
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
            localtime($zeit);

    if(lc($typ) eq 'voll') {
        return sprintf('%02d:%02d:%02d %02d.%02d.%04d',
            $hour, $min, $sec, $mday, $mon+1, $year+1900);
    } elsif(lc($typ) eq 'tag') {
        return sprintf('%02d.%02d.%04d',
            $mday, $mon+1, $year+1900);
    } else {
        return sprintf('%02d:%02d:%02d',
            $hour, $min, $sec);
    }
}

#returns a string containg the stacktrace
# ------------------
sub stackTrace {
# ------------------
    my $result;
    my $l;
    my $s;
    my $n = 0;
    $result .= "=========== top of stack =========\n";
    do {
        my ($package, $filename, $line, $subroutine) = caller($n++);
        $l = $s;
        $s = sprintf("%s:%s (%s)", $filename||"", $line||"", $subroutine||"");
        if ($s ne ":") {
            $result .= "$s\n";
        }
    } while (!defined $l || $s ne $l);
    $result .= "=========== end of stack =========\n";
}

# ------------------
sub lg {
# ------------------
    my $msg = shift;
    my $lev = shift || 5;

    return 1 if($VERBOSE < $lev);

    $msg = sprintf('ERR:202 %s', $msg)
        unless($msg =~ /^ERR:\d{3}/);

    $msg .= &stackTrace
        if($VERBOSE > 5);

    my ($package, $filename, $line, $subroutine) = caller(3);

    my  $module = '';
        $module = (split('::', $package))[-1]
            if($package);

    &{$LOG}(sprintf('%s: %s', $module, $msg));

    return 1;
}

# ------------------
sub debug {
# ------------------
    my $msg = shift;

    &lg(sprintf('ERR:250 %s', $msg), 2);

    return 1;
}

# ------------------
sub error {
# ------------------
    my $msg = shift;

    &lg(sprintf('ERR:501 %s', $msg), 1);

    return undef;
}

# ------------------
sub panic {
# ------------------
    my $msg = shift;

    &lg(sprintf('ERR:550 %s', $msg), 1);

    return undef;
}

# ------------------
sub getFromSocket {
# ------------------
    my $sock = shift or return undef;

    my (@Data, $Line);
    my $len = 0;
	do {
		$Line = <$sock> || 0;
        $len += length($Line);
		$Line =~ s/\r\n//g;
		push(@Data, $Line);
	} while($Line);

    lg sprintf('Get Data from socket: %s',
            convert($len),
        );

    return (\@Data, $len);
}


# ------------------
sub rep2str {
# ------------------
    my %args = @_;
    my $text = $args{-text};
    $text =~ s/\<(.+?)\>/$args{"-$1"}/sig;
    return $text;
}

# ------------------
sub dumper {
# ------------------
    my $var = shift || '<undef>';
    my $args = @_;

    $Data::Dumper::Maxdepth = $args->{d}
        if($args->{d});
    $Data::Dumper::Indent = $args->{i}
        if($args->{d});

    debug Dumper( $var );
}

# ------------------
sub fields {
# ------------------
    my $dbh = shift || return error ('No DBH!' );
    my $str = shift || return error ('No SQL!');

    $str =~ s/order\s+by.+//sig;
    my $sql = sprintf('%s %s 0 = 1', $str, ($str =~ /where/i ? 'AND' : 'WHERE'));
	my $sth = $dbh->prepare($sql) or return error("$DBI::errstr - $sql");
	$sth->execute or return error("$DBI::errstr - $sql");
	my $fields = $sth->{'NAME'};
    return $fields;
}

# ------------------
sub tableExists {
# ------------------
    my $dbh = shift || return error ('No DBH!' );
    my $name = shift || return error ('No Table!');

    my $erg = $dbh->selectall_arrayref('show tables');
    for(@$erg) {
        return 1 if($name eq $_->[0]);
    }
    return 0;
}

#--------------------------------------------------------
sub load_file {
#--------------------------------------------------------
	my $file = shift || die "Kein File bei Loader $!";

    lg sprintf('Load file "%s" from system',
            $file,
        );

	my $fh = IO::File->new("< $file")
	    or return error("Can't open File $file $! ");
	my $data;
	while ( defined (my $l = <$fh>) ) {
	        $data .= $l;
	}
	$fh->close;
	return $data;
}

#--------------------------------------------------------
sub save_file {
#--------------------------------------------------------
    my ($file, $data) = @_;
	return unless($file);

    lg sprintf('Save %s in file "%s"',
            $file,
            convert(length($data))
        );

	my $fhi = new IO::File("> $file")
	    or return error("Can't write!");
	print $fhi $data;
	$fhi->close;

    return $file
}


#--------------------------------------------------------
sub buildsearch {
#--------------------------------------------------------
    my ($InFields, $Search) = @_;
    my $ss = {};
    foreach my $su (split(/\s*,\s*/, $Search)) {
        $su =~ s/\'/\./sg;

        # Split inFields and make a sql where clausel
        # if field with minus at top, then not rlike
        foreach my $f (split(/\s*,\s*/, $InFields)) {
            my $searchtext = $su;
            if($searchtext =~ s/^\-//) {
                push(@{$ss->{$su}}, "$f NOT RLIKE '$searchtext'");
            } else {
                push(@{$ss->{$su}}, "$f RLIKE '$searchtext'");
            }
        }
    }

    my @fieldsearch;
    foreach my $su (keys %$ss) {
        my $type = ($su =~ /^\-/) ? 'AND' : 'OR';
        push(@fieldsearch, "(" . join(" $type ", @{$ss->{$su}}) . ")");
    }
	return join('AND ', @fieldsearch);
}

# ------------------
sub deleteDir {
# ------------------
    my $dir = shift || return;

    lg sprintf('Delete directory "%s" in the system',
            $dir,
        );

    foreach my $file (glob(sprintf('%s/*', $dir))) {
        deleteDir($file)
            if(-d $file);
        unlink $file;
    }
    rmdir $dir;
    return 1;
}

# ------------------
sub getip {
# ------------------
    my $handle = shift  || return error ('No Handle!' );
    my $p = getpeername($handle);
    my($port, $iaddr) = unpack_sockaddr_in($p);
    my $ip = inet_ntoa($iaddr);
    lg sprintf('Read IP from handle: %s',
            $ip,
        );
    return $ip;
}

# Takes kilobytes and formats for MB and GB if necessary
sub convert {
    my $kbytes = $_[0] / 1024;
    my $result = 0;

    if ( $kbytes > 1048576 ) {
        $result = sprintf("%.2f", $kbytes / 1048576);
        $result .= " GB";
    } elsif ( $kbytes > 1024 ) {
        $result = sprintf("%.2f", $kbytes / 1024);
        $result .= " MB";
    } else {
        $result = sprintf("%.2f", $kbytes);
        $result .= " KB";
    }

    return $result;
}


1;
