#!/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    

# Custom modules
from config.constants import alt, lat, lon, temp_fits
from utils.logger import log
from utils.analysis import fit_star
from devices import tel

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 in 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())

    # 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
