use strict;
use warnings;

use Test::More;
use Types::Numbers ':all';

use lib 't/lib';
use NumbersTest;

my @types = (
     SignedInt[4],   SignedInt[8],   SignedInt[16],   SignedInt[32],   SignedInt[64],   SignedInt[128],
   UnsignedInt[4], UnsignedInt[8], UnsignedInt[16], UnsignedInt[32], UnsignedInt[64], UnsignedInt[128],
    BlessedInt[2],  BlessedInt[3],  BlessedInt[ 5],  BlessedInt[10],  BlessedInt[20],  BlessedInt[ 39],
);

plan tests => scalar @types;

foreach my $type (@types) {
   my $name = $type->display_name;
   $name =~ /^(\w+)\[(\d+)\]/;
   my ($base, $bits) = ($1, $2);

   $bits = ceil($bits / _BASE2_LOG) if ($base eq 'BlessedInt');  # digits to bits

   subtest $name => sub {
      plan tests => ($base eq 'BlessedInt' ? 15 : 111);

      note explain {
         name => $name,
         inline => $type->inline_check('$num'),
      };

      # Common tests
      numbers_test( undef, $type, 0);
      numbers_test( 'ABC', $type, 0);
      numbers_test(   4.5, $type, 0);
      numbers_test(  $nan, $type, 0);
      numbers_test( $pinf, $type, 0);
      numbers_test( $ninf, $type, 0);
      numbers_test( $Inan, $type, 0);
      numbers_test($Ipinf, $type, 0);
      numbers_test($Ininf, $type, 0);

      numbers_test(     0, $type, $base ne 'BlessedInt');
      numbers_test(     1, $type, $base ne 'BlessedInt');
      numbers_test(    -1, $type, $base eq  'SignedInt');

      numbers_test(   $I0, $type, 1);
      numbers_test(   $I1, $type, 1);
      numbers_test(  $I_1, $type, $base ne 'UnsignedInt');

      # Specific limits
      return if ($base eq 'BlessedInt');   ### TODO: Could probably use some more tests for BlessedInt

      # (trying to minimize the level of automation while still keep some sanity...)
      foreach my $test_bits (4,8,16,24,32,48,64,128) {
         my $spos = 2 ** ($test_bits-1) - 1;  # 8-bit =  127
         my $sneg = -1 - $spos;               # 8-bit = -128
         my $upos = 2 ** $test_bits - 1;      # 8-bit =  255

         note "Bits = $test_bits";

         # -1 = global fail, 0 = use detail below, 1 = global pass
         my $pass = $bits <=> $test_bits;

         if ($base eq 'UnsignedInt') {
            numbers_test($spos+0, $type, $pass || 1);
            numbers_test($sneg-0, $type,          0);
            numbers_test($upos+0, $type, $pass || 1);
            numbers_test($spos+1, $type, $pass || 1);
            numbers_test($sneg-1, $type,          0);
            numbers_test($upos+1, $type, $pass || 0);
         }
         if ($base eq 'SignedInt') {
            numbers_test($spos+0, $type, $pass || 1);
            numbers_test($sneg-0, $type, $pass || 1);
            numbers_test($upos+0, $type, $pass || 0);
            numbers_test($spos+1, $type, $pass || 0);
            numbers_test($sneg-1, $type, $pass || 0);
            numbers_test($upos+1, $type, $pass || 0);
         }

         $pass = $bits <=> $test_bits;
         $spos = $bigtwo->copy ** ($test_bits-1) - 1;  # 8-bit =  127
         $sneg = -1 - $spos;                           # 8-bit = -128
         $upos = $bigtwo->copy ** $test_bits - 1;      # 8-bit =  255

         if ($base eq 'UnsignedInt') {
            numbers_test($spos+0, $type, $pass || 1);
            numbers_test($sneg-0, $type,          0);
            numbers_test($upos+0, $type, $pass || 1);
            numbers_test($spos+1, $type, $pass || 1);
            numbers_test($sneg-1, $type,          0);
            numbers_test($upos+1, $type, $pass || 0);
         }
         if ($base eq 'SignedInt') {
            numbers_test($spos+0, $type, $pass || 1);
            numbers_test($sneg-0, $type, $pass || 1);
            numbers_test($upos+0, $type, $pass || 0);
            numbers_test($spos+1, $type, $pass || 0);
            numbers_test($sneg-1, $type, $pass || 0);
            numbers_test($upos+1, $type, $pass || 0);
         }
      }
   } or diag explain {
      name => $name,
      inline => $type->inline_check('$num'),
   };
}

done_testing;
