#!/usr/bin/perl
use warnings;
use strict;

use Getopt::Long;
use DateTime::Format::ISO8601;
use Pod::Usage;
use File::Basename qw{};

use Carp;

use Elive;
use Elive::Util;
use Elive::Entity;
use Elive::Entity::Meeting;
use Elive::Entity::MeetingParameters;
use Elive::Entity::ServerParameters;
use Elive::Entity::Participant;
use Elive::Entity::ParticipantList;
use Elive::Entity::Preload;
use Elive::Entity::User;

=head1 NAME

elive_raise_meeting

=head1 SYNOPSIS

  elive_raise_meeting http://myserver.com/my-site[:port] userid[=role] [userid[=role]...]

  Authentication

    -user someAdminUser         # server login user
    -pass somepass              # server login password

  Basic

    -facilitator  userId
    -name         'meeting name'
    -meeting_pass otherpass      # meeting password
    -start  'YYYY-MM-DD HH:MM'   # start time
    -end '[YYYY-MM-DD] HH::MM'   # end time

  Meeting Settings

    -boundary     0|15|30|..     # number minutes participants can arrive before
                                 # or leave after the scheduled meeting times.
    -max_talkers  n              # max no. of simultaneous talkers
    -recording    on|off|auto    # set recording status
    -raise_hands                 # automatically raise hands on entry
    -seats        count          # number of seats to reserve

  Preloads

    -upload      local_file      # upload a meeting preload file
    -import_from_server  file    # import a meeting preload from the server
    -use_preload preload_id      # use an existing preload for the meeting

=head1 DESCRIPTION

Creates a meeting on an Elluminate Live (c) server

=head1 SEE ALSO

    perldoc Elive
    http://search.cpan.org/dist/Elive

=cut

my $username = 'serversupport';
my $password;
my $debug;
my $facilitator;
my $start_str;
my $end_str;
my $meeting_name = 'elive test meeting';
my $meeting_password;
my $max_talkers = 1;
my $recording = 'auto';
my $raise_hands;
my $import;
my $private;
my $upload;
my $preload_opt;
my $url;
my $help;
my $boundary = 15;
my $seats;

main(@ARGV) unless caller;

sub main {

    local(@ARGV) = @_;

    GetOptions(
	'name|meeting_name=s' => \$meeting_name,
	'username|user=s' => \$username,
	'password|pass=s' => \$password,
	'facilitator|moderator=s' => \$facilitator,
	'start=s' => \$start_str,
	'end=s' => \$end_str,
	'meeting_password|meeting_pass=s' => \$meeting_password,
	'max_talkers=i' => \$max_talkers,
	'raise_hands' => \$raise_hands,
	'private' => \$private,
	'debug=i' => \$debug,
	'help|?' => \$help,
	'upload=s' => \$upload,
	'import_from_server=s' => \$import,
	'use_preload=i' => \$preload_opt,
	'recording' => \$recording,
	'boundary=i' => \$boundary,
	'seats=i' => \$seats,
    )
	&& (($help && pod2usage(2)) || ($url = shift @ARGV))
	or die pod2usage(1);

    $recording = uc($recording);
    die pod2usage("recording mode must be 'on', 'off' or 'auto'")
	unless $recording =~ m{^ ON|OFF|AUTO|REMOTE $}x;
    $recording =~ s{^ AUTO $}{REMOTE}x;

    Elive->debug($debug);

    if ($debug) {
	$SIG{__WARN__} = \&Carp::cluck if $debug > 1;
	$SIG{__DIE__} = \&Carp::confess;
    }

    $url ||= Elive::Util::prompt('Elluminate Server: '),
    $username ||= Elive::Util::prompt('Username: '),
    $password ||= Elive::Util::prompt('Password: ', password => 1),

    our $connection = Elive->connect($url, $username, $password);

    my $start = $start_str
	? DateTime::Format::ISO8601->parse_datetime($start_str)
	: DateTime->now->clone->add(minutes => 15);

    my $end = $end_str
	? DateTime::Format::ISO8601->parse_datetime($end_str,
						    base_datetime => $start)
	: $start->clone->add(minutes => 45);

    die "end time must be later than start time"
	unless ($end->epoch > $start->epoch);

    my $upload_data;

    if ($upload) {
	open(UPLOAD, '<', $upload)
	    or die "unable to open $upload: $!";

	$upload_data = join('', <UPLOAD>);
	close (UPLOAD);
    }

    my $facilitator_obj = $facilitator
	? _get_user($facilitator)
	: Elive->login;

    my @participants;

    foreach my $spec (@ARGV) {

	#
	# parse a participant spec: user[=roleId]
	#
	my $user_spec;
	my $role;

	if (($user_spec,$role) = ($spec =~ m{^([^=]+)=([^=]+)$}x)) {
	    die "non-numeric role: $spec"
		unless $role =~ m{^\d+$};
	}
	else {
	    $user_spec = $spec;
	}

	$role = 3 if (!defined $role);

	die "role ($role) not in range 0-3: $spec"
	    unless ($role >= 0 && $role <= 3);

	die "null username: $spec"
	    if ($user_spec eq '');

	my $user_obj = _resolve_user($user_spec);

	if (grep {$_->{user} eq $user_obj} @participants) {
	    warn "duplicate user: $user_spec"
	}
	else {
	    push (@participants, 
		  Elive::Entity::Participant->new({
		      user => $user_obj,
		      role => $role,
		      }),
		);
	}
    }

    my $existing_preload;

    if ($preload_opt) {

	if ($preload_opt =~ m{^\d+$}) {
	    #
	    # numeric - assume it's a preload id
	    #
	    $existing_preload = Elive::Entity::Preload->retrieve([$preload_opt]);
	}
	else {
	    #
	    # preload filter appears to be ineffective. Shouldn't get here
	    # anyway - option has been restricted to integer
	    die "non numeric preload id";
	    #
	    # treat it as a filename.
	    #
	    my $preloads =  Elive::Entity::Preload->list(filter => "name = $preload_opt");
	    #
	    # take most recent
	    #
	    ($existing_preload) = sort {$b->id cmp $a->id} @$preloads;
	}
	
	die "no existing preload: $preload_opt"
	    unless $existing_preload;
    }

    my %meeting_data = (
	    name => $meeting_name,
	    facilitatorId => $facilitator_obj->stringify,
	    start => $start->epoch . '000',
	    end => $end->epoch . '000',
	);

    $meeting_data{password} = $meeting_password
	if defined $meeting_password;

    $meeting_data{seats} = $seats
	if defined $seats;

    $meeting_data{privateMeeting} = $private
	if defined $private;

    my $meeting = Elive::Entity::Meeting->insert(\%meeting_data);
    
    print "created meeting: ".$meeting->name." with id ".$meeting->meetingId."\n";
    $meeting->add_preload($existing_preload)
	if $existing_preload;

    do {
	
	my $meeting_parameters
	    = Elive::Entity::MeetingParameters->retrieve([$meeting->meetingId])
	    or die "Unable to retrieve meeting parameters for this meeting";
	
	$meeting_parameters->maxTalkers($max_talkers)
	    if defined $max_talkers;

	$meeting_parameters->raiseHandOnEnter($raise_hands)
	    if defined $raise_hands;

	$meeting_parameters->recordingStatus($recording);

	$meeting_parameters->update;
    };


    if ($boundary) {
	
	my $server_parameters
	    = Elive::Entity::ServerParameters->retrieve([$meeting->meetingId])
	    or die "Unable to retrieve server parameters for this meeting";

	$server_parameters->boundaryMinutes($boundary)
	    if defined $boundary;

	$server_parameters->update;
    };

    if (@participants) {

	setup_participants($meeting->meetingId, @participants);

    }
    else {
	print "no participants\n";
    }

    #
    # to do - allow multiple uploads
    #
    if ($upload) {

	my $upload_basename = File::Basename::basename($upload);

	my $preload = Elive::Entity::Preload->upload({
	    name => $upload_basename,
	    data => $upload_data,
	    ownerId => $facilitator_obj->stringify,
	 });

	printf("adding '%s' preload: %s (%s)\n",
	       $preload->type, $preload->name, $preload->mimeType);

	$meeting->add_preload($preload);
    }

    #
    # to do - allow multiple imports
    #
    if ($import) {

	print "importing (from server): $import\n";

	my $preload = Elive::Entity::Preload->import_from_server({
	    fileName => $import,
	    ownerId => $facilitator_obj->stringify,
	 });

	printf("adding '%s' preload: %s (%s)\n",
	       $preload->type, $preload->name, $preload->mimeType);

	$meeting->add_preload($preload);
    }

    print "meeting address: ".$meeting->web_url."\n";

    Elive->disconnect;

    return $meeting;
}

########################################################################

sub setup_participants {
    my $meeting_id = shift;
    my @participants = @_;
	#
	# Create participant list
	#
	my $participant_list = Elive::Entity::ParticipantList
	    ->insert({meetingId => $meeting_id,
		      participants => \@participants,
		     });

	my @participants_echo;
	my $participant_objs = $participant_list->participants;

	foreach (@$participant_objs) {
	    my $user_obj = $_->user;
	    my $loginName = $user_obj->loginName;
	    my $email = $user_obj->email;

	    my $participant_str = ($loginName || $user_obj->userId);
	    $participant_str .= ' <'.$email.'>'
		if $email;
	    push (@participants_echo, $participant_str);
	}

	print "participants: ".join(', ', @participants_echo)."\n";
}

########################################################################

sub _resolve_user {
    my $user_spec = shift;

    die 'usage: _get_user($user_name_or_id)'
	unless (defined $user_spec && $user_spec ne '');
    #
    # Try by username
    #
    my $user;

    if ($user_spec =~ m{^\d+$}) {
	#
	# Numeric id. Assume it's a user id
	#
	$user = Elive::Entity::User->construct({userId => $user_spec});
    }
    else {
	#
	# Treat non-numeric as a loginName
	#
	$user = Elive::Entity::User->get_by_loginName($user_spec);
    }

    die "unkown user: $user_spec"
	unless ($user);

    return $user;
}
