package mkcommon;

use 5.012002;
use strict;
use warnings;

require Exporter;

our @ISA = qw(Exporter);

# Items to export into callers namespace by default. Note: do not export
# names by default without a very good reason. Use EXPORT_OK instead.
# Do not simply export all your public functions/methods/constants.

# This allows declaration	use mkcommon ':all';
# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
# will save memory.
our %EXPORT_TAGS = ( 'all' => [ qw(
	
) ] );

our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );

our @EXPORT = qw(
	
);

our $VERSION = '0.01';

my $BLOCK_SIZE=256;

# Preloaded methods go here.

sub new {

    my $this=shift;

    my $class = ref($this) || $this;
    my $self = {};
    bless $self, $class;

    $$self{'char_array'}=[];
    $$self{'char_class'}=[];
    $$self{'char_start'}=[0];

    $$self{'last_block'}=-1;
    $$self{'last'}="";
    $$self{'last_f'}=0;
    $$self{'last_l'}=0;

    return $self;
}

sub _doemit_block {
    my $this=shift;

    my $f=shift;
    my $l=shift;

    push @{$$this{'char_array'}}, [$f, $l];
    push @{$$this{'char_class'}}, $$this{'last'};
}

sub _doemit_endblock {

    my $this=shift;

    push @{$$this{'char_start'}}, $#{$$this{'char_array'}}+1;
}

# _doemit invokes _doemit_block() for each unicode char range with a given
# linebreaking class. However, once a unicode char range starts in a different
# $BLOCK_SIZE character class, call _doemit_endblock() before calling _doemit_block().
#
# If a single unicode char range crosses a $BLOCK_SIZE character class boundary,
# split it at the boundary; call _doemit_endblock() to finish the current $BLOCK_SIZE
# char boundary, call _doemit_endblock(), then call _doemit_block() for the
# rest of the char range.


sub _doemit {

    my $this=shift;

    $this->_doemit_endblock()
	if int($$this{'last_f'} / $BLOCK_SIZE)
	!= $$this{'last_block'} && $$this{'last_block'} != -1;

    if (int($$this{'last_f'} / $BLOCK_SIZE) != int($$this{'last_l'} / $BLOCK_SIZE))
    {
	while (int($$this{'last_f'} / $BLOCK_SIZE) != int($$this{'last_l'} / $BLOCK_SIZE))
	{
	    my $n=int($$this{'last_f'} / $BLOCK_SIZE) * $BLOCK_SIZE + ($BLOCK_SIZE-1);

	    $this->_doemit_block($$this{'last_f'}, $n);
	    $this->_doemit_endblock();
	    $$this{'last_f'}=$n+1;
	}
    }
    $this->_doemit_block($$this{'last_f'}, $$this{'last_l'});

    $$this{'last_block'}=int($$this{'last_l'} / $BLOCK_SIZE);
}

#
# Coalesce adjacent unicode char blocks that have the same linebreaking
# property. Invoke _doemit() for the accumulate unicode char range once
# a range with a different linebreaking class is seen.

sub range {

    my $this=shift;

    my $f=shift;
    my $l=shift;
    my $t=shift;

    if ($$this{'last_l'} + 1 == $f && $$this{'last'} eq $t)
    {
	$$this{'last_l'}=$l;
	return;
    }

    $this->_doemit() if $$this{'last'};  # New linebreaking class

    $$this{'last_f'}=$f;
    $$this{'last_l'}=$l;
    $$this{'last'}=$t;
}

sub output {
    my $this=shift;

    $this->_doemit();  # Emit last linebreaking unicode char range class

    $this->_doemit_endblock(); # End of the most recent $BLOCK_SIZE char range class

    print "static const uint8_t unicode_rangetab[][2]={\n";

    my $comma="\t";

    my $modulo=sprintf("0x%X", $BLOCK_SIZE-1);

    foreach ( @{$$this{'char_array'}} )
    {
	print "${comma}{0x" . sprintf("%04x", $$_[0]) . " & $modulo, 0x"
	    . sprintf("%04x", $$_[1]) . " & $modulo}";
	$comma=",\n\t";
    }

    print "};\n\n";

    print "static const uint8_t unicode_classtab[]={\n";

    $comma="\t";
    foreach ( @{$$this{'char_class'}} )
    {
	print "${comma}$_";
	$comma=",\n\t";
    }

    print "};\n\n";

    print "static const size_t unicode_indextab[]={\n";

    $comma="\t";

    my $prev_block=-1;
    foreach (@{$$this{'char_start'}})
    {
	my $sp=$_;
	my $cnt=1;

	if ($sp <= $#{$$this{'char_array'}})
	{
	    my $block=int($$this{'char_array'}->[$sp]->[0] / $BLOCK_SIZE);

	    $cnt = $block - $prev_block;
	    $prev_block=$block;
	}

	foreach (1..$cnt)
	{
	    print "$comma$sp";
	    $comma=",\n\t";
	}
    }

    print "};\n\n";
}

1;
