package Globus::Testing::Container;

use Globus::Core::Paths;
use Carp;
use IPC::Open3;
use strict;

sub new
{
    my $proto = shift;
    my $class = ref($proto) || $proto;
    my $self = {};
    my $args = shift;

    foreach my $arg (keys %{$args}) {
        if ($arg eq 'security') {
            $self->{SECURITY} = $args->{$arg};
        } elsif ($arg eq 'type') {
            if ($args->{$arg} =~ /c/i) {
                $self->{TYPE} = 'c';
            } elsif ($args->{$arg} =~ /java/i) {
                $self->{TYPE} = 'java';
            }
        } elsif ($arg eq 'descriptor') {
            $self->{DESCRIPTOR} = $args->{$arg};
        } elsif ($arg eq 'port') {
            $self->{PORT} = int($args->{$arg});
        } elsif ($arg eq 'timeout') {
            $self->{TIMEOUT} = int($args->{$arg});
        }
    }

    # Set defaults
    if (!exists($self->{SECURITY})) {
        $self->{SECURITY} = 0;
    }
    if (!exists($self->{TYPE})) {
        $self->{TYPE} = 'c';
    }
    if (!exists($self->{PORT})) {
        $self->{PORT} = 0;
    }
    if (!exists($self->{TIMEOUT})) {
        $self->{TIMEOUT} = 120;
    }

    bless $self, $class;
    return $self;
}

sub contact
{
    my $self = shift;

    if (!exists $self->{CONTACT}) {
        $self->start_container();
    }

    return $self->{CONTACT};
}

sub start_container
{
    my $self = shift;

    if ($self->{TYPE} eq 'c') {
        $self->start_c_container();
    } elsif ($self->{TYPE} eq 'java') {
        $self->start_java_container();
    } else {
        croak("Unknown container type " . $self->{TYPE} . "\n");
    }
}

sub start_c_container
{
    my $self = shift;
    my $container = $Globus::Core::Paths::bindir . '/globus-wsc-container';
    my @container_args = ($container);

    if (! $self->{SECURITY}) {
        push(@container_args, '-nosec');
    }
    push(@container_args, '-port', $self->{PORT});

    $self->{CONTAINER_PID} = open3(
        $self->{CONTAINER_IN},
        $self->{CONTAINER_OUT},
        $self->{CONTAINER_ERR},
        @container_args);

    if ($self->{CONTAINER_PID} == 0)
    {
        croak("Unable to start container\n");
    }

    local $SIG{ALRM} = sub { die "alarm\n" };
    eval {
        my $line;
        alarm $self->{TIMEOUT};

        while ($line = readline($self->{CONTAINER_OUT})) {
            if ($line =~ m/Contact: (\S+)/) {
                $self->{CONTACT} = $1;
                last;
            }
            my $rin = '';
            vec($rin,fileno($self->{CONTAINER_OUT}),1) = 1;
            select($rin, undef, undef, 1);
        }
    };
    alarm 0;
    if ($@ eq "alarm" || !exists($self->{CONTACT})) {
        print "# Failed to start container\n";
        $self->terminate();
    }
    if ($self->{CONTAINER_IN}) {
        close($self->{CONTAINER_IN});
        delete $self->{CONTAINER_IN};
    }
    if ($self->{CONTAINER_OUT}) {
        close($self->{CONTAINER_OUT});
        delete $self->{CONTAINER_OUT};
    }
    if ($self->{CONTAINER_ERR}) {
        close($self->{CONTAINER_ERR});
        delete $self->{CONTAINER_OUT};
    }
}

sub start_java_container
{
    my $self = shift;
    my $container = $Globus::Core::Paths::bindir . '/globus-start-container';
    my @container_args = ($container);

    if (! $self->{SECURITY}) {
        push(@container_args, '-nosec');
    }
    if (exists $self->{DESCRIPTOR}) {
        push(@container_args, '-containerDesc', $self->{DESCRIPTOR});
    }
    push(@container_args, '-p', $self->{PORT});

    $self->{CONTAINER_PID} = open3(
        $self->{CONTAINER_IN},
        $self->{CONTAINER_OUT},
        $self->{CONTAINER_ERR},
        @container_args);

    if ($self->{CONTAINER_PID} == 0)
    {
        croak("Unable to start container\n");
    }
    local $SIG{ALRM} = sub { die "alarm\n" };
    eval {
        my $line;
        alarm $self->{TIMEOUT};

        while ($line = readline($self->{CONTAINER_OUT})) {
            if ($line =~ m/Starting SOAP server at: (\S+)wsrf\/services/) {
                $self->{CONTACT} = "$1";
                last;
            }
            my $rin = '';
            vec($rin,fileno($self->{CONTAINER_OUT}),1) = 1;
            select($rin, undef, undef, 1);
        }
    };
    alarm 0;
    if ($@ eq "alarm" || !exists($self->{CONTACT})) {
        $self->terminate();
    }
    if ($self->{CONTAINER_IN}) {
        close($self->{CONTAINER_IN});
        delete $self->{CONTAINER_IN};
    }
    if ($self->{CONTAINER_OUT}) {
        close($self->{CONTAINER_OUT});
        delete $self->{CONTAINER_OUT};
    }
    if ($self->{CONTAINER_ERR}) {
        close($self->{CONTAINER_ERR});
        delete $self->{CONTAINER_OUT};
    }
}

sub terminate
{
    my $self = shift;

    if (exists $self->{CONTAINER_ERR})
    {
        my $line;

        while ($line = readline($self->{CONTAINER_ERR}))
        {
            print "# $line\n"
        }
    }
    if (exists $self->{CONTAINER_PID}) {
        kill(15, $self->{CONTAINER_PID});
        delete $self->{CONTAINER_PID};
    }
    if (exists $self->{CONTAINER_IN})
    {
        close($self->{CONTAINER_IN});
        delete $self->{CONTAINER_IN};
    }
    if (exists $self->{CONTAINER_OUT})
    {
        close($self->{CONTAINER_OUT});
        delete $self->{CONTAINER_OUT};
    }
    if (exists $self->{CONTAINER_ERR})
    {
        close($self->{CONTAINER_ERR});
        delete $self->{CONTAINER_ERR};
    }
    if (exists $self->{CONTACT})
    {
        delete $self->{CONTACT};
    }
}

sub DESTROY
{
    my $self = shift;
    $self->terminate();
}

1;
