#!/usr/bin/env python
#
# Copyright (C) 2013  Branson Stephens and Chris Pankow
#
# 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.


#
# =============================================================================
#
#                                   Preamble
#
# =============================================================================
#

import pygtk
pygtk.require("2.0")
import pygst
pygst.require("0.10")
import gobject
import gst

from gstlal import simplehandler
from gstlal import pipeparts
from gstlal import datasource
from gstlal import vetosrc
from optparse import OptionParser, Option
import os

# Pad probe handler.  Useful for debugging.
def probeBufferHandler(pad,gst_buffer):
    print 'Got inside handler.'
    print 'gpsstart  = %s' % gst_buffer.timestamp
    print 'offset    = %s' % gst_buffer.offset
    print 'is gap    = %s' % gst_buffer.flag_is_set(gst.BUFFER_FLAG_GAP)
    return True

#
# =============================================================================
#
#                                   Options
#
# =============================================================================
#

parser = OptionParser(description = __doc__)
datasource.append_options(parser)

parser.add_option("--frame-type", metavar = "name", help = "Specify the non-instrumental part of the frame type. The full frame type will be constructed by prepending the instrument.")
parser.add_option("--init-time", metavar = "s", default = 0, type = "int", help = "GPS time of first file to ingest. Note: default of zero will be replaced with 'now'")
parser.add_option("--wait-time", metavar = "s", default = 64, type = "int", help = "Time to wait for next input file before giving up.")
parser.add_option("--frame-duration", metavar = "s", default = 64, type = "int", help = "Set the duration of the output frames")
parser.add_option("--dir-digits", metavar = "s", default = 5, type = "int", help = "The number of digits of the gps time in the directory name")
parser.add_option("--frames-per-file", metavar = "s", default = 1, type = "int", help = "Output frames per file")
parser.add_option("--output-channel-name", metavar = "name", help = "If an additional frame cache is requested, indicate the channel name to extract.")
parser.add_option("--output-path", metavar = "name", help = "Path to output frame files.")
parser.add_option("--output-type", metavar = "name", help = "Method of output. Valid choices are files 'files' (default) and shared memory 'shm'", default = "files")
parser.add_option("--shm-partition", metavar = "name", help = "Shared memory partition to write frames to. Required in case of output-type = shm, ignored otherwise.")
parser.add_option("--input-path", metavar = "name", help = "Path to input numpy files.")
parser.add_option("--input-prefix", metavar = "name", help = "Prefix for numpy files.")
parser.add_option("--input-ext", metavar = "name", help = "Extension for numpy files.", default = ".npy.gz")
parser.add_option("-v", "--verbose", action = "store_true", help = "Be verbose (optional).")

options, filenames = parser.parse_args()

#
# =============================================================================
#
#                                    Main
#
# =============================================================================
#

#
# Set the pipeline up
#

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

#
# Setup.
#

channel_list = [options.output_channel_name,]
channel_dict = datasource.channel_dict_from_channel_list(channel_list)
# Assume instrument is the first (only) key of the channel dict
instrument = channel_dict.keys()[0]
channel_name = channel_dict[instrument]

# Check the instrument from the input_prefix
obsStr = options.input_prefix.split('-')[0]
if not instrument.startswith(obsStr):
    raise ValueError("Output channel instrument clashes with input prefix.")

# Setup the source class
if options.init_time > 0:
    init_time = options.init_time
else:
    init_time = None
vsrc = vetosrc.vetoSource(options.input_path, options.input_prefix,
            options.input_ext, options.wait_time, init_time, options.dir_digits)

# Create the appsrc with accoutrements
appsrc = pipeparts.mkgeneric(pipeline, None, "appsrc", caps=gst.Caps(vsrc.caps), 
    format="time")
appsrc.connect('need-data', vsrc.need_data)

# XXX You can force the appsrc to spit out debug info.  But this
# doesn't seem to be very useful for logging purposes.
#gst.debug_set_threshold_for_name('appsrc', gst.LEVEL_DEBUG)
gst.debug_set_threshold_for_name('python', gst.LEVEL_INFO)

# Define the muxer.
mux = pipeparts.mkframecppchannelmux(pipeline, None, 
    frames_per_file = options.frames_per_file, 
    frame_duration = options.frame_duration)

# Link the source to the muxer. 
appsrc.get_pad("src").link(mux.get_pad(instrument + ':' + channel_name))

# Stick progress report in pipeline.
progRep = pipeparts.mkprogressreport(pipeline, mux, "progress_sink")

# For debugging, an example of how to hook up the handler.
#progRepSinkPad = progRep.get_pad("sink")
#progRepSinkPad.add_buffer_probe(probeBufferHandler)

# Final destination.
# XXX multicast?
if options.output_type == "files":
    try:
        os.makedirs(options.output_path)
    except Exception as e:
        print "Failed with %s" % e

    # Inject tags.  The framecpp_filesink element uses the tags to figure
    # out the output filename.
    tagInj = pipeparts.mktaginject(pipeline, progRep, 
        "instrument=%s,channel-name=%s" % (instrument, channel_name))

    path = options.output_path
    if path:
        fs = pipeparts.mkframecppfilesink(pipeline, tagInj, 
            frame_type = options.frame_type, path = options.output_path)
    else:
        fs = pipeparts.mkframecppfilesink(pipeline, progRep, 
            frame_type = options.frame_type)

elif options.output_type == "shm":
    lvshmsink = pipeparts.mkgeneric(pipeline, progRep, "gds_lvshmsink")
    lvshmsink.set_property("shm-name", options.shm_partition)
    lvshmsink.set_property("num-buffers", 10)
    # Let's guess the blocksize, then multiply by a fudge factor.
    # Note: This assumes the stream consists of 64-bit samples.
    # XXX This could be done better by attaching a pad probe to the lvhsmsink
    # sink pad and setting blocksize according to the size of the incoming buffers
    blocksize = vetosrc.rate * 8 * options.frame_duration * options.frames_per_file
    blocksize = blocksize * 2
    lvshmsink.set_property("blocksize", blocksize)
    # FIXME: I think this means it needs to be read at least once
    lvshmsink.set_property("buffer-mode", 1)
else:
    raise ValueError("Invalid output type.")

#
# Start the thing going.
#

pipeline.set_state(gst.STATE_PLAYING)
mainloop.run()


#
# done
#
