The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/env perl

use strict;
use warnings;
use autodie;
use utf8;

use Carp qw( croak );
use Cwd qw( abs_path );
use File::Basename qw( dirname );
use MaxMind::DB::Writer::Serializer;
use MaxMind::DB::Writer::Tree::InMemory;
use MaxMind::DB::Writer::Tree::File;
use Net::Works::Network;
use Test::MaxMind::DB::Common::Util qw( standard_test_metadata );

my $Dir = dirname( abs_path($0) );

sub main {
    my @sizes = ( 24, 28, 32 );
    my @ipv4_range = ( '1.1.1.1', '1.1.1.32' );

    my @ipv4_subnets = Net::Works::Network->range_as_subnets(@ipv4_range);

    foreach my $record_size (@sizes) {
        write_test_db(
            $record_size,
            \@ipv4_subnets,
            { ip_version => 4 },
            'ipv4',
        );
    }

    my @ipv6_subnets = Net::Works::Network->range_as_subnets(
        '::1:ffff:ffff',
        '::2:0000:0059'
    );

    foreach my $record_size (@sizes) {
        write_test_db(
            $record_size,
            \@ipv6_subnets,
            { ip_version => 6 },
            'ipv6',
        );

        write_test_db(
            $record_size,
            [
                @ipv6_subnets,
                Net::Works::Network->range_as_subnets( @ipv4_range, 6 ),
            ],
            { ip_version => 6 },
            'mixed',
        );
    }

    write_geoip2_city_db();

    write_test_serialization_data();
}

sub write_test_db {
    my $record_size     = shift;
    my $subnets         = shift;
    my $metadata        = shift;
    my $ip_version_name = shift;

    my $tree = MaxMind::DB::Writer::Tree::InMemory->new(
        ip_version => $subnets->[0]->version() );

    for my $subnet ( @{$subnets} ) {
        $tree->insert_subnet(
            $subnet,
            { ip => $subnet->first()->as_string() }
        );
    }

    my $writer = MaxMind::DB::Writer::Tree::File->new(
        tree        => $tree,
        record_size => $record_size,
        standard_test_metadata(),
        %{$metadata},
        alias_ipv6_to_ipv4 => ( $subnets->[0]->version() == 6 ? 1 : 0 ),
        map_key_type_callback => sub { 'utf8_string' },
    );

    my $filename = sprintf(
        "$Dir/MaxMind-DB-test-%s-%i.mmdb",
        $ip_version_name,
        $record_size,
    );
    open my $fh, '>', $filename;

    $writer->write_tree($fh);

    return ( $tree, $filename );
}

sub write_geoip2_city_db {
    my $tree = MaxMind::DB::Writer::Tree::InMemory->new( ip_version => 6 );

    my $json = JSON::XS->new()->utf8();

    open my $source_fh, '<', "$Dir/../source-data/GeoIP2-City-Test.json";
    while (<$source_fh>) {
        my ( $network, $record ) = @{ $json->decode($_) };
        $tree->insert_subnet(
            Net::Works::Network->new_from_string( string => $network ),
            $record
        );
    }
    close $source_fh;


    my %type_map = (
        cellular              => 'uint16',
        city                  => 'map',
        continent             => 'map',
        country               => 'map',
        geoname_id            => 'uint32',
        is_anonymous_proxy    => 'boolean',
        is_satellite_provider => 'boolean',
        latitude              => 'double',
        location              => 'map',
        longitude             => 'double',
        names                 => 'map',
        postal                => 'map',
        registered_country    => 'map',
        represented_country   => 'map',
        subdivisions          => [ 'array', 'map' ],
        traits                => 'map',
    );

    my $type_cb = sub { $type_map{ $_[0] } // 'utf8_string' };

    my $writer = MaxMind::DB::Writer::Tree::File->new(
        tree          => $tree,
        record_size   => 28,
        ip_version    => 6,
        database_type => 'GeoIP2 City Test Database',
        languages     => [ 'en', 'zh' ],
        description   => {
            en =>
                'GeoIP2 City Test Database (a small sample of real GeoIP2 data)',
            zh => '小型数据库',
        },
        alias_ipv6_to_ipv4    => 1,
        map_key_type_callback => $type_cb,
    );

    open my $output_fh, '>', "$Dir/GeoIP2-City-Test.mmdb";
    $writer->write_tree($output_fh);
    close $output_fh;

    return;
}

sub write_test_serialization_data {
    my $serializer = MaxMind::DB::Writer::Serializer->new(
        map_key_type_callback => sub { 'utf8_string' } );

    $serializer->store_data( map => { long_key  => 'long_value1' } );
    $serializer->store_data( map => { long_key  => 'long_value2' } );
    $serializer->store_data( map => { long_key2 => 'long_value1' } );
    $serializer->store_data( map => { long_key2 => 'long_value2' } );
    $serializer->store_data( map => { long_key  => 'long_value1' } );
    $serializer->store_data( map => { long_key2 => 'long_value2' } );

    open my $fh, '>', 'maps-with-pointers.raw';
    print {$fh} ${ $serializer->buffer() }
        or die "Cannot write to maps-with-pointers.raw: $!";
    close $fh;

    return;
}

main();