#!/usr/bin/perl

use strict;
use warnings;

use File::Spec;

my $datadir = File::Spec->catfile($ENV{'HOME'}, '/lib/mobile-cidr');
my $libdir = File::Spec->catfile($ENV{'HOME'}, '/lib/mobile-cidr');
mkdir $datadir;
mkdir $libdir;
my $perl_code = File::Spec->catfile($libdir, "mobile_ip_test.pl");
my $php_code = File::Spec->catfile($libdir, "mobile_ip_test.php");

use YAML;
use Module::Pluggable::Fast name => 'components', search => ['WWW::MobileCarrierJP'], require => 'yes';

my %carriers = ();
for my $module (components()) {
	if ($module !~ m/CIDR/) {
		next;
	}
    my $fname = $module;
    $fname =~ s/^WWW::MobileCarrierJP:://;
    $fname =~ s/::/-/g;
    $fname = lc $fname;
	my $scrape = $module->scrape();

	my $carrier = $fname;
	$carrier =~ s/-cidr//;
	$carriers{$carrier} = $scrape;
    YAML::DumpFile(
        File::Spec->catfile($datadir, "$fname.yaml"),
        $scrape
    );
}


use Net::IPAddress;

sub expand {
	my ($cidr, $carrier) = @_;
	my $num = ip2num($cidr->{ip});

	my $subnet = $cidr->{subnetmask};
	$subnet =~ s/\///;
	my ($mask, $lower, $upper);
	if ($subnet != 32) {
		$mask = mask(0xFFFFFFFF, $subnet);
		$lower = $num & $mask;
		$upper = $num | ~$mask;
	}
	else {
		$lower = $upper = $num;
	}

	return [ $lower, $upper, $carrier ];
}

my @cidrs = ();
foreach my $c (keys(%carriers)) {
	foreach my $cidr (@{$carriers{$c}}) {
		push(@cidrs, expand($cidr, $c));
	}
}

{
	# sort and merge
	my @tmp = sort({ $a->[0] <=> $b->[0] } @cidrs);
	@cidrs = ();
	push(@cidrs, shift(@tmp));
	while (scalar(@tmp)) {
		my $t = pop(@cidrs) || shift(@tmp);
		my $n = shift(@tmp);
		if ((($t->[1] + 1) == $n->[0]) && ($t->[2] eq $n->[2])) {
			push(@cidrs, [ $t->[0], $n->[1], $t->[2] ]);
		}
		else {
			push(@cidrs, $t, $n);
		}
	}
}

{
	# output to csv
	open(my $fh, '> ' . File::Spec->catfile($datadir, "cidrs.csv"));
	foreach my $c (@cidrs) {
		print($fh join(',', @$c) . "\n");
	}
}

# output php/perl code
sub print_ip {
	my ($fh, $start, $end, $indent) = @_;
	my $half = int(($start + $end) / 2);
	my $t = "\t" x $indent;

	my $print_cond = sub {
		my $c = shift;
		if ($c->[0] == $c->[1]) {
			print($fh $t . "if (\$ip == $c->[0]) {\n");
			print($fh $t . "	return '$c->[2]';\n");
			print($fh $t . "}\n");
		}
		else {
			print($fh $t . "if ((\$ip >= $c->[0]) && (\$ip <= $c->[1])) {\n");
			print($fh $t . "	return '$c->[2]';\n");
			print($fh $t . "}\n");
		}
	};

	if ($start == $end) {
		&$print_cond($cidrs[$start]);
		return;
	}
	elsif (($end - $start) == 1){
		&$print_cond($cidrs[$start]);
		&$print_cond($cidrs[$end]);
		return;
	}

	my $half_c = $cidrs[$half];
	print($fh $t . "if (\$ip <= $half_c->[1]) {\n");
	&print_ip($fh, $start, $half, $indent + 1);
	print($fh $t . "} else {\n");
	&print_ip($fh, $half + 1, $end, $indent + 1);
	print($fh $t . "}\n");
}

if ($perl_code) {
	open(my $fh, "> $perl_code");
	print($fh <<__EOH__);
sub mobile_ip_test {
	my \$ip = shift;
__EOH__
	&print_ip($fh, 0, scalar(@cidrs) - 1, 1);
	print($fh <<__EOH__);
	return '';
}
1;
__EOH__
}

if ($php_code) {
	open(my $fh, "> $php_code");
	print($fh <<__EOH__);
<?php
function mobile_ip_test(\$ip) {
	if (! is_numeric(\$ip)) {
		\$ip = sprintf('%u', ip2long(\$ip));
	}
__EOH__
	&print_ip($fh, 0, scalar(@cidrs) - 1, 1);
	print($fh <<__EOH__);
	return '';
}
?>
__EOH__
}
