package Selenium::Server;

use 5.008001;
use strict;
use warnings;
use Carp qw(croak);
use LWP::UserAgent;
use Test::TCP;
use File::Slurp qw(write_file);
use File::Temp;
use File::Spec;

our $VERSION = '0.01';
$VERSION = eval $VERSION;

my $ua = LWP::UserAgent->new;

our $LATEST_VERSION;
sub latest_version {
   my $class = shift;

   unless ($LATEST_VERSION) {
       my $body = $ua->get('http://code.google.com/p/selenium/downloads/list')->content;
       my ($version) = $body =~ /selenium-server-standalone-(\d+.\d+.\d+).jar/i;
       $LATEST_VERSION = $version;
   }

   $LATEST_VERSION;
}

sub download {
   my ($class, $version, $path) = @_;

   my $name = "selenium-server-standalone-${version}.jar";
   my $url  = "http://selenium.googlecode.com/files/${name}";

   my $res = $ua->get($url);
   write_file($path, { binmode => ':raw' }, $res->content);
}

sub new {
   my ($class, %args) = @_;

   my $jar = exists $args{jar} ? $args{jar} : do {
       my $version = exists $args{version} ? $args{version} : $class->latest_version;

       my $dir  = File::Spec->tmpdir;
       my $name = "selenium-server-standalone-${version}.jar";
       my $path = File::Spec->catfile($dir, $name);

       $class->download($version, $path) unless -e $path;
       $path;
   };

   return bless { jar => $jar }, $class;
}

sub jar {
   my $self = shift;
   return $self->{jar} || '';
}

sub host {
   my $self = shift;
   return $self->{server} ? '127.0.0.1' : '';
}

sub port {
   my $self = shift;
   return $self->{server} ? $self->{server}->port : '';
}

sub start {
   my ($self, $args) = @_;

   my $server = Test::TCP->new(
       code => sub {
           my $port = shift;

           my $fh = File::Temp->new(UNLINK => 1);
           open STDOUT, '>&', $fh or croak "dup(2) failed:$!";
           open STDERR, '>&', $fh or croak "dup(2) failed:$!";

           my $cmd = sprintf 'java -jar "%s" %s -port %s', $self->jar, ($args || ''), $port;
           system $cmd;
       },
   );
   $server->start;

   $self->{server} = $server;
}

sub stop {
   my $self = shift;

   if ($self->{server}) {
       my ($host, $port) = ($self->host, $self->port);
       my $url = "http://${host}:${port}/selenium-server/driver/?cmd=shutDownSeleniumServer";
       $ua->get($url);

       delete $self->{server};
   }
}

sub DESTROY {
   my $self = shift;
   $self->stop;
}

1;

=head1 NAME

Selenium::Server - A wrapper of selenium-server-standalone.jar

=head1 SYNOPSIS

 use Selenium::Server;

 # (default) download and use latest version jar
 my $server = Selenium::Server->new;
 # specify jar
 my $server = Selenium::Server->new(jar => '/path/to/selenium-server.jar');
 # specify version
 my $server = Selenium::Server->new(version => '2.11.0');

 $server->start;
 # with arguments
 $server->start('-timeout 60 -trustAllSSLCertificates');

 my $host = $server->host; # '127.0.0.1'
 my $port = $server->port;

 $server->stop;

=head1 DESCRIPTION

Selenium::Server is a wrapper of Selenium RemoteWebDriver Server;
selenium-server-standalone-{version}.jar file.

=head1 METHODS

=over 4

=item * new()

Creates a selenium-server wrapper instance.

=item * start($args)

Starts selenium-server, with $args if specified.

=item * stop()

Stops selenium-server.

=item * host()

Returns selenium-server host. (127.0.0.1)

=item * port()

Returns selenium-server port.

=item * jar()

Returns selenium-server JAR file path.

=item * latest_version()

Returns selenium-server latest version string.

=item * download($version, $path)

Downloads selenium-server jar file specified by $version to $path.

=back

=head1 AUTHOR

NAKAGAWA Masaki E<lt>[email protected]<gt>

=head1 LICENSE

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.

=head1 SEE ALSO

L<Alien::SeleniumRC>

L<http://selenium.googlecode.com/svn/trunk/rb/lib/selenium/server.rb>

=cut