use strict;
use warnings;
use lib './lib';
use JavaScript::Duktape;
use Data::Dumper;

my $js = JavaScript::Duktape->new();
my $duk = $js->duk;

## this is an advanced array example
## where we will be using low level duktape API
## to create & construct new javascript classes
## from perl

{
    ## set some javascript objects
    $js->eval(q{
        function Users (name, age, isAdmin){
            this.name = name;
            this.age = age;
            this.role = isAdmin;
        }

        function Roles (role){
            this.admin = role.admin;
            this.developer = role.developer;
        }
    });

    ## getting both Users & Roles objects from
    ## javascript land as perl objects
    my $users = $js->get_object('Users');
    my $role  = $js->get_object('Roles');

    ## add userList javascript array
    ## but this time from perl
    $js->set('usersList', [
        $users->new('Joe The Admin', 36, $role->new({ admin => true, developer => false })),
        $users->new('Doe The Developer', 28, $role->new({ admin => false, developer => true }))
    ]);

    ## did we get this right ?
    $js->eval(q{
        print(usersList[0].name); // => Joe
        print(usersList[1].age); // => 28
    });

    ## get userList from perl again!!
    ## not sure if you ever need to do this :)
    my $usersList = $js->get_object('usersList');

    ## set a map function, we are going to use it
    ## from javascript to map our userList
    ## and set admins
    $duk->push_perl_function( sub {

        # javascript array map prototype pass
        # three arguments, (current element, element index, whole array)
        # so getting argument at index 0 for each array element

        # we need to extract it as javascript object
        # because we need to do some stuff with it
        # so instead of using to_perl method
        # we use to_perl_object
        my $user = $duk->to_perl_object(0);

        # if his role is admin add isAdmin prop
        if ($user->role->admin){
            $duk->push_perl($user);
            $duk->push_true();
            $duk->put_prop_string(-2, "isAdmin");
            $duk->pop();
        }

        # return array element
        $duk->push_perl($user);

        # tell duktape stack that we are pushing/return something
        return 1;
    });

    ## push the above created function to duktape stack
    ## as a global function with "maps" name
    $duk->put_global_string('maps');

    ## use our maps function from javascript
    my $admins = $duk->eval_string(q{ usersList.map(maps); });

    ## get
    $admins = $duk->to_perl_object(-1);

    ## don't forget to pop eval results
    ## we already have it mapped to perl
    $duk->pop();

    ## admins should be a javascript array object
    ## let's check if forEach works
    $admins->forEach( sub {
        my $user = $duk->to_perl_object(0);
        if ($user->isAdmin) {
            print "==================================\n";
            print "found an admin\n";
            print "his name is : ";
            print $user->name, "\n";
            print "==================================\n";
        }
    });

    ## $admin is a javascript array prototype
    ## so instead of using map from javascript we can
    ## also use it from perl land too, and we will
    ## assign new created array to $dev array
    my $dev = $admins->map( sub {
        my $user = $duk->to_perl_object(0);
        if (!$user->isAdmin){
            return $user;
        }
        return false;
    });

    ## again since javascript map function
    ## creates new Array, $dev is an object to
    ## javascript Array
    $dev->forEach( sub {
        my $user = shift;
        if ($user == false){
            print "** only devs allowed\n";
        } else {
            print "welcome home ", $user->{name}, "\n";
        }
    });

    print "==================================\n";
    ## now back to perl again, let's check who's admin
    ## we can also get list as a perl array
    ## eval function will not convert results as objects
    ## it will return as perl data so ..
    $admins = $js->eval(q{ usersList.map(maps); });
    for (@{$admins}){
        if (!$_->{isAdmin}) {
            print "found a Developer\n";
            print "his name is : ";
            print $_->{name}, "\n";
        }
    }
}