package Javonet::Core::Protocol::CommandSerializer;
use strict;
use warnings FATAL => 'all';
use Moose;

use lib 'lib';
use aliased 'Javonet::Sdk::Core::PerlCommand' => 'PerlCommand';
use aliased 'Javonet::Sdk::Core::RuntimeLib' => 'RuntimeLib', qw(get_runtime);
use aliased 'Javonet::Sdk::Core::Type' => 'Type', qw(get_type);
use aliased 'Javonet::Core::Protocol::TypeSerializer' => 'TypeSerializer';
use aliased 'Javonet::Sdk::Core::TypesHandler' => 'TypesHandler';
use Javonet::Sdk::Internal::ConnectionType;
use Thread::Queue;

use Exporter qw(import);
our @EXPORT = qw(serialize serialize_tcp_address serialize_recursively insert_into_buffer);

my @byte_buffer = ();
my $queue = Thread::Queue->new();

sub serialize {
    my ($self, $root_command, $connection_type, $tcp_address, $runtimeVersion) = @_;
    @byte_buffer = ();
    $queue = Thread::Queue->new();
    $queue->insert(0, $root_command);
    insert_into_buffer(($root_command->{runtime}, $runtimeVersion));
    insert_into_buffer(serialize_tcp_address($connection_type, $tcp_address));
    insert_into_buffer(Javonet::Sdk::Core::RuntimeLib::get_runtime('Perl'), $root_command->{command_type});
    serialize_recursively();
    return @byte_buffer;
}

sub serialize_tcp_address {
    my ($connection_type, $tcp_address) = @_;
    my @tcp_byte_array;
    if (Javonet::Sdk::Internal::ConnectionType::get_connection_type('Tcp') eq $connection_type) {
        my @tcp_array = split(':', $tcp_address);
        my @tcp_ip = split('\\.', $tcp_array[0]);
        my @bytes_port = unpack "C*", pack "v", $tcp_array[1];
        @tcp_byte_array = (Javonet::Sdk::Internal::ConnectionType::get_connection_type('Tcp'), @tcp_ip, @bytes_port);
        return @tcp_byte_array;
    }
    else {
        @tcp_byte_array = (Javonet::Sdk::Internal::ConnectionType::get_connection_type('InMemory'), 0, 0, 0, 0, 0, 0);
        return @tcp_byte_array;
    }
}

sub serialize_recursively {
    my $left = $queue->pending();
    if ($left == 0) {
        return @byte_buffer;
    }
    my $command = $queue->dequeue();
    $queue->insert(0, $command->drop_first_payload_argument());
    my $current_payload_ref = $command->{payload};
    my @cur_payload = @$current_payload_ref;
    my $payload_len = @cur_payload;
    if ($payload_len > 0) {
        if (!defined $cur_payload[0]) {
            insert_into_buffer(TypeSerializer->serializePrimitive(undef));
        }
        elsif ($cur_payload[0]->isa("Javonet::Sdk::Core::PerlCommand")) {
            my $inner_command = $cur_payload[0];
            insert_into_buffer(TypeSerializer->serializeCommand($inner_command));
            $queue->insert(0, $inner_command);
        }
        elsif (TypesHandler->is_primitive_or_none($cur_payload[0])) {
            my @result = TypeSerializer->serializePrimitive($cur_payload[0]);
            insert_into_buffer(@result);
        }
        else {
            die "Unsupported payload item type: " . ref($cur_payload[0]) . " for payload item: $cur_payload[0].";
        }
        return serialize_recursively();
    }
    else {
        $queue->dequeue();
    }
    return serialize_recursively();
}

sub insert_into_buffer {
    my @arguments = @_;
    @byte_buffer = (@byte_buffer, @arguments);
}

no Moose;
1;