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

# Third-party modules
import numpy as np
from astropy import units as u
from astropy.io import fits
from astropy.time import Time

# Other templates
from ..config.constants import alt, lat, lon, pixscale, rotangle, temp_fits
from ..devices import cam, dom, foc, lamp, light, rot, tel, tel_temp
from ..utils.logger import log
from ..utils.structure import save_filename
from .basetemplate import BaseTemplate


class Template(BaseTemplate):
    def __init__(self):
        super().__init__()
        self.name = "fillheader"
        self.description = "Fills the header of observed FITS files"

    def content(self, params):

        ########################
        ##### Params check #####
        ########################

        try:
            save = params.get("save") or True
            objname = params.get("object") or "test"
            ob = params.get("ob") or None
            tpl = params.get("template") or None
            radec = params["radec"]
            altaz = params["altaz"]
            utc = params["utc"]
            lst = params["lst"]
        except KeyError as e:
            log.error(f"Parameter {e} not found")
            return

        #########################
        ##### Header update #####
        #########################

        filename = temp_fits
        gps = Time(utc, format="unix")

        try:
            with fits.open(filename, 'update') as h:
                for hdu in h:

                    ######################
                    ##### Basic info #####
                    ######################

                    now1 = Time.now()

                    # log.debug(f"CONST keys start {(Time.now()-now).sec :.2f}")
                    hdu.header['object'] = objname, "Object name"
                    hdu.header['latitude'] = lat, "[deg] Telescope latitude N"
                    hdu.header['longitud'] = lon, "[deg] Telescope longitude E from Greeenwich"
                    hdu.header['altitude'] = alt, "[m] Telescope height from sea level"
                    hdu.header['equinox'] = 2000, "Equinox"
                    hdu.header['date'] = Time.now(
                    ).isot, "(UTC) Date the file was written"
                    # log.debug(f"CONST  keys end {(Time.now()-now).sec :.2f}")

                    ########################
                    ##### Cabinet info #####
                    ########################

                    # now = Time.now()
                    # log.debug(f"CABINET keys start {(Time.now()-now).sec :.2f}")

                    # Asking to the cabinet basic info (ra, dec, alt, az, utc)
                    hdu.header['alt'] = altaz[0], "[deg] Altitude at start"
                    hdu.header['az'] = altaz[1], "[deg] Azimuth clockwise from N at start"
                    hdu.header['ra'] = radec[0] * \
                        15.0, "[deg] Right ascension J2000 at start"
                    hdu.header['dec'] = radec[1], "[deg] Declination J2000 at start"
                    hdu.header['mjd-obs'] = gps.mjd, "[day] (MJD) Date the exposure was started"
                    hdu.header['date-obs'] = gps.isot[:-
                                                      4], "(UTC) Date the exposure was started"
                    hdu.header['lst'] = lst, "Local Sidereal Time"
                    hdu.header['ha'] = lst - radec[0], "Hour Angle at start"

                    alt_rad = np.radians(altaz[0])
                    sec_z = np.nan if np.isclose(
                        np.sin(alt_rad), 0) else round(
                        1 / np.sin(alt_rad), 2)
                    hdu.header['airmass'] = sec_z, "Airmass at start"

                    focrel = foc.position
                    focabs = foc.absolute

                    temperatures = tel_temp.temperature
                    cover = tel.cover

                    hdu.header['telfocus'] = focrel, "[mm] M2 Relative position"
                    hdu.header['hierarch TEL FOC REL'] = focrel, "[mm] M2 Relative position"
                    hdu.header['hierarch TEL FOC ABS'] = focabs, "[mm] M2 Absolute position"
                    hdu.header['hierarch TEL M1 TEMP'] = temperatures[0], "[C] M1 temperature"
                    hdu.header['hierarch TEL M2 TEMP'] = temperatures[1], "[C] M2 temperature"
                    hdu.header['hierarch TEL M3 TEMP'] = temperatures[2], "[C] M3 temperature"
                    hdu.header['hierarch TEL COVER'] = cover, "M1 cover. 1.0 if full open, 0.0 if close"

                    # log.debug(f"CABINET keys end {(Time.now()-now).sec :.2f}")

                    ######################
                    ##### ASCOM info #####
                    ######################

                    # now = Time.now()
                    # log.debug(f"ASCOM keys start {(Time.now()-now).sec :.2f}")

                    rotpos = rot.position
                    rotabs = rot.absolute

                    hdu.header['hierarch TEL ROT REL'] = rotpos, "[deg] Relative rotator position angle"
                    hdu.header['hierarch TEL ROT ABS'] = rotabs, "[deg] Absolute rotator position angle"
                    hdu.header['hierarch TEL OFFSET ALT'] = tel.offset[0], "[rad] Offset in alt"
                    hdu.header['hierarch TEL OFFSET AZ'] = tel.offset[1], "[rad] Offset in az"
                    hdu.header['hierarch TEL PARKED'] = tel.park, "True if telescope is parked"
                    hdu.header['hierarch DOM AZIMUTH'] = dom.azimuth, "[deg] Dome Azimuth clockwise from N"
                    hdu.header['hierarch DOM SHUTTER'] = dom.open, "Dome Shutter. False if close, True if open"
                    hdu.header['hierarch DOM SLAVED'] = dom.slave, "True if dome slaved to telescope at start"
                    hdu.header['hierarch DOM LAMP'] = lamp.state, "Flat lamp. True if on, False if off"
                    hdu.header['hierarch DOM LIGHT'] = light.state, "Dome light. True if on, False if off"
                    # log.debug(f"ASCOM keys end {(Time.now()-now).sec :.2f}")

                    #######################
                    ##### Camera info #####
                    #######################

                    # Calling all from camera's GetImageParamter.cgi

                    ca = cam.all

                    xpixscale = round(pixscale * int(ca["binning"][0]), 3)
                    ypixscale = round(pixscale * int(ca["binning"][1]), 3)
                    camx = ca["xrange"]
                    camy = ca["yrange"]

                    hdu.header['hierarch CAM AMBIENT'] = ca["ambient"], "[C] Camera ambient temperature"
                    hdu.header['hierarch CAM FAN'] = ca["fan"], "Cooler power in percent of usage"
                    hdu.header['hierarch CAM COOLER'] = ca["cooler"], "Camera cooler. True if on, False if off"
                    hdu.header['hierarch CAM TEMP'] = ca["temperature"], "[C] Camera temperature"
                    hdu.header['hierarch CAM XPIXSCALE'] = xpixscale, "[arcsec/px] X scale of px in current binning"
                    hdu.header['hierarch CAM YPIXSCALE'] = ypixscale, "[arcsec/px] Y scale of px in current binning"
                    hdu.header['hierarch CAM XRANGE'] = str(
                        camx), "[x_start, x_end] in current binning"
                    hdu.header['hierarch CAM YRANGE'] = str(
                        camy), "[y_start, y_end] in current binning"
                    # #log.debug(f"CAM keys end {(Time.now()-now).sec :.2f}"

                    ########################
                    ##### WCS keywords #####
                    ########################

                    xdegscale = xpixscale * u.arcsec.to(u.deg)
                    ydegscale = ypixscale * u.arcsec.to(u.deg)
                    angle = np.deg2rad(rotpos + rotangle)
                    flip = -1

                    hdu.header["WCSAXES"] = 2
                    hdu.header["CRPIX1"] = (
                        camx[1] - camx[0]) / 2., "Pixel coordinate of reference point"
                    hdu.header["CRPIX2"] = (
                        camy[1] - camy[0]) / 2., "Pixel coordinate of reference point"
                    hdu.header["PC1_1"] = np.cos(
                        angle), "Coordinate transformation matrix element"
                    hdu.header["PC2_1"] = np.sin(
                        angle), "Coordinate transformation matrix element"
                    hdu.header["PC1_2"] = np.sin(
                        angle), "Coordinate transformation matrix element"
                    hdu.header["PC2_2"] = flip * \
                        np.cos(angle), "Coordinate transformation matrix element"
                    hdu.header["CDELT1"] = xdegscale, "[deg] Coordinate increment at reference point"
                    hdu.header["CDELT2"] = ydegscale, "[deg] Coordinate increment at reference point"
                    hdu.header["CUNIT1"] = 'deg', "Units of coordinate increment and value"
                    hdu.header["CUNIT2"] = 'deg', "Units of coordinate increment and value"
                    hdu.header["CTYPE1"] = 'RA---TAN', "Right ascension, gnomonic projection"
                    hdu.header["CTYPE2"] = 'DEC--TAN', "Declination, gnomonic projection"
                    hdu.header["CRVAL1"] = radec[0] * 15.0
                    hdu.header["CRVAL2"] = radec[1]
                    hdu.header["RADESYS"] = "ICRS"

                    # wcss = WCS()
                    # wcss.wcs.ctype = ["RA---TAN", "DEC--TAN"]

                    # wcss.wcs.cd = scale*np.array([[np.cos(angle), np.sin(angle)],
                    #                         [np.sin(angle), flip*np.cos(angle)]])

                    # wcss.wcs.cunit = ['deg', 'deg']

                    # wcss.wcs.crval = [radec,
                    #                   radec*15]

                    # wcss.wcs.crpix = [ (camx[1]-camx[0])/2.,
                    #                    (camy[1]-camy[0])/2.]

                    # hdu.header.update(wcss.to_header())

                    log.debug(
                        f"Header updated in {(Time.now() - now1).sec:.2f}")

        except FileNotFoundError as e:
            log.error("File not found")
            log.error(e)

        ###############################
        ##### Saving in data path #####
        ###############################

        if save:
            outpath = save_filename(filename)
            log.info(f"Copying {filename} as {outpath}")
            self.filename = str(outpath)
            log.debug(f"fillheader {self.filename}")
