use utf8;
use strict;
use Dbase::Help qw(DoFn);
use Dbase::Globals qw(get_ipnum);
use Dbase::IP;
use warnings; no warnings qw(redefine);

## Die Funktion wird für diverse Adressschnipsel aufgerufen.
# Flag&1    Aufruf für freie Bereiche (0,adresse)
# Flag&2    Aufruf für belegte Bereiche (id,adresse)
# Flag&8    Aufruf des enthaltenden Bereichs
# Flag&16   rekursiver Abstieg in belegte Bereiche
# Flag&32   Aufruf für Netz- oder Broadcastadressen (-1,adresse)
# Flag&64   intern (bei Rekursion): nur direkt übergeordnete Bereiche ausgeben
#
## Aufruf mit:
## &$func($id,$adr,$level,$ziel);
## ID=0 (freie Netze/Adressen) oder -1 (Netz- und Broadcastadresse)
## Level: bei rekursive Abstieg jeweils +1, zwecks Ausdruckens von Einrückungen etc.
## Ziel: des betreffenden bzw. übergeordneten Bereichs, kann undef sein

sub min($$) {
	my($a,$b) = @_;
	($a<$b) ? $a : $b;
}
sub walk_ip($$;$$);
sub walk_ip($$;$$) {
	my($net,$func,$flag,$level) = @_;
	$flag=0 unless $flag;
	$level=0 unless $level;
	my $steps=0;

	if($flag&(8|64)) {
		my $id = get_ipnum($net);
		if($id) {
			my($adr,$bit,$ziel)=DoFn("select ip6,bits,dest from ipkunde where id=$id");
			$adr = Dbase::IP->new_db($adr,$bit);
			#print STDERR "Call1 with $adr\n" if $flag&8 or $bit==$net->db_bits;
			return undef if ($flag&8 or $bit==$net->db_bits) and not defined &$func($id,$adr,$level,$ziel);
		}
	}
	my $addr = $net->bitmask(0);
	my $last = $net+1;

	while($addr != $last) {
		my($id,$fid,$mxb,$ziel) = DoFn("select id,ip6,bits,dest from ipkunde where ${\ $net->dbsub(undef,$addr) } and (ende is NULL or ende > UNIX_TIMESTAMP(NOW())) order by ip6,bits desc limit 1");
		if(defined $mxb) {
			$fid = Dbase::IP->new_db($fid,$mxb);
		} else {
			$fid = $last;
		}
		if($flag&(1|32) and $addr<$fid) {
			while($addr < $fid) {
				$addr = $addr->bitmask(min($addr->min_mask,$net->db_bits));
				# TODO: der nächste Block ist noch etwas ineffizient
				while (($addr+1)->bitmask(0) > $fid->bitmask(0) and $addr->db_bits) {
					$addr = $addr->bitmask($addr->db_bits-1);
				}
				$steps++;
				if($addr->db_bits == 0) { ## finde raus ob erste/letzte in einem Netzbereich
					my(undef,$upadr) = get_ipnum($addr);
					#print STDERR "UpAddr $addr => $upadr\n" if defined $upadr;
					if($upadr and $upadr->db_bits > 1 and ($upadr->networkaddr->{addr} eq $addr->{addr} or $upadr->broadcastaddr->{addr} eq $addr->{addr})) {
						#print STDERR "Call2 with $addr\n" if $flag&32;
						return undef if $flag&32 and not defined &$func(-1,$addr,$level+1,$ziel);
						$addr++;
						next;
					}
					#print STDERR "Call3 with $addr on $upadr\n" if $flag&1;
				} else {
					#print STDERR "Call3 with $addr\n" if $flag&1;
				}
				return undef if $flag&1 and not defined &$func(0,$addr,$level+1,$ziel);
				$addr++;
			}
		}
		last if $last == $fid;
		#print STDERR "Call4 with $fid\n" if $flag&2;
		return undef if $flag&2 and not defined &$func($id,$fid,$level+1,$ziel);
		if($flag&16 and $mxb) {
			my $res;
			$flag = ($flag&~8)|64 if $flag&8;
			$res = walk_ip($fid->bitmask($mxb-1),$func,$flag,$level+1);
			return undef unless defined $res;
			$steps += $res;
			$res = walk_ip($fid->bitmask($mxb-1)+1,$func,$flag,$level+1);
			return undef unless defined $res;
			$steps += $res;
		}
		$addr = ($fid+1)->bitmask(0);
		$steps++;
	}
	$steps;
}
1;
