####################################################################################################################################
# PROTOCOL COMMAND MASTER MODULE
####################################################################################################################################
package pgBackRest::Protocol::Command::Master;
use parent 'pgBackRest::Protocol::Base::Master';

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

use File::Basename qw(dirname);
use Time::HiRes qw(gettimeofday);

use pgBackRest::Common::Exception;
use pgBackRest::Common::Ini;
use pgBackRest::Common::Log;
use pgBackRest::Common::Io::Process;
use pgBackRest::Protocol::Base::Master;
use pgBackRest::Version;

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

    # Assign function parameters, defaults, and log debug info
    my
    (
        $strOperation,
        $strName,                                   # Name of the protocol
        $strId,                                     # Id of this process for error messages
        $strCommand,                                # Command to execute on local/remote
        $iBufferMax,                                # Maximum buffer size
        $iCompressLevel,                            # Set compression level
        $iCompressLevelNetwork,                     # Set compression level for network only compression
        $iProtocolTimeout,                          # Protocol timeout
    ) =
        logDebugParam
        (
            __PACKAGE__ . '->new', \@_,
            {name => 'strName'},
            {name => 'strId'},
            {name => 'strCommand'},
            {name => 'iBufferMax'},
            {name => 'iCompressLevel'},
            {name => 'iCompressLevelNetwork'},
            {name => 'iProtocolTimeout'},
        );

    # Set command
    if (!defined($strCommand))
    {
        confess &log(ASSERT, 'strCommand must be set');
    }

    # Execute the command
    my $oIo = new pgBackRest::Common::Io::Process(
        new pgBackRest::Common::Io::Buffered(
            new pgBackRest::Common::Io::Handle($strId), $iProtocolTimeout, $iBufferMax), $strCommand);

    # Create the class hash
    my $self = $class->SUPER::new($strName, $strId, $oIo);
    bless $self, $class;

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

####################################################################################################################################
# close
####################################################################################################################################
sub close
{
    my $self = shift;

    # Assign function parameters, defaults, and log debug info
    my
    (
        $strOperation,
        $bComplete,
    ) =
        logDebugParam
        (
            __PACKAGE__ . '->close', \@_,
            {name => 'bComplete', default => false, trace => true},
        );

    # Exit status defaults to success
    my $iExitStatus = 0;
    my $bClosed = false;

    # Only send the exit command if the process is running
    if (defined($self->io()) && defined($self->io()->processId()))
    {
        &log(TRACE, "sending exit command to process");

        eval
        {
            $self->cmdWrite(OP_EXIT);
            return true;
        }
        or do
        {
            my $oException = $EVAL_ERROR;
            my $strError = 'unable to shutdown protocol';
            my $strHint = 'HINT: the process completed all operations successfully but protocol-timeout may need to be increased.';

            if (isException(\$oException))
            {
                $iExitStatus = $oException->code();
            }
            else
            {
                if (!defined($oException))
                {
                    $oException = 'unknown error';
                }

                $iExitStatus = ERROR_UNKNOWN;
            }

            &log(WARN,
                $strError . ($iExitStatus == ERROR_UNKNOWN ? '' : sprintf(' [%03d]', $oException->code())) . ': ' .
                ($iExitStatus == ERROR_UNKNOWN ? $oException : $oException->message()) .
                ($bComplete ? "\n${strHint}" : ''));
        };

        $self->{oIo}->close();
        undef($self->{oIo});
        $bClosed = true;
    }

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

1;
