#!perl

use strict;
use warnings;

use Test::More;

use_ok 'CPU::Z80::Disassembler::Labels';

sub by_addr { $a->addr <=> $b->addr }

isa_ok my $labels = CPU::Z80::Disassembler::Labels->new,
		'CPU::Z80::Disassembler::Labels';

# index is empty
is $labels->search_name('hello'), undef;
is $labels->search_addr(34), undef;
is_deeply [sort by_addr $labels->search_all], [];
is $labels->max_length, 0;
is $labels->next_label, undef;
is $labels->next_label(0xFFFF), undef;

# add one label
isa_ok my $hello = $labels->add(34, 'hello', 23),
		'CPU::Z80::Disassembler::Label';
is $hello->name, 		'hello';
is $hello->addr,		34;
is_deeply [$hello->refer_from], [23];
is $labels->max_length, 5;
is $labels->next_label, $hello;
is $labels->next_label(33), $hello;
is $labels->next_label(34), $hello;
is $labels->next_label(35), undef;

is $labels->search_name('hello'), $hello;
is $labels->search_addr(34), $hello;
is_deeply [sort by_addr $labels->search_all], [sort by_addr ($hello)];

# add same label, other reference
isa_ok my $hello2 = $labels->add(34, 'hello', 27),
		'CPU::Z80::Disassembler::Label';
is $hello2, $hello;
is $hello->name, 		'hello';
is $hello->addr,		34;
is_deeply [$hello->refer_from], [23, 27];
is $labels->max_length, 5;
is $labels->next_label, $hello;
is $labels->next_label(33), $hello;
is $labels->next_label(34), $hello;
is $labels->next_label(35), undef;

is $labels->search_name('hello'), $hello;
is $labels->search_addr(34), $hello;
is_deeply [sort by_addr $labels->search_all], [sort by_addr ($hello)];

# add label with same name, other address
eval { $labels->add(33, 'hello') };
like $@, qr/^Label 'hello' with addresses \$0022 and \$0021 at t.*Labels\.t line \d+/;

# add another on same address, no reference address
eval { $labels->add(34, 'world') };
like $@, qr/^Labels 'hello' and 'world' at the same address \$0022 at t.*Labels\.t line \d+/;

is $labels->search_name('hello'), $hello;
is $labels->search_addr(34), $hello;
is_deeply [sort by_addr $labels->search_all], [sort by_addr ($hello)];
is $labels->max_length, 5;

# add another on different address
isa_ok my $earth = $labels->add(35, 'earth', 21),
		'CPU::Z80::Disassembler::Label';
is $earth->name, 		'earth';
is $earth->addr,		35;
is_deeply [$earth->refer_from], [21];
is $labels->max_length, 5;
is $labels->next_label, $hello;
is $labels->next_label(33), $hello;
is $labels->next_label(34), $hello;
is $labels->next_label(35), $earth;
is $labels->next_label(36), undef;

is $labels->search_name('earth'), $earth;
is $labels->search_addr(34), $hello;
is $labels->search_addr(35), $earth;
is_deeply [sort by_addr $labels->search_all], 
		  [sort by_addr ($hello, $earth)];

# create a temporary label
isa_ok my $t1 = $labels->add(0x0036, undef), 'CPU::Z80::Disassembler::Label';

is $labels->search_name("L_0036"), $t1;
is $labels->search_addr(34), $hello;
is $labels->search_addr(35), $earth;
is $labels->search_addr(0x36), $t1;
is_deeply [sort by_addr $labels->search_all], 
		  [sort by_addr ($hello, $earth, $t1)];
is $labels->max_length, 6;
is $labels->next_label, $hello;
is $labels->next_label(33), $hello;
is $labels->next_label(34), $hello;
is $labels->next_label(35), $earth;
is $labels->next_label(36), $t1;
is $labels->next_label(0x36), $t1;
is $labels->next_label(0x37), undef;

isa_ok my $t2 = $labels->add(0x0036, "T2345678"), 'CPU::Z80::Disassembler::Label';
is $t1, $t2;

is $labels->search_name("L_0036"), undef;
is $labels->search_name("T2345678"), $t1;
is $labels->search_addr(34), $hello;
is $labels->search_addr(35), $earth;
is $labels->search_addr(0x36), $t1;
is_deeply [sort by_addr $labels->search_all], 
		  [sort by_addr ($hello, $earth, $t1)];
is $labels->max_length, 8;
is $labels->next_label, $hello;
is $labels->next_label(33), $hello;
is $labels->next_label(34), $hello;
is $labels->next_label(35), $earth;
is $labels->next_label(36), $t1;
is $labels->next_label(0x36), $t1;
is $labels->next_label(0x37), undef;



done_testing;
