# Licensed under a 3-clause BSD style license - see LICENSE.rst
"""
Module that implements a background noise calculator (BNC) for the pandeia ETC engine
"""

import numpy as np
from warnings import warn

from .scene import Scene
from .custom_exceptions import EngineInputError
from .calc_utils import build_empty_scene
from .etc3D import setup, make_observation

from . import debug_utils


def bnc(calc_input, validate=False):
    """
    This function uses the pandeia ETC engine to calculate the standard deviation of the per-pixel count rate for a
    given calculation when only the background signal is included. The input can be any valid pandeia calculation.
    The scene information will be ignored and the strategy information ignored except for number of dithers. The output
    will be the maximum standard deviation (sqrt(variance)) encountered at the detector plane.

    Parameters
    ----------
    calc_input: dict
        Pandeia engine input API compliant dict containing information required to perform the background noise calculation

    Returns
    -------
    pix_stddev: float
        Maximum standard deviation in pixel count rate (e-/sec/pixel) generated by the input background signal.
    """

    calc_config, instrument, strategy, scene_configuration, background, background_level, warnings = setup(calc_input, validate=validate)

    # ignore the scene in the input calculation and use an empty one instead. the only input signal in the calculation will
    # be the background which is spatially constant across the scene.
    scene_configuration = build_empty_scene()

    debug_utils.init(calc_input)

    scene = Scene(instrument.telescope.tel_name, config=scene_configuration)
    
    # dithering images uncorrelates all of the noise and thus reduces the variance by ndithers:
    #   variance = variance_single/ndithers
    if hasattr(strategy, 'dithers'):
        ndithers = len(strategy.dithers)
        strategy.dithers = [{'x': 0.0, 'y': 0.0}] # we don't actually need or want to calculate all of them now
    else:
        ndithers = 1
    my_detector_signal_list, my_detector_noise_list, my_detector_saturation_list, obs = make_observation(calc_config, instrument, 
                                                                       strategy, scene, background, background_level, validate=validate)

    # get the detector plane noise products
    # dithers have the same setup, just a different pointing, so we can be confident using only the first one
    if validate:
        det_var, det_stddev, det_rn_var, det_ff_var = my_detector_noise_list[0].on_detector()
    else:
        det_var, det_stddev, det_rn_var = my_detector_noise_list[0].on_detector()

    pix_stddev = det_stddev.max() / np.sqrt(ndithers)
    return pix_stddev
