#!/usr/bin/env python
#
# Copyright (C) 2010 Leo Singer
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or (at your
# option) any later version.
#
# This program 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 General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
"""
Advanced LIGO mock data server.

Produces mock data frames under the current working directory, as in:
./H1/H-H1_MOCK-0000/H-H1_MOCK-000000256-16.gwf

Also, if the --port option is provided, this program also deploys a TCP server
that provides 

To retrieve streaming h(t) and print as ASCII, try the following:
 1. Launch with the optional TCP server mode, as in:
   $ gstlal_mock_data_server --port 6000
 2. Launch the client pipeline with:
   $ gst-launch tcpclientsrc port=6000 ! gdpdepay ! lal_nxydump ! filesink location=/dev/stdout
"""
__author__ = "Leo Singer <leo.singer@ligo.org>"


#
# Parse command line options
#

from optparse import OptionParser, Option

opts, args = OptionParser(usage = "%prog [options]" + __doc__, option_list = [
	Option("--port", "-p", type="int", help="Enable streaming h(t) on this port."),
	Option("--instrument", "-i", default="H1", help="Name of instrument (default=H1)"),
	Option("--gps-start-time", "-s", default=0, type="int", metavar="INT", help="GPS start time in seconds (default=0)"),
	Option("--gps-end-time", "-e", type="int", metavar="INT", help="GPS start time in seconds (default=infinity)"),
]).parse_args()


#
# Late imports
#

from gstlal.pipeutil import gst, gobject
from gstlal import pipeparts
from gstlal.simplehandler import Handler


# Output location parameters
channel_name = 'MOCK-STRAIN'

# Sample rate, block size parameters
fs = 16384 # sample rate in Hz
unit_size = 8 # data unit size in bytes
frame_duration = 16 # frame file duration in seconds
max_streaming_buf_size = 1024 # max length in bytes for streaming buffers

# For streaming h(t), produce blocks that begin on integer nanosecond boundaries
# because these have exactly representable timestamps.
min_streaming_buf_size = unit_size * fs / gst.util_greatest_common_divisor(fs, gst.SECOND)
streaming_buf_size = (max_streaming_buf_size // min_streaming_buf_size) * min_streaming_buf_size

#
# Build pipeline
#

pipeline = gst.Pipeline("mock_data_server")
mainloop = gobject.MainLoop()
handler = Handler(mainloop, pipeline)

# Show debug messages from tcpserversink
gst.debug_set_threshold_for_name('tcpserversink', gst.LEVEL_DEBUG)

# Make mock h(t)
src = pipeparts.mkfakeadvLIGOsrc(pipeline, instrument = opts.instrument, channel_name = channel_name, blocksize = streaming_buf_size)
src = pipeparts.mkprogressreport(pipeline, src, "progress_hoft")
src = pipeparts.mktee(pipeline, src)

# writing buffers to frame files
head = pipeparts.mkqueue(pipeline, src, max_size_time = 2 * frame_duration * gst.SECOND, max_size_buffers = 0, max_size_bytes = 0)
head = pipeparts.mkframecppchannelmux(pipeline, {"%s:%s" % (opts.instrument, channel_name): head}, frame_duration = frame_duration, frames_per_file = 1)
pipeparts.mkframecppfilesink(pipeline, head, frame_type = "%s_MOCK" % opts.instrument)

# Create optional TCP server
if opts.port is not None:
	head = pipeparts.mkqueue(pipeline, src, max_size_time = 0, max_size_buffers = 3, max_size_bytes = 0)
	head = pipeparts.mkgeneric(pipeline, head, "gdppay")
	head = pipeparts.mktcpserversink(pipeline, head, port = opts.port)

#
# Run pipeline
#

seek_start_type = gst.SEEK_TYPE_SET
seek_start_time = opts.gps_start_time * gst.SECOND
if opts.gps_end_time is None:
	seek_stop_type = gst.SEEK_TYPE_NONE
	seek_stop_time = -1
else:
	seek_stop_type = gst.SEEK_TYPE_SET
	seek_stop_time = opts.gps_end_time * gst.SECOND

if pipeline.set_state(gst.STATE_READY) == gst.STATE_CHANGE_FAILURE:
	raise RuntimeError("pipeline failed to enter READY state")
event = gst.event_new_seek(1., gst.FORMAT_TIME, gst.SEEK_FLAG_NONE, seek_start_type, seek_start_time, seek_stop_type, seek_stop_time)

for elem in pipeline.iterate_sources():
	if not elem.send_event(event):
		raise RuntimeError("Element did not accept seek event: %r" % elem)

if pipeline.set_state(gst.STATE_PLAYING) == gst.STATE_CHANGE_FAILURE:
	raise RuntimeError("pipeline railed to enter PLAYING state")
mainloop.run()
