#!/usr/bin/env python
#
# Copyright (C) 2010--2014  Kipp Cannon, Chad Hanna
#
# 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.
#

### A program to create some prior likelihood data to seed an offline analysis

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


from optparse import OptionParser
import numpy
import scipy

from lal import series

from ligo.lw import ligolw
from ligo.lw import lsctables
from ligo.lw import array as ligolw_array
from ligo.lw import param as ligolw_param
from ligo.lw import utils as ligolw_utils
from ligo.lw.utils import process as ligolw_process


from gstlal import far
from gstlal import svd_bank
from gstlal import templates

@ligolw_array.use_in
@ligolw_param.use_in
@lsctables.use_in
class LIGOLWContentHandler(ligolw.LIGOLWContentHandler):
	pass

__author__ = "Chad Hanna <chad.hanna@ligo.org>"
__version__ = "git id %s" % ""	# FIXME
__date__ = ""	# FIXME


#
# =============================================================================
#
#                                 Command Line
#
# =============================================================================
#


def parse_command_line():
	parser = OptionParser(
		version = "Name: %%prog\n%s" % "" # FIXME
	)
	parser.add_option("-v", "--verbose", action = "store_true", help = "Be verbose.")
	# FIXME:  default must be identical to gstlal_inspiral's default
	parser.add_option("--coincidence-threshold", metavar = "value", type = "float", default = 0.005, help = "Set the coincidence window in seconds (default = 0.005).  The light-travel time between instruments will be added automatically in the coincidence test.")
	# FIXME:  default must be identical to gstlal_inspiral's default
	parser.add_option("--min-instruments", metavar = "count", type = "int", default = 2, help = "Set the minimum number of instruments that must contribute triggers to form a candidate (default = 2).")
	parser.add_option("--write-likelihood", metavar = "filename", help = "Write merged raw likelihood data to this file.")
	parser.add_option("--instrument", action = "append", help = "Append to a list of instruments to create dist stats for.  List must be whatever instruments you intend to analyze.")
	parser.add_option("-p", "--background-prior", metavar = "N", default = 1, type = "float", help = "Include an exponential background prior with the maximum bin count = N, default 1")
	parser.add_option("--df", metavar = "N", default = 40, help = "set the degrees of freedom for the background chisq prior: default 40. You can also use template bandwidth to set this by setting it to 'bandwidth'")
	parser.add_option("--svd-file", metavar = "filename", help = "The SVD file to read the template ids from")
	parser.add_option("--mass-model-file", metavar = "filename", help = "The mass model file to read from (hdf5 format)")
	parser.add_option("--dtdphi-file", metavar = "filename", help = "dtdphi snr ratio pdfs to read from (hdf5 format). Default passed by gstlal_inspiral_pipe, but not when run as a standalone program.")
	parser.add_option("--psd-xml", type = "string", help = "Specify a PSD to use for computing template bandwidth. Required if df is bandwidth")
	options, filenames = parser.parse_args()

	process_params = dict(options.__dict__)

	if not options.instrument:
		raise ValueError("must specify at least one --instrument")
	options.instrument = set(options.instrument)

	if options.min_instruments < 1:
		raise ValueError("--min-instruments must be >= 1")
	if options.min_instruments > len(options.instrument):
		raise ValueError("--min-instruments is greater than the number of unique --instrument's")

	if filenames:
		raise ValueError("unrecognized arguments after options: %s" % " ".join(filenames))

	if options.df == "bandwidth" and options.psd_xml is None:
		raise ValueError("Must specify psd xml file if using bandwidth to set degrees of freedom")
	psd = {}
	if options.psd_xml:
		for ifo, p in series.read_psd_xmldoc(ligolw_utils.load_filename(options.psd_xml, verbose = options.verbose, contenthandler = series.PSDContentHandler)).items():
			f = numpy.arange(len(p.data.data)) * p.deltaF
			psd[ifo] = scipy.interpolate.interp1d(f, p.data.data)


	template_ids = []
	horizon_factors = {}
	bandwidths = []
	for n, bank in enumerate(svd_bank.read_banks(options.svd_file, contenthandler = LIGOLWContentHandler, verbose = options.verbose)):
		template_ids += [row.template_id for row in bank.sngl_inspiral_table]
		# FIXME don't hard code
		if options.df == "bandwidth":
			for ifo in psd:
				bandwidths += [templates.bandwidth(row.mass1, row.mass2, row.spin1z, row.spin2z, f_min = 10.0, f_max = row.f_final, delta_f = 0.25, psd = psd[ifo])]
		horizon_factors.update(bank.horizon_factors)
	if options.df == "bandwidth":
		# NOTE the 4000 is tuned by looking at real data distributions
		options.df = int(4000. / min(bandwidths))

	return options, process_params, filenames, template_ids, horizon_factors


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


#
# command line
#


options, process_params, filenames, template_ids, horizon_factors = parse_command_line()


#
# initialize output document (records process start time)
#


xmldoc = ligolw.Document()
xmldoc.appendChild(ligolw.LIGO_LW())
process = ligolw_process.register_to_xmldoc(xmldoc, u"gstlal_inspiral_create_prior_diststats", ifos = options.instrument, paramdict = process_params)


#
# create parameter distribution priors
#


rankingstat = far.RankingStat(template_ids = template_ids, instruments = options.instrument, delta_t = options.coincidence_threshold, min_instruments = options.min_instruments, population_model_file = options.mass_model_file, dtdphi_file = options.dtdphi_file, horizon_factors = horizon_factors)

if options.background_prior > 0:
	rankingstat.denominator.add_noise_model(number_of_events = options.background_prior, df = int(options.df))

# Add the numerator
rankingstat.numerator.add_signal_model()


#
# record results in output file
#


far.gen_likelihood_control_doc(xmldoc, rankingstat, None)


#
# done
#


ligolw_process.set_process_end_time(process)
ligolw_utils.write_filename(xmldoc, options.write_likelihood, gz = options.write_likelihood.endswith(".gz"), verbose = options.verbose)