# Source code for noctua.utils.coordinates

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-

    """
    Coordinates-related functions
    """

    # Third-party modules
    from astropy import units as u
    from astropy.coordinates import EarthLocation, Longitude, SkyCoord
    from astropy.coordinates.name_resolve import NameResolveError
    from astropy.io import fits
    from astropy.time import Time
    from astropy.wcs import WCS

    # Other templates
    from ..config.constants import alt, lat, lon, temp_fits
    from ..devices import tel
    from .analysis import fit_star
    from .logger import log

    def to_radec(string):
        """Transform a string containing the object name, i.e. "Polaris", or
        the object coordinates, i.e. "12 34 56.7 +89 00 00.0" in an array
        of float coordinates in units of [hours, degrees].
        """

        if isinstance(string, list):
            return string

        try:
            coor = SkyCoord(string,
                            unit=("hourangle", "deg"),
                            equinox=Time.now())

            return [coor.ra.hourangle, coor.dec.degree]

        except ValueError as e:
            log.warning("Cannot resolve coordinates, trying with name")
            log.warning(e)
            try:
                coor = SkyCoord.from_name(string)
                log.info("Found catalog name")
                coorstr = coor.to_string(
                    style='hmsdms', sep=' ', precision=1, pad=True)
                log.info(f"Correspond to {coorstr}")
                return [coor.ra.hourangle, coor.dec.degree]
            except NameResolveError as e:
                log.error("Cannot resolve name")
                log.error(e)
                return [None, None]

    def to_radians(string, equinox=None):
        """Transform a string containing the object name, i.e. "Polaris", or
        the object coordinates, i.e. "12 34 56.7 +89 00 00.0" in an array
        of float coordinates in units of radians.
        """

        if isinstance(string, list):
            return string

        if not equinox:
            equinox = Time.now()

        try:
            coor = SkyCoord(string,
                            unit=("hourangle", "deg"),
                            equinox=equinox)

            return [coor.ra.radian, coor.dec.radian]

        except ValueError as e:
            log.warning("Cannot resolve coordinates, trying with name")
            log.warning(e)
            try:
                coor = SkyCoord.from_name(string)
                log.info("Found catalog name")
                return [coor.ra.radian, coor.dec.radian]
            except NameResolveError as e:
                log.error("Cannot resolve name")
                log.error(e)
                return [None, None]

    def to_hms_dms(array, join=False):
        """Transform an array of coordinates from units of degrees,
        i.e. [12.0, 89.0] in an array of string coordinates,
        i.e. ["12 00 00", "+89 00 00"].
        """

        try:
            coor = SkyCoord(ra=array[0], dec=array[1],
                            unit=("deg", "deg"),
                            equinox=Time.now())

            if join:
                return coor.to_string(
                    style="hmsdms", sep=" ", precision=1, pad=True)

            return [
                coor.ra.to_string(
                    sep=" ",
                    precision=1,
                    pad=True),
                coor.dec.to_string(
                    sep=" ",
                    precision=1,
                    pad=True,
                    alwayssign=True)]

        except Exception as e:
            log.warning(e)
            if join:
                return None
            return [None, None]

    def to_hms(number):
        """Transform a value in units of degrees,
        i.e.  89.0, in a string coordinate,
        i.e. "+89 00 00".
        """

        try:
            coor = Longitude(number, unit="deg")

            return coor.wrap_at(
                180 *
                u.deg).to_string(
                sep=" ",
                precision=1,
                pad=True)

        except Exception as e:
            log.error(e)
            return None

    def calculate_offset(string="13 56 44  +27 28 01", header=None):
        """Take a string of coordinates representing what is expected to be
        the center of an OARPAF FITS image, and the header of that FITS
        image.  Uses the coordinates of that image to calculate the
        difference between that point and the center of the image in [alt,
        az], units of deg.

        """

        if not header:
            header = fits.getheader(temp_fits)

        location = EarthLocation(lat=lat, lon=lon, height=alt)

        obstime = Time(header["date-obs"])

        # Coordinates from the header of the image:
        # They come from the telescope pointing model,
        # and correspond to the CCD center
        # (i.e. 2072.5, 2063.0 in full frame).
        ra = header["ra"]
        dec = header["dec"]
        coor_center = SkyCoord(ra=ra, dec=dec,
                               unit=("deg", "deg"),
                               equinox=obstime,
                               obstime=obstime,
                               location=location)

        # Coordinates of the star. They should be the header one
        coor_star = SkyCoord(string,
                             unit=("hourangle", "deg"),
                             equinox=obstime,
                             obstime=obstime,
                             location=location)

        # trans = coor_star.transform_to(coor_center.skyoffset_frame())

        # log.warning(f"Ignoring that IERS accuracy is bad! Check if the server is back online")
        # from astropy.utils import iers
        # iers.conf.iers_degraded_accuracy = 'ignore'
        # iers.conf.auto_max_age = None

        # This is the offset in degrees
        dalt = coor_center.altaz.alt - coor_star.altaz.alt
        daz = coor_center.altaz.az - coor_star.altaz.az

        log.info(f"Offset in arcsec (web): {dalt.arcsec}, {-daz.arcsec}]")
        log.debug(f"Offset in deg (astelos): {dalt.deg}, {-daz.deg}")

        return [dalt.deg, -daz.deg]

    def apply_offset(fits_file=temp_fits, box=100, model="moffat", display=False):
        """Take an image, fit a Moffat2d on a centered box center supposing
        there is a star, calculate the offset with respect to the center,
        sum it to the existing telescope offset.

        """

        # Load data and header from an image

        data = fits.getdata(fits_file)
        header = fits.getheader(fits_file)

        centx, centy = data.shape[0] // 2, data.shape[1] // 2

        width = box
        height = box

        data = data[centx - width // 2:centx + width //
                    2, centy - height // 2:centy + height // 2]
        log.debug(f"Taking subimage [{centx -
                                      width //
                                      2}:{centx +
                                          width //
                                          2}, {centy -
                                               height //
                                               2}:{centy +
                                                   height //
                                                   2}]")

        # Fit on what you find there

        try:
            fitted = fit_star(data, model=model, display=display)
            log.debug(
                f"Subimage fit x:{
                    fitted.xc}, y:{
                    fitted.yc}, fwhm:{
                    fitted.fwhm}")
        except Exception as e:
            log.error("Error while fitting")
            log.error(e)
            return [0, 0]

        true_x = centx - width // 2 + fitted.xc
        true_y = centy - height // 2 + fitted.yc

        log.debug(f"True xy x:{true_x}, y:{true_y}")

        # Transform pixels to radec

        wcs = WCS(header)

        coords = wcs.pixel_to_world(true_x, true_y)
        string_coords = to_hms_dms([coords.ra.deg, coords.dec.deg], join=True)

        log.debug(f"True HMSDMS: {string_coords}")

        dalt, daz = calculate_offset(string_coords, header=header)

        try:
            current_alt_offset, current_az_offset = tel.offset
            log.debug(
                f"Current offsets in arcsec: {
                    current_alt_offset *
                    3600}, {
                    current_az_offset *
                    3600}")
        except Exception as e:
            log.error("Cannot retrieve offset from cabinet:")
            log.error(e)
            return [0, 0]

        updated_offset = [current_alt_offset + dalt, current_az_offset + daz]

        tel.offset = updated_offset
        log.debug(
            f"Updated offset in arcsec: {
                updated_offset[0] *
                3600}, {
                updated_offset[1] *
                3600}")

        return updated_offset