package Javonet::Core::Protocol::CommandDeserializer;
use strict;
use warnings;
use Moose;
use lib 'lib';
use aliased 'Javonet::Sdk::Core::PerlCommand' => 'PerlCommand';
use aliased 'Javonet::Sdk::Core::Type' => 'Type', qw(get_type);
use aliased 'Javonet::Core::Protocol::TypeDeserializer' => 'TypeDeserializer', qw(deserializeInt deserializeString deserializeDouble deserializeFloat);


our $position = 0;
our @buffer = [];
our $command;

sub new {
    my ($self, $array_ref) = @_;
    @buffer = @$array_ref;
    $command = PerlCommand->new(runtime =>$buffer[0],
        command_type => $buffer[10], payload=>[]);
    $position = 11;
    return $self;
}

sub isAtEnd {
    my $length = @buffer;
    return $position == $length;
}

sub decode {
    while (!isAtEnd()) {
        $command = $command->addArgumentToPayload([readObject($buffer[$position])]);
    }
    return $command;
}

sub readObject {
    my $type_num = $_[0];
    if($type_num == Type->get_type('Command')){
        return readCommand();
    }
    elsif($type_num == Type->get_type('JavonetString')){
        return readString();
    }
    elsif($type_num == Type->get_type('JavonetInteger')){
        return readInt();
    }
    elsif($type_num == Type->get_type('JavonetFloat')){
        return readFloat();
    }
    elsif($type_num == Type->get_type('JavonetDouble')){
        return readDouble();
    }
    elsif($type_num == Type->get_type('JavonetBoolean')){
        return readBoolean();
    }
    else{
        die "Type is not supported: $type_num"
    }
}

sub readCommand{
    my $p = $position;
    my @int_buffer = @buffer[$p + 1 .. $p + 4];
    my $numberOfArgumentInPayload = TypeDeserializer->deserializeInt(\@int_buffer);
    my $runtime = $buffer[$p + 5];
    my $commandType = $buffer[$p + 6];

    $position += 7;
    my $returnCommand = PerlCommand->new(runtime => $runtime, command_type => $commandType);
    return readCommandRecursively($numberOfArgumentInPayload, $returnCommand);
}

sub readCommandRecursively {
    my $numberOfArgumentInPayloadLeft = $_[0];
    my $cmd = $_[1];

    if ($numberOfArgumentInPayloadLeft == 0) {
        return $cmd;
    }
    else {
        my $p = $position;
        my @int_buffer = @buffer[$p + 1 .. $p + 4];
        my $argSize = TypeDeserializer->deserializeInt(\@int_buffer);
        $cmd = $cmd->addArgumentToPayload([readObject($buffer[$p])]);
        return readCommandRecursively($numberOfArgumentInPayloadLeft - 1, $cmd);
    }
}

sub readInt{
    $position += 2;
    my $p = $position;
    $position += 4;
    my @int_buffer = @buffer[$p .. $p + 3];
    return TypeDeserializer->deserializeInt(\@int_buffer);
}

sub readFloat{
    $position += 2;
    my $p = $position;
    $position += 4;
    my @float_buffer = @buffer[$p .. $p + 3];
    return TypeDeserializer->deserializeFloat(\@float_buffer);
}

sub readDouble{
    $position += 2;
    my $p = $position;
    $position += 8;
    my @double_buffer = @buffer[$p .. $p + 7];
    return TypeDeserializer->deserializeDouble(\@double_buffer);
}

sub readString{
    my $p = $position;
    my $string_encoding_mode = $buffer[$p + 1];
    my @int_buffer = @buffer[$p + 2 .. $p + 5];
    my $size = TypeDeserializer->deserializeInt(\@int_buffer);
    $position += 6;
    $p = $position;
    $position += $size;
    my @string_buffer = @buffer[$p .. $p + $size - 1];
    my $decodedString = TypeDeserializer->deserializeString($string_encoding_mode, \@string_buffer);

    return $decodedString
}

sub readBoolean{
    $position += 2;
    my $p = $position;
    $position += 1;
    my $bool_buffer = $buffer[$p];
    return TypeDeserializer->deserializeBoolean($bool_buffer);
}


no Moose;
1;