"""
Methods for guessing details about a telescope from some metadata, so that
we can create TelescopeDescriptions from Monte-Carlo where some
parameters like the names of the camera and optics structure are not
stored in the file.
"""
from collections import namedtuple
import astropy.units as u
import numpy as np
from .optics import ReflectorShape
__all__ = [
    "GuessingKey",
    "GuessingResult",
    "guess_telescope",
    "unknown_telescope",
    "type_from_mirror_area",
]
GuessingKey = namedtuple(
    "GuessingKey",
    ["n_pixels", "focal_length", "n_mirror_tiles"],
    defaults=(None,),  # Mirror Tiles are optional
)
GuessingResult = namedtuple(
    "GuessingResult", ["type", "name", "camera_name", "n_mirrors", "reflector_shape"]
)
def _build_lookup_tree(telescopes):
    tree = {}
    for k, v in telescopes:
        if k.n_pixels not in tree:
            tree[k.n_pixels] = {}
        if k.focal_length not in tree[k.n_pixels]:
            tree[k.n_pixels][k.focal_length] = {}
        d = tree[k.n_pixels][k.focal_length]
        if k.n_mirror_tiles in d:
            other = d[k.n_mirror_tiles]
            raise ValueError(f"GuessingKeys are not unique: {k}: {v}, {other}")
        d[k.n_mirror_tiles] = v
    return tree
# focal length must have at most two digits after period
# as we round the lookup to two digits
_sc = ReflectorShape.SCHWARZSCHILD_COUDER
_dc = ReflectorShape.DAVIES_COTTON
_h = ReflectorShape.HYBRID
_p = ReflectorShape.PARABOLIC
# This is a list of tuples instead of a dict to be able to check for duplicates
TELESCOPE_NAMES = [
    (GuessingKey(2048, 2.28), GuessingResult("SST", "GCT", "CHEC", 2, _sc)),
    (GuessingKey(2368, 2.15), GuessingResult("SST", "ASTRI", "ASTRICam", 2, _sc)),
    (GuessingKey(2048, 2.15), GuessingResult("SST", "ASTRI", "CHEC", 2, _sc)),
    (GuessingKey(1296, 5.60), GuessingResult("SST", "1M", "DigiCam", 1, _dc)),
    (GuessingKey(1764, 16.0), GuessingResult("MST", "MST", "FlashCam", 1, _h)),
    (GuessingKey(1855, 16.0), GuessingResult("MST", "MST", "NectarCam", 1, _h)),
    (GuessingKey(1855, 28.0), GuessingResult("LST", "LST", "LSTCam", 1, _p)),
    (GuessingKey(11328, 5.59), GuessingResult("MST", "SCT", "SCTCam", 1, _sc)),
    # Non-CTA Telescopes
    (
        GuessingKey(1039, 16.97, 964),
        GuessingResult("LST", "MAGIC-1", "MAGICCam", 1, _p),
    ),
    (
        GuessingKey(1039, 16.97, 247),
        GuessingResult("LST", "MAGIC-2", "MAGICCam", 1, _p),
    ),
    (GuessingKey(1039, 17.0, 964), GuessingResult("LST", "MAGIC-1", "MAGICCam", 1, _p)),
    (GuessingKey(1039, 17.0, 247), GuessingResult("LST", "MAGIC-2", "MAGICCam", 1, _p)),
    (GuessingKey(960, 15.0), GuessingResult("MST", "HESS-I", "HESS-I", 1, _dc)),
    (GuessingKey(2048, 36.0), GuessingResult("LST", "HESS-II", "HESS-II", 1, _p)),
    (GuessingKey(1440, 4.89), GuessingResult("SST", "FACT", "FACT", 1, _h)),
]
LOOKUP_TREE = _build_lookup_tree(TELESCOPE_NAMES)
[docs]
def guess_telescope(n_pixels, focal_length, n_mirror_tiles=None):
    """
    From n_pixels of the camera and the focal_length,
    guess which telescope we are dealing with.
    This is mainly needed to add human readable names
    to telescopes read from simtel array.
    Parameters
    ----------
    n_pixels: int
        number of pixels of the telescope's camera
    focal_length: float or u.Quantity[length]
        Focal length, either in m or as astropy quantity
    Returns
    -------
    result: GuessingResult
        A namedtuple having type, telescope_name, camera_name and n_mirrors fields
    """
    focal_length = round(u.Quantity(focal_length, u.m).to_value(u.m), 2)
    try:
        d = LOOKUP_TREE[n_pixels][focal_length]
        # first check with n_mirror_tiles
        tel = d.get(n_mirror_tiles)
        if tel is not None:
            return tel
        # fallback to "not needed mirror tiles"
        return d[None]
    except KeyError:
        raise ValueError(f"Unknown telescope: n_pixel={n_pixels}, f={focal_length}") 
@u.quantity_input(mirror_area=u.m**2)
def type_from_mirror_area(mirror_area):
    mirror_diameter = (2 * np.sqrt(mirror_area / np.pi)).to_value(u.m)
    if mirror_diameter < 8:
        return "SST"
    if mirror_diameter < 16:
        return "MST"
    return "LST"
def unknown_telescope(mirror_area, n_pixels, n_mirrors=-1):
    """Create a GuessingResult for an unknown_telescope"""
    # this allows passing a plain number in square meter and any quantity
    # with an area unit
    mirror_area = u.Quantity(mirror_area, u.m**2)
    return GuessingResult(
        type=type_from_mirror_area(mirror_area),
        name=f"UNKNOWN-{mirror_area.to_value(u.m**2):.0f}M2",
        camera_name=f"UNKNOWN-{n_pixels}PX",
        n_mirrors=n_mirrors,
        reflector_shape="UNKNOWN",
    )