package XS::JIT::Builder;

use strict;
use warnings;
use 5.010;

our $VERSION = '0.06';

use Exporter 'import';
our @EXPORT_OK = qw(
    INLINE_NONE INLINE_GETTER INLINE_SETTER 
    INLINE_HV_GETTER INLINE_HV_SETTER
);
our %EXPORT_TAGS = (
    inline => [qw(INLINE_NONE INLINE_GETTER INLINE_SETTER 
                  INLINE_HV_GETTER INLINE_HV_SETTER)],
);

require XSLoader;
XSLoader::load('XS::JIT::Builder', $VERSION);

sub write_file {
    my ($self, $filename) = @_;

    die "write_file requires a filename" unless defined $filename;

    open my $fh, '>', $filename
        or die "Cannot open '$filename' for writing: $!";
    print $fh $self->code;
    close $fh
        or die "Cannot close '$filename': $!";

    return $self;
}

=head1 NAME

XS::JIT::Builder - interface for building XS/C code strings

=head1 SYNOPSIS

    use XS::JIT;
    use XS::JIT::Builder;
    use File::Temp qw(tempdir);

    my $cache_dir = tempdir(CLEANUP => 1);
    my $b = XS::JIT::Builder->new;
    
    # Build a simple accessor
    $b->xs_function('get_name')
      ->xs_preamble
      ->get_self_hv
      ->hv_fetch('hv', 'name', 4, 'val')
      ->if('val != NULL')
        ->return_sv('*val')
      ->endif
      ->return_undef
      ->xs_end;
    
    # Compile and use
    XS::JIT->compile(
        code      => $b->code,
        name      => 'MyClass',
        cache_dir => $cache_dir,
        functions => {
            'MyClass::name' => { source => 'get_name', is_xs_native => 1 },
        },
    );
    
    my $obj = bless { name => 'Alice' }, 'MyClass';
    print $obj->name;  # prints "Alice"

=head1 DESCRIPTION

XS::JIT::Builder provides a interface for generating C code dynamically. 
It handles indentation automatically and provides convenient methods for 
common XS patterns.

All methods return C<$self> to enable method chaining. The generated code
can be retrieved with C<code()> and passed to C<XS::JIT-E<gt>compile()>.

=head1 METHODS

=head2 Lifecycle

=over 4

=item new(%options)

Create a new builder.

    my $b = XS::JIT::Builder->new;
    my $b = XS::JIT::Builder->new(use_tabs => 1);
    my $b = XS::JIT::Builder->new(indent_width => 2);

Options:

=over 4

=item use_tabs

Use tabs for indentation instead of spaces (default: false).

=item indent_width

Number of spaces per indentation level when not using tabs (default: 4).

=back

=item code()

Return the accumulated C code as a string.

    my $c_code = $b->code;

=item reset()

Clear the builder and reset state for reuse.

    $b->reset;
    # Builder is now empty, can build new code

=item write_file($filename)

Write the accumulated C code to a file. This is useful for:

=over 4

=item * Distribution - Generate the XS file once, ship it with your module

=item * Debugging - Inspect the generated code

=item * Performance - Skip runtime JIT compilation entirely

=back

    $b->write_file('MyModule.c');
    # Now you can compile MyModule.c as a regular XS/C extension

Returns C<$self> to enable method chaining.

=back

=head2 Low-level Output

=over 4

=item line($text)

Add a line with current indentation.

    $b->line('int x = 42;');

=item raw($text)

Add raw text without automatic indentation or newline.

    $b->raw('/* inline comment */');

=item comment($text)

Add a C comment.

    $b->comment('This is a comment');
    # Generates: /* This is a comment */

=item blank()

Add a blank line.

=back

=head2 XS Structure

=over 4

=item xs_function($name)

Start an XS function definition.

    $b->xs_function('my_function');
    # Generates: XS_EUPXS(my_function) {

=item xs_preamble()

Add standard XS preamble (dVAR, dXSARGS, etc.).

    $b->xs_function('my_func')
      ->xs_preamble;

=item xs_end()

Close an XS function.

    $b->xs_end;
    # Generates: }

=item xs_return($count)

Add XSRETURN statement.

    $b->xs_return('1');
    # Generates: XSRETURN(1);

=item xs_return_undef()

Return undef from XS function.

    $b->xs_return_undef;
    # Generates: ST(0) = &PL_sv_undef; XSRETURN(1);

=item method_start($name, $min_args, $max_args, $usage)

Start a method with argument checking.

    $b->method_start('set_value', 2, 2, '$self, $value');
    # Generates function with items check and usage croak

=back

=head2 Control Flow

=over 4

=item if($condition)

Start an if block.

    $b->if('items > 1');

=item elsif($condition)

Add an elsif clause.

    $b->elsif('items == 1');

=item else()

Add an else clause.

    $b->else;

=item endif()

Close an if/elsif/else block.

    $b->if('x > 0')
      ->return_pv('positive')
    ->endif;

=item for($init, $cond, $incr)

Start a for loop.

    $b->for('int i = 0', 'i < 10', 'i++');

=item while($condition)

Start a while loop.

    $b->while('ptr != NULL');

=item endloop() / endfor() / endwhile()

Close a loop. All three are aliases.

    $b->for('int i = 0', 'i < n', 'i++')
      ->raw('sum += array[i];')
    ->endfor;

=item block()

Start a bare block.

    $b->block;
    # Generates: {

=item endblock()

Close a bare block.

    $b->block
      ->declare_iv('temp', 'x')
      ->raw('x = y; y = temp;')
    ->endblock;

=back

=head2 Variable Declarations

=over 4

=item declare($type, $name, $value)

Declare a variable of any type.

    $b->declare('int', 'count', '0');
    # Generates: int count = 0;

=item declare_sv($name, $value)

Declare an SV* variable.

    $b->declare_sv('arg', 'ST(1)');
    # Generates: SV* arg = ST(1);

=item declare_iv($name, $value)

Declare an IV (integer) variable.

    $b->declare_iv('count', 'items - 1');
    # Generates: IV count = items - 1;

=item declare_nv($name, $value)

Declare an NV (double) variable.

    $b->declare_nv('total', '0.0');
    # Generates: NV total = 0.0;

=item declare_pv($name, $value)

Declare a const char* variable.

    $b->declare_pv('str', 'SvPV_nolen(ST(0))');
    # Generates: const char* str = SvPV_nolen(ST(0));

=item declare_hv($name, $value)

Declare an HV* variable.

    $b->declare_hv('hash', '(HV*)SvRV(arg)');

=item declare_av($name, $value)

Declare an AV* variable.

    $b->declare_av('array', '(AV*)SvRV(arg)');

=item new_hv($name)

Declare and create a new hash.

    $b->new_hv('hv');
    # Generates: HV* hv = newHV();

=item new_av($name)

Declare and create a new array.

    $b->new_av('results');
    # Generates: AV* results = newAV();

=item assign($var, $value)

Assign a value to an existing variable.

    $b->assign('count', 'count + 1');
    # Generates: count = count + 1;

=back

=head2 Type Checking

=over 4

=item check_items($min, $max, $usage)

Check argument count, croak if wrong. Use max=-1 for no upper limit.

    $b->check_items(1, 2, '$self, $value?');

=item check_defined($sv, $error_msg)

Croak if SV is not defined.

    $b->check_defined('ST(1)', 'value required');

=item check_hashref($sv, $error_msg)

Croak if SV is not a hash reference.

    $b->check_hashref('ST(0)', 'expected hashref');

=item check_arrayref($sv, $error_msg)

Croak if SV is not an array reference.

    $b->check_arrayref('arg', 'expected arrayref');

=back

=head2 SV Conversion

=over 4

=item sv_to_iv($result_var, $sv)

Convert SV to integer.

    $b->sv_to_iv('count', 'ST(1)');
    # Generates: IV count = SvIV(ST(1));

=item sv_to_nv($result_var, $sv)

Convert SV to double.

    $b->sv_to_nv('value', 'ST(1)');

=item sv_to_pv($result_var, $len_var, $sv)

Convert SV to string with length.

    $b->sv_to_pv('str', 'len', 'ST(1)');
    # Generates: STRLEN len; const char* str = SvPV(ST(1), len);

=item sv_to_bool($result_var, $sv)

Convert SV to boolean.

    $b->sv_to_bool('flag', 'ST(1)');

=item new_sv_iv($result_var, $value)

Create new SV from integer.

    $b->new_sv_iv('result', 'count');
    # Generates: SV* result = newSViv(count);

=item new_sv_nv($result_var, $value)

Create new SV from double.

    $b->new_sv_nv('result', 'total');

=item new_sv_pv($result_var, $str, $len)

Create new SV from string.

    $b->new_sv_pv('result', 'buffer', 'len');
    # Generates: SV* result = newSVpvn(buffer, len);

=back

=head2 Return Helpers

=over 4

=item return_iv($value)

Return an integer value.

    $b->return_iv('42');
    $b->return_iv('count');

=item return_nv($value)

Return a double value.

    $b->return_nv('3.14');
    $b->return_nv('result');

=item return_pv($str)

Return a string value. The string is automatically quoted if it doesn't
start with a letter/underscore (i.e., if it's not a C variable name).

    $b->return_pv('hello');      # Returns literal "hello"
    $b->return_pv('buffer');     # Returns C variable buffer
    $b->return_pv('"quoted"');   # Returns literal "quoted"

=item return_sv($sv)

Return an SV.

    $b->return_sv('result');
    $b->return_sv('sv_2mortal(newSViv(42))');

=item return_yes()

Return true (C<&PL_sv_yes>).

=item return_no()

Return false (C<&PL_sv_no>).

=item return_undef()

Return undef.

=item return_self()

Return the invocant (C<$self>).

    $b->return_self;
    # For method chaining

=back

=head2 Self/Object Methods

=over 4

=item get_self()

Get the invocant as SV*.

    $b->get_self;
    # Generates: SV* self = ST(0);

=item get_self_hv()

Get the invocant and its underlying hash.

    $b->get_self_hv;
    # Generates: SV* self = ST(0); HV* hv = (HV*)SvRV(self);

=item mortal($result_var, $sv_expr)

Create a mortal copy of an SV.

    $b->mortal('result', 'newSViv(42)');
    # Generates: SV* result = sv_2mortal(newSViv(42));

=back

=head2 Hash Operations

=over 4

=item hv_fetch($hv, $key, $len, $result_var)

Fetch from a hash with a literal string key.

    $b->hv_fetch('hv', 'name', 4, 'val');
    # Generates: SV** val = hv_fetch(hv, "name", 4, 0);

=item hv_fetch_sv($hv, $key_expr, $len_expr, $result_var)

Fetch from a hash with a C expression key.

    $b->hv_fetch_sv('hv', 'key', 'key_len', 'val');
    # Generates: SV** val = hv_fetch(hv, key, key_len, 0);

=item hv_store($hv, $key, $len, $value)

Store into a hash with a literal string key.

    $b->hv_store('hv', 'name', 4, 'newSVpv("Alice", 0)');
    # Generates: (void)hv_store(hv, "name", 4, newSVpv("Alice", 0), 0);

=item hv_store_sv($hv, $key_expr, $len_expr, $value)

Store into a hash with a C expression key.

    $b->hv_store_sv('hv', 'key', 'key_len', 'newSVsv(val)');

=item hv_fetch_return($hv, $key, $len)

Fetch from hash and return the value (or undef if not found).

    $b->hv_fetch_return('hv', 'name', 4);

=item hv_delete($hv, $key, $len)

Delete a key from a hash.

    $b->hv_delete('hv', 'name', 4);

=item hv_exists($hv, $key, $len, $result_var)

Check if a key exists in a hash.

    $b->hv_exists('hv', 'name', 4, 'found');

=back

=head2 Array Operations

=over 4

=item av_fetch($av, $index, $result_var)

Fetch from an array.

    $b->av_fetch('av', 'i', 'elem');
    # Generates: SV** elem = av_fetch(av, i, 0);

=item av_store($av, $index, $value)

Store into an array.

    $b->av_store('av', 'i', 'newSViv(42)');

=item av_push($av, $value)

Push onto an array.

    $b->av_push('results', 'newSViv(n)');

=item av_len($av, $result_var)

Get the highest index of an array.

    $b->av_len('av', 'last_idx');
    # Generates: SSize_t last_idx = av_len(av);

=back

=head2 Prebuilt Patterns

These methods generate complete XS functions for common patterns.

=over 4

=item accessor($attr_name, \%options)

Generate a read-write accessor (or read-only with C<readonly =E<gt> 1>).
The function name is the attribute name.

    $b->accessor('name');                        # read-write
    $b->accessor('id', { readonly => 1 });       # read-only

=item ro_accessor($func_name, $attr_name, $attr_len)

Generate a complete read-only accessor function.

    $b->ro_accessor('get_name', 'name', 4);

=item rw_accessor($func_name, $attr_name, $attr_len)

Generate a complete read-write accessor function.

    $b->rw_accessor('name', 'name', 4);

=item predicate($attr_name)

Generate a predicate method (C<has_$attr_name>).

    $b->predicate('name');
    # Generates has_name() that returns true if 'name' key exists

=item clearer($attr_name)

Generate a clearer method (C<clear_$attr_name>).

    $b->clearer('cache');
    # Generates clear_cache() that deletes 'cache' key

=item constructor($func_name, \@attrs)

Generate a constructor. Attrs is arrayref of C<[$name, $len]> pairs
or simple attribute name strings.

    $b->constructor('new', [
        ['name', 4],
        ['age', 3],
    ]);
    
    # Or with auto-calculated lengths:
    $b->constructor('new', ['name', 'age']);

=back

=head1 COMPLETE EXAMPLES

=head2 Simple Class with Accessors

    use XS::JIT;
    use XS::JIT::Builder;
    use File::Temp qw(tempdir);

    my $cache = tempdir(CLEANUP => 1);
    my $b = XS::JIT::Builder->new;

    # Constructor
    $b->xs_function('person_new')
      ->xs_preamble
      ->new_hv('hv')
      ->if('items >= 2')
        ->hv_store('hv', 'name', 4, 'newSVsv(ST(1))')
      ->endif
      ->if('items >= 3')
        ->hv_store('hv', 'age', 3, 'newSVsv(ST(2))')
      ->endif
      ->raw('SV* self = newRV_noinc((SV*)hv);')
      ->raw('sv_bless(self, gv_stashpv("Person", GV_ADD));')
      ->return_sv('self')
      ->xs_end
      ->blank;

    # Name accessor (read-write)
    $b->rw_accessor('person_name', 'name', 4)->blank;

    # Age accessor (read-only) 
    $b->ro_accessor('person_age', 'age', 3);

    XS::JIT->compile(
        code      => $b->code,
        name      => 'Person',
        cache_dir => $cache,
        functions => {
            'Person::new'  => { source => 'person_new', is_xs_native => 1 },
            'Person::name' => { source => 'person_name', is_xs_native => 1 },
            'Person::age'  => { source => 'person_age', is_xs_native => 1 },
        },
    );

    my $p = Person->new('Alice', 30);
    say $p->name;        # Alice
    $p->name('Bob');     # set name
    say $p->age;         # 30

=head2 Class with Inheritance

    my $b = XS::JIT::Builder->new;
    
    # Base class constructor
    $b->xs_function('animal_new')
      ->xs_preamble
      ->new_hv('hv')
      ->hv_store('hv', 'name', 4, 'items > 1 ? newSVsv(ST(1)) : newSVpv("", 0)')
      ->raw('SV* self = newRV_noinc((SV*)hv);')
      ->raw('sv_bless(self, gv_stashpv(SvPV_nolen(ST(0)), GV_ADD));')
      ->return_sv('self')
      ->xs_end
      ->blank;
    
    # Dog constructor (overrides Animal)
    $b->xs_function('dog_new')
      ->xs_preamble
      ->new_hv('hv')
      ->hv_store('hv', 'name', 4, 'items > 1 ? newSVsv(ST(1)) : newSVpv("", 0)')
      ->hv_store('hv', 'breed', 5, 'items > 2 ? newSVsv(ST(2)) : newSVpv("mutt", 0)')
      ->raw('SV* self = newRV_noinc((SV*)hv);')
      ->raw('sv_bless(self, gv_stashpv("Dog", GV_ADD));')
      ->return_sv('self')
      ->xs_end;
    
    # Compile and set up inheritance
    XS::JIT->compile(...);
    
    package Dog;
    use parent -norequire => 'Animal';

=head2 Ultra-fast Array-based Objects with Inline Ops

For maximum performance, use array-based objects with inline ops.
This bypasses XS call overhead entirely at compile time:

    use XS::JIT;
    use XS::JIT::Builder qw(:inline);
    
    # Generate op-based accessors
    my $b = XS::JIT::Builder->new;
    $b->op_ro_accessor('get_name', 0);  # slot 0 is read-only
    $b->op_rw_accessor('age', 1);       # slot 1 is read-write
    
    XS::JIT->compile(
        code      => $b->code,
        name      => 'Cat',
        cache_dir => $cache,
        functions => {
            'Cat::name' => { source => 'get_name', is_xs_native => 1 },
            'Cat::age'  => { source => 'age', is_xs_native => 1 },
        },
    );
    
    # Register inline ops for compile-time optimization
    XS::JIT::Builder::inline_init();
    XS::JIT::Builder::inline_register(\&Cat::name, INLINE_GETTER, 0);
    XS::JIT::Builder::inline_register(\&Cat::age, INLINE_SETTER, 1);
    
    # Now function calls are replaced with custom ops at compile time!
    package Cat;
    sub new { bless [$_[1], $_[2]], $_[0] }
    
    package main;
    my $cat = Cat->new('Whiskers', 3);
    say $cat->name;      # Inline op - no XS call overhead
    $cat->age(4);        # Inline setter

=head2 Inline Op Types

The following constants are available via C<:inline> export tag:

=over 4

=item INLINE_NONE (0)

No inlining.

=item INLINE_GETTER (1)

Read-only slot accessor. Replaces C<< $obj->name >> with a custom op
that reads directly from C<< $obj->[slot] >>.

=item INLINE_SETTER (2)

Read-write slot accessor. Supports both getter and setter modes.

=item INLINE_HV_GETTER (3)

Read-only hash accessor (not yet implemented).

=item INLINE_HV_SETTER (4)

Read-write hash accessor (not yet implemented).

=back

=head2 Custom Op Builder Methods

These methods generate code for building custom Perl ops, enabling
compile-time optimization through call checkers.

=head3 PP Function Builders

PP (push-pop) functions are the runtime handlers for custom ops.

=over 4

=item pp_start($name)

Start a pp function definition.

    $b->pp_start('pp_my_op');
    # Generates: OP* pp_my_op(pTHX) {

=item pp_end()

End a pp function with C<return NORMAL>.

    $b->pp_end;
    # Generates: return NORMAL; }

=item pp_dsp()

Add dSP declaration for stack access.

    $b->pp_dsp;
    # Generates: dSP;

=item pp_get_self()

Get the invocant from the stack without popping.

    $b->pp_get_self;
    # Generates: SV* self = TOPs;

=item pp_pop_self()

Pop the invocant from the stack.

    $b->pp_pop_self;
    # Generates: SV* self = POPs;

=item pp_pop_sv($name)

Pop an SV from the stack.

    $b->pp_pop_sv('value');
    # Generates: SV* value = POPs;

=item pp_pop_nv($name)

Pop a numeric value from the stack.

    $b->pp_pop_nv('amount');
    # Generates: NV amount = POPn;

=item pp_pop_iv($name)

Pop an integer value from the stack.

    $b->pp_pop_iv('count');
    # Generates: IV count = POPi;

=item pp_get_slots()

Get the slots array from self (for array-based objects).

    $b->pp_get_slots;
    # Generates: AV* slots = (AV*)SvRV(self);

=item pp_slot($name, $index)

Access a specific slot from the slots array.

    $b->pp_slot('name_sv', 0);
    # Generates: SV* name_sv = *av_fetch(slots, 0, 0);

=item pp_return_sv($expr)

Return an SV to the stack.

    $b->pp_return_sv('name_sv');
    # Generates: SETs(name_sv); RETURN;

=item pp_return_nv($expr)

Return a numeric value.

    $b->pp_return_nv('result');
    # Generates: SETn(result); RETURN;

=item pp_return_iv($expr)

Return an integer value.

    $b->pp_return_iv('count');
    # Generates: SETi(count); RETURN;

=item pp_return_pv($expr)

Return a string value.

    $b->pp_return_pv('str');
    # Generates: SETs(sv_2mortal(newSVpv(str, 0))); RETURN;

=item pp_return()

Return without modifying the stack.

    $b->pp_return;
    # Generates: return NORMAL;

=back

=head3 Call Checker Builders

Call checkers run at compile time to replace subroutine calls with custom ops.

=over 4

=item ck_start($name)

Start a call checker function.

    $b->ck_start('ck_my_method');
    # Generates: OP* ck_my_method(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) {

=item ck_end()

End a call checker with fallback.

    $b->ck_end;
    # Generates: return ck_entersub_args_proto_or_list(...); }

=item ck_preamble()

Add standard call checker preamble to extract arguments.

    $b->ck_preamble;
    # Extracts pushmark, args, method from the optree

=item ck_build_unop($pp_func, $targ_expr)

Build a custom unary op (one argument).

    $b->ck_build_unop('pp_my_getter', 'slot_num');
    # Generates code to create OP_CUSTOM with the specified pp function

=item ck_build_binop($pp_func, $targ_expr)

Build a custom binary op (two arguments).

    $b->ck_build_binop('pp_my_setter', 'slot_num');

=item ck_fallback()

Fall through to default call checking.

    $b->ck_fallback;
    # Generates: return ck_entersub_args_proto_or_list(...);

=back

=head3 XOP Helpers

XOP (extended op) declarations register custom ops with Perl.

=over 4

=item xop_declare($name, $pp_func, $desc)

Declare a custom op descriptor.

    $b->xop_declare('my_xop', 'pp_my_op', 'my custom op');
    # Generates static XOP declaration and registration

=item register_checker($cv_expr, $ck_func, $ckobj_expr)

Register a call checker for a CV.

    $b->register_checker('cv', 'ck_my_method', 'newSViv(slot)');
    # Generates: cv_set_call_checker(cv, ck_my_method, ckobj);

=back

=head3 Custom Op Example

    my $b = XS::JIT::Builder->new;
    
    # Declare the XOP
    $b->xop_declare('slot_getter_xop', 'pp_slot_getter', 'slot getter');
    
    # Build the pp function
    $b->pp_start('pp_slot_getter')
      ->pp_dsp
      ->pp_get_self
      ->pp_get_slots
      ->line('IV slot = PL_op->op_targ;')
      ->pp_slot('val', 'slot')
      ->pp_return_sv('val')
      ->pp_end;
    
    # Build the call checker
    $b->ck_start('ck_slot_getter')
      ->ck_preamble
      ->ck_build_unop('pp_slot_getter', 'SvIV(ckobj)')
      ->ck_end;

=head1 SEE ALSO

L<XS::JIT> - The main JIT compiler module

The C API is available in F<xs_jit_builder.h> for direct use from XS code.

=head1 AUTHOR

LNATION <email@lnation.org>

=head1 LICENSE

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.

=cut

1;
