####################################################################################################################################
# Protocol File
####################################################################################################################################
package pgBackRest::Protocol::Storage::File;
use parent 'pgBackRest::Common::Io::Base';

use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use English '-no_match_vars';

use Exporter qw(import);
    our @EXPORT = qw();

use pgBackRest::Common::Exception;
use pgBackRest::Common::Log;

####################################################################################################################################
# CONSTRUCTOR
####################################################################################################################################
sub new
{
    my $class = shift;

    # Assign function parameters, defaults, and log debug info
    my
    (
        $strOperation,
        $oProtocol,                                         # Master or minion protocol
        $oFileIo,                                           # File whose results will be returned via protocol
    ) =
        logDebugParam
        (
            __PACKAGE__ . '->new', \@_,
            {name => 'oProtocol', trace => true},
            {name => 'oFileIo', required => false, trace => true},
        );

    # Create class
    my $self = $class->SUPER::new($oProtocol->io()->id() . ' file');
    bless $self, $class;

    # Set variables
    $self->{oProtocol} = $oProtocol;
    $self->{oFileIo} = $oFileIo;

    # Set read/write
    $self->{bWrite} = false;

    # Return from function and log return values if any
    return logDebugReturn
    (
        $strOperation,
        {name => 'self', value => $self}
    );
}

####################################################################################################################################
# read - read block from protocol
####################################################################################################################################
sub read
{
    my $self = shift;
    my $rtBuffer = shift;

    my $lBlockSize;

    # Read the block header and make sure it's valid
    my $strBlockHeader = $self->{oProtocol}->io()->readLine();

    if ($strBlockHeader !~ /^BRBLOCK[0-9]+$/)
    {
        confess &log(ERROR, "invalid block header '${strBlockHeader}'", ERROR_FILE_READ);
    }

    # Get block size from the header
    $lBlockSize = substr($strBlockHeader, 7);

    # Read block if size > 0
    if ($lBlockSize > 0)
    {
        $self->{oProtocol}->io()->read($rtBuffer, $lBlockSize, true);
    }

    # Return the block size
    return $lBlockSize;
}

####################################################################################################################################
# write - write block to protocol
####################################################################################################################################
sub write
{
    my $self = shift;
    my $rtBuffer = shift;

    # Set write
    $self->{bWrite} = true;

    # Get the buffer size
    my $lBlockSize = defined($rtBuffer) ? length($$rtBuffer) : 0;

    # Write if size > 0 (0 ends the copy stream so it should only be done in close())
    if ($lBlockSize > 0)
    {
        # Write block header to the protocol stream
        $self->{oProtocol}->io()->writeLine("BRBLOCK${lBlockSize}");

        # Write block if size
        $self->{oProtocol}->io()->write($rtBuffer);
    }

    return length($$rtBuffer);
}

####################################################################################################################################
# close - set the result hash
####################################################################################################################################
sub close
{
    my $self = shift;

    # Close if protocol is defined (to prevent this from running more than once)
    if (defined($self->{oProtocol}))
    {
        # If writing output terminator
        if ($self->{bWrite})
        {
            $self->{oProtocol}->io()->writeLine("BRBLOCK0");
        }

        # On master read the results
        if ($self->{oProtocol}->master())
        {
            ($self->{rhResult}) = $self->{oProtocol}->outputRead();

            # Minion will send one more output message after file is closed which can be ignored
            $self->{oProtocol}->outputRead();
        }
        # On minion write the results
        else
        {
            $self->{oProtocol}->outputWrite($self->{oFileIo}->resultAll());
        }

        # Delete protocol to prevent close from running again
        delete($self->{oProtocol});
    }
}

1;
