#!/usr/bin/ruby
# encoding: UTF-8

# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
#
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors.
#
# BigBlueButton is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with BigBlueButton.  If not, see <http://www.gnu.org/licenses/>.

require File.expand_path('../../../lib/recordandplayback', __FILE__)
require File.expand_path('../../../lib/recordandplayback/edl', __FILE__)

require 'optimist'
require 'yaml'
require 'nokogiri'
require 'erb'

opts = Optimist::options do
  opt :meeting_id, "Meeting id to process", :type => String
  opt :stderr, "Log output to stderr"
end
Optimist::die :meeting_id, "must be provided" unless opts[:meeting_id]
meeting_id = opts[:meeting_id]

start_real_time = nil
begin
  m = /-(\d+)$/.match(meeting_id)
  start_real_time = m[1].to_i
end

# Load parameters and set up paths
props = YAML::load(File.open(File.expand_path('../../bigbluebutton.yml', __FILE__)))
screenshare_props = YAML::load(File.open(File.expand_path('../../screenshare.yml', __FILE__)))

recording_dir = props['recording_dir']
playback_dir = screenshare_props['playback_dir']
process_dir = "#{recording_dir}/process/screenshare/#{meeting_id}"
raw_archive_dir = "#{recording_dir}/raw/#{meeting_id}"
donefile = "#{recording_dir}/status/processed/#{meeting_id}-screenshare.done"
log_file = "#{props['log_dir']}/screenshare/process-#{meeting_id}.log"

if opts[:stderr]
  BigBlueButton.logger = Logger.new(STDERR)
else
  BigBlueButton.logger = Logger.new(log_file)
end
logger = BigBlueButton.logger

if File.exists?(donefile)
  logger.warn "This processing script has already been run"
  exit 0
end

begin

FileUtils.mkdir_p(process_dir)

logger.info "Reading basic recording information"
events = Nokogiri::XML(File.open("#{raw_archive_dir}/events.xml"))
initial_timestamp = nil
final_timestamp = nil
metadata = events.at_xpath('/recording/metadata')
meetingName = metadata['meetingName']
begin
  event = events.at_xpath('/recording/event[position()=1]')
  initial_timestamp = event['timestamp'].to_i
  event = events.at_xpath('/recording/event[position()=last()]')
  final_timestamp = event['timestamp'].to_i
end

video_edl = []
begin
  logger.info "Generating video events list"

  # Webcams
  webcam_edl = BigBlueButton::Events.create_webcam_edl(events, raw_archive_dir, props['show_moderator_viewpoint'])
  logger.debug "Webcam EDL:"
  BigBlueButton::EDL::Video.dump(webcam_edl)

  # Deskshare
  deskshare_edl = BigBlueButton::Events.create_deskshare_edl(events, raw_archive_dir)
  logger.debug "Deskshare EDL:"
  BigBlueButton::EDL::Video.dump(deskshare_edl)

  video_edl = BigBlueButton::EDL::Video.merge(webcam_edl, deskshare_edl)
end

logger.debug "Merged Video EDL:"
BigBlueButton::EDL::Video.dump(video_edl)

start_time = BigBlueButton::Events.first_event_timestamp(events)
end_time = BigBlueButton::Events.last_event_timestamp(events)

logger.info "Applying recording start/stop events to video"
video_edl = BigBlueButton::Events.edl_match_recording_marks_video(video_edl, events, start_time, end_time)
logger.debug "Trimmed Video EDL:"
BigBlueButton::EDL::Video.dump(video_edl)

audio_edl = []
logger.info "Generating audio events list"
audio_edl = BigBlueButton::AudioEvents.create_audio_edl(events, raw_archive_dir)
logger.debug "Audio EDL:"
BigBlueButton::EDL::Audio.dump(audio_edl)

logger.info "Applying recording start/stop events to audio"
audio_edl = BigBlueButton::Events.edl_match_recording_marks_audio(audio_edl, events, start_time, end_time)
logger.debug "Trimmed Audio EDL:"
BigBlueButton::EDL::Audio.dump(audio_edl)

logger.info "Rendering audio"
audio = "#{process_dir}/audio.#{BigBlueButton::EDL::Audio::WF_EXT}"
if File.exist?(audio)
  logger.warn "  Skipping rendering audio ... File already exists"
else
  audio = BigBlueButton::EDL::Audio.render(audio_edl, "#{process_dir}/audio")
end

layout = screenshare_props['layout']

logger.info "Rendering video"
video = "#{process_dir}/video.#{BigBlueButton::EDL::Video::WF_EXT}"
if File.exist?(video)
  logger.warn "  Skipping rendering video ... File already exists"
else
  video = BigBlueButton::EDL::Video.render(video_edl, layout, "#{process_dir}/video")
end

logger.info "Encoding output files to #{screenshare_props['formats'].length} formats"
screenshare_props['formats'].each_with_index do |format, i|
  logger.info "  #{format[:mimetype]}"
  filename = "#{process_dir}/screenshare-#{i}.#{format[:extension]}"
  if File.exist?(filename)
    logger.warn "    Skipping encode ... File already exists"
  else
    filename = BigBlueButton::EDL.encode(audio, video, format, "#{process_dir}/screenshare-#{i}", 0)
  end
end

logger.info("Generating closed captions")
ret = BigBlueButton.exec_ret('utils/gen_webvtt', '-i', raw_archive_dir, '-o', process_dir)
if ret != 0
  raise "Generating closed caption files failed"
end
captions = JSON.load(File.new("#{process_dir}/captions.json", 'r'))

# Publishing support files

logger.info "Generating index page"
index_template = "#{playback_dir}/index.html.erb"
index_erb = ERB.new(File.read(index_template))
index_erb.filename = index_template
File.open("#{process_dir}/index.html", 'w') do |index_html|
  index_html.write(index_erb.result)
end

logger.info "Generating metadata xml"
duration = BigBlueButton::Events.get_recording_length(events)
meeting_xml = events.at_xpath('/recording/meeting')
breakout_xml = events.at_xpath('/recording/breakout')
breakout_rooms_xml = events.at_xpath('/recording/breakoutRooms')
metadata_xml = Nokogiri::XML::Builder.new do |xml|
  xml.recording {
    xml.id(meeting_id)
    xml.state('available')
    xml.published('true')
    xml.start_time(start_real_time)
    xml.end_time(start_real_time + final_timestamp - initial_timestamp)
    xml << meeting_xml.to_xml unless meeting_xml.nil?
    xml << breakout_xml.to_xml unless breakout_xml.nil?
    xml << breakout_rooms_xml.to_xml unless breakout_rooms_xml.nil?
    xml.playback {
      xml.format('screenshare')
      xml.link("#{props['playback_protocol']}://#{props['playback_host']}/recording/screenshare/#{meeting_id}/")
      xml.duration(duration)
    }
    xml.meta {
      metadata.attributes.each do |k, v|
        xml.method_missing(k, v)
      end
    }
  }
end
File.open("#{process_dir}/metadata.xml", 'w') do |metadata_file|
  metadata_file.write(metadata_xml.to_xml)
end

logger.info "Copying css and js support files"
FileUtils.cp_r("#{playback_dir}/css", process_dir)
FileUtils.cp_r("#{playback_dir}/js", process_dir)
FileUtils.cp_r("#{playback_dir}/video-js", process_dir)

logger.info "Processing successfully completed, writing done file"

File.open(donefile, 'w') do |done|
  done.write("Processed #{meeting_id}")
end

rescue Exception => e
  warn e.message
  logger.error e.message
  e.backtrace.each do |traceline|
    logger.error traceline
  end
  exit 1
end
