#!/usr/bin/env python
#
#  Copyright (C) 2010-2011, 2016, 2019  Smithsonian Astrophysical Observatory
#
#
#  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.
#

"""
Script:
  make_instmap_weights

  Create a spectral weights table in a two-column ASCII file containing energy
  bin center in the first column and normalized weighting in the second column.

  This script is run from the Unix command line, within the CIAO environment.


Example syntax:

   make_instmap_weights outfile="weights.txt" model="xsphabs.abs1*powlaw1d.p1" \
   paramvals="abs1.nh=1.3;p1.gamma=1.7" emin=0.2 emax=8.0 ewidth=0.25

"""

import sys
import os
import logging

import paramio
import sherpa.astro.ui as sherpa
from sherpa_contrib.utils import save_instmap_weights

from ciao_contrib.logger_wrapper import initialize_logger, \
    make_verbose_level, set_verbosity, get_verbosity, handle_ciao_errors
from ciao_contrib.param_wrapper import open_param_file

toolname = "make_instmap_weights"
__revision__ = "06 June 2019"

initialize_logger(toolname)

v3 = make_verbose_level(toolname, 3)
v5 = make_verbose_level(toolname, 5)


def get_par(args):
    """ Get make_instmap_weights parameters from parameter file """

    pinfo = open_param_file(args, toolname=toolname)
    pfile = pinfo["fp"]

    # Common parameters:
    params = {}

    # ascii file containing columns of energy and weighting
    params["outfile"] = paramio.pgetstr(pfile, "outfile")

    # sherpa spectral model
    params["mdl"] = None
    params["mdl"] = paramio.pgetstr(pfile, "model")
    params["mdl"] = params["mdl"].strip("\"' ")
    if params["mdl"] == "":
        raise ValueError("Sherpa model definition is required")

    # spectral model parameters
    params["pvals"] = None
    params["pvals"] = paramio.pgetstr(pfile, "paramvals")
    params["pvals"] = params["pvals"].strip("\"'; ")
    if params["pvals"] == "":
        raise ValueError("Model parameters must be defined")

    # for XSpec models that depend on photoelectric cross-section
    # and solar abundances
    params["xsect"] = paramio.pgetstr(pfile, "xsect")
    params["abund"] = paramio.pgetstr(pfile, "abund")

    # load emin, emax, and bin width
    params["emin"] = None
    if paramio.pgetstr(pfile, "emin").upper() != "INDEF":
        params["emin"] = paramio.pgetd(pfile, "emin")

    params["emax"] = None
    if paramio.pgetstr(pfile, "emax") != "INDEF":
        params["emax"] = paramio.pgetd(pfile, "emax")

    params["de"] = None
    if paramio.pgetstr(pfile, "ewidth") != "INDEF":
        params["de"] = paramio.pgetd(pfile, "ewidth")

    # set clobber of ascii file
    params["clobber"] = paramio.pgetstr(pfile, "clobber") == "yes"

    # set verbosity
    params["verbose"] = paramio.pgeti(pfile, "verbose")

    # validate emin
    if params["emin"] is None:
        raise ValueError("emin cannot be set to 'INDEF'")
    elif params["emin"] >= params["emax"]:
        raise ValueError("emin cannot be greater than or equal to emax")

    # validate emax
    if params["emax"] is None:
        raise ValueError("emax cannot be set to 'INDEF'")
    elif params["emax"] <= params["emin"]:
        raise ValueError("emax cannot be less than or equal to emin")

    # validate ewidth
    if params["de"] is None:
        raise ValueError("ewidth cannot be set to 'INDEF'")
    elif params["de"] > abs(params["emax"] - params["emin"]):
        raise ValueError("ewidth cannot be greater than emax-emin")
    elif params["de"] == 0.:
        raise ValueError("ewidth cannot be equal to zero")

    paramio.paramclose(pfile)
    return params


def printverbose():
    """Produce output for verbosity levels"""

    if get_verbosity() == 0:
        logging.getLogger("sherpa").setLevel(logging.ERROR)

    v3("Model used: ")
    v3(sherpa.get_model())

    v5("Dataspace produced: ")
    v5(sherpa.get_data())
    v5("XSpec solar abundances")
    v5(sherpa.get_xsabund())
    v5("XSpec photoelectric cross-section")
    v5(sherpa.get_xsxsect())


@handle_ciao_errors(toolname, __revision__)
def make_instmap_weights(args):
    """Do the work"""

    params = get_par(args)
    set_verbosity(params["verbose"])

    # Since the XSPEC settings may change over time, always set the
    # values to the user's settings.
    #
    # There was a comment about changing the XSPEC cosmology settings,
    # but no attempt to actually do so. Should there be? (DJB can not
    # think of a case where it would matter here, but admits to not
    # having thought about it too much).
    #
    sherpa.set_xsabund(params["abund"])
    sherpa.set_xsxsect(params["xsect"])

    # create a synthetic data ID to appropriately bin a model
    sherpa.dataspace1d(params["emin"], params["emax"], params["de"])
    sherpa.set_model(params["mdl"])

    for par in params["pvals"].split(";"):
        par = par.split("=")
        sherpa.set_par(par[0], val=par[1])

    printverbose()
    save_instmap_weights(params["outfile"], params["clobber"])


if __name__ == "__main__":
    make_instmap_weights(sys.argv)
