#!/usr/bin/env perl

use strict;
use warnings;

our $home;

BEGIN {
  use FindBin;
  FindBin::again();

  $home = ($ENV{NETDISCO_HOME} || $ENV{HOME});

  # try to find a localenv if one isn't already in place.
  if (!exists $ENV{PERL_LOCAL_LIB_ROOT}) {
      use File::Spec;
      my $localenv = File::Spec->catfile($FindBin::RealBin, 'localenv');
      exec($localenv, $0, @ARGV) if -f $localenv;
      $localenv = File::Spec->catfile($home, 'perl5', 'bin', 'localenv');
      exec($localenv, $0, @ARGV) if -f $localenv;

      die "Sorry, can't find libs required for App::Netdisco.\n"
        if !exists $ENV{PERLBREW_PERL};
  }
}

BEGIN {
  use Path::Class;

  # stuff useful locations into @INC and $PATH
  unshift @INC,
    dir($FindBin::RealBin)->parent->subdir('lib')->stringify,
    dir($FindBin::RealBin, 'lib')->stringify;

  use Config;
  $ENV{PATH} = $FindBin::RealBin . $Config{path_sep} . $ENV{PATH};
}

use App::Netdisco;
use Dancer ':script';
use Dancer::Plugin::DBIC 'schema';

use HTTP::Tiny;
use Text::CSV 'csv';
use Math::BigInt;

binmode STDOUT, ":utf8";

my %urls = (
  MAL => 'https://raw.githubusercontent.com/netdisco/upstream-sources/master/ieee/MA/MA-L.csv',
  MAM => 'https://raw.githubusercontent.com/netdisco/upstream-sources/master/ieee/MA/MA-M.csv',
  MAS => 'https://raw.githubusercontent.com/netdisco/upstream-sources/master/ieee/MA/MA-S.csv',
);

my %oui = ();

foreach my $MA (sort keys %urls) {
    my $resp = HTTP::Tiny->new->get($urls{$MA});
    my $content = $resp->{content};
    my $aoh = csv( in => \$content, headers => 'auto', encoding => 'UTF-8' );
    foreach my $row (@$aoh) {
        next if $row->{'Organization Name'} eq 'IEEE Registration Authority';
        next if exists $oui{ lc $row->{'Assignment'} };

        $row->{abbrev} = shorten($row->{'Organization Name'});

        $row->{base} = lc $row->{'Assignment'};
        $row->{bits} = length($row->{base}) * 4;

        $row->{first} = $row->{'Assignment'} . '0' x ( 12 - length( $row->{'Assignment'} ) );
        $row->{last}  = $row->{'Assignment'} . 'F' x ( 12 - length( $row->{'Assignment'} ) );

        $row->{range} = '['. Math::BigInt->from_hex($row->{first})->as_int()
          .','. Math::BigInt->from_hex($row->{last})->as_int() .']';

        $oui{ $row->{base} } = $row;
    }
}

# roll everything back if we're testing
my $txn_guard = $ENV{ND2_DB_ROLLBACK}
  ? schema('netdisco')->storage->txn_scope_guard : undef;

schema('netdisco')->txn_do(sub{
    schema('netdisco')->resultset('Manufacturer')->delete;

    schema('netdisco')->resultset('Manufacturer')->populate([
        map {{
            company => $oui{$_}->{'Organization Name'},
            abbrev  => $oui{$_}->{abbrev},
            base    => $oui{$_}->{base},
            bits    => $oui{$_}->{bits},
            first   => $oui{$_}->{first},
            last    => $oui{$_}->{last},
            range   => $oui{$_}->{range},
        }} sort keys %oui
    ]);

    schema('netdisco')->storage->dbh_do(
      sub {
        my ($storage, $dbh, @args) = @_;

        local $dbh->{TraceLevel} =
            ($ENV{DBIC_TRACE} ? '1|SQL' : $dbh->{TraceLevel});

        $dbh->do(q{
            UPDATE node SET oui = (
              SELECT base FROM manufacturer
                WHERE ('x' || lpad( translate( mac::text, ':', ''), 16, '0')) ::bit(64) ::bigint <@ range
              LIMIT 1
            )
        });
      },
    );
});

exit 0;

# This subroutine is based on Wireshark's make-manuf
# http://anonsvn.wireshark.org/wireshark/trunk/tools/make-manuf
sub shorten {
    my $manuf = shift;

    #$manuf = decode("utf8", $manuf, Encode::FB_CROAK);
    $manuf = " " . $manuf . " ";

    # Remove any punctuation
    $manuf =~ tr/',.()/    /;

    # & isn't needed when Standalone
    $manuf =~ s/ \& / /g;

    # remove junk whitespace
    $manuf =~ s/\s+/ /g;

    # Remove any "the", "inc", "plc" ...
    $manuf
        =~ s/\s(?:the|inc|incorporated|plc|systems|corp|corporation|s\/a|a\/s|ab|ag|kg|gmbh|co|company|limited|ltd|holding|spa)(?= )//gi;

    # Convert to consistent case
    $manuf =~ s/(\w+)/\u\L$1/g;

    # Deviating from make-manuf for HP
    $manuf =~ s/Hewlett[-]?Packard/Hp/;

    # Truncate all names to first two words max 20 chars
    if (length($manuf) > 21) {
        my @twowords = grep {defined} (split ' ', $manuf)[0 .. 1];
        $manuf = join ' ', @twowords;
    }

    # Remove all spaces
    $manuf =~ s/\s+//g;

    #return encode( "utf8", $manuf );
    return $manuf;
}

__DATA__

    0C15C5000000   {
        Assignment               "0C15C5",
        bits                     24,
        first                    "0C15C5000000",
        last                     "0C15C5FFFFFF",
        "Organization Address"   "167, Churye-2Dong, Sasang-Gu, Busan   KR 617-716 " (dualvar: 167),
        "Organization Name"      "SDTEC Co., Ltd.",
        oui                      "0c:15:c5",
        Registry                 "MA-L",
        abbrev                   "Sdtec"
    },

    [98] {
             Assignment               "8C1F64117" (dualvar: 8),
             "Organization Address"   "Spinnereistrasse 10 St. Gallen  CH 9008 ",
             "Organization Name"      "Grossenbacher Systeme AG",
             Registry                 "MA-S"
         },
