# si punta una stella
# si prende un'immagine
# si individua la stella e si calcola il centroide
# si calcola la distanza dal centro del ccd della stella
# si calcola l'offset necessario per portarla in quel punto
# si corregge il tracciamento


# System modules
import itertools
import os
import shutil
import signal
import subprocess
import sys
# common libraries
import tkinter as tk
from time import sleep
from tkinter import simpledialog

# Third-party modules
# Libraries
import ascomtest
import constants as c
import numpy as np
from astropy import log
from astropy import units as u
from astropy.coordinates import SkyCoord
from astropy.io import fits
# astropy
from astropy.modeling import fitting, models
from astropy.stats import sigma_clip
from astropy.visualization import astropy_mpl_style
from matplotlib import pyplot as plt

surface = models.Const2D(amplitude=950)
moffat2d = models.Moffat2D(amplitude=1000)
fit_g = fitting.LevMarLSQFitter()


# funzione che punta la stella e la osserva
# inserire un target da osservare O nome, coordinate e dati
# restituisce il nome dell'immagine scattata
# DA TESTARE
def track_and_take(target, objname="Polaris", radec="02 31 48.7 +89 15 51", duration=0.1):

    target1 = {
        "obj":   objname,
        "radec": radec,
        "bins":  1,
        "ndit":  1,
        "dits":  duration,
        "filts": 3,
    }

    if(target):
        if "altaz" in target:
            target1["radec"] = None
        for e in target:
            if(type(target[e]) == list):
                target1[e] = (target[e])[0]
            else:
                target1[e] = target[e]

    acquire(target1)
    log.info(f"Telescope is tracking {objname}")

    filename = observe(target1)
    log.info(f"Shooted the star in {filename}")

    return filename


# funzione che calcola il centroide
# apre l'immagine in ds9
# chiede di inserire la posizione approssimativa della stella
# TESTATA e FUNZIONANTE
def calc_centroid(img='temp.fits', plot=True):

    # TODO: sarebbe più corretto farlo con matplotlib, non è difficile, ma per comodità ora uso ds9
    p = subprocess.Popen(["ds9", img])
    sleep(2)

    # chiedo la posizione della stella
    ROOT = tk.Tk()
    ROOT.withdraw()
    starx = simpledialog.askinteger(
        title="X", prompt="Inserire coordinata x della stella")
    stary = simpledialog.askinteger(
        title="Y", prompt="Inserire coordinata y della stella")
    box = simpledialog.askinteger(
        title="Box", prompt="Inserire le dimensioni del box")
    p.kill()

    log.info(f"Star coordinates are {starx} and {stary}")
    log.info(f"Looking for centroid")

    # ritaglia un box
    data = fits.getdata(img)
    z = data[stary-box//2:stary+box//2, starx-box//2:starx+box//2]
    # fitta la stella nel box e restituisce le coordinate del centroide
    # TODO: verificare meglio possibili errori di +-1pixel

    y, x = np.mgrid[-z.shape[0]//2+1:z.shape[0] //
                    2+1, -z.shape[1]//2+1:z.shape[1]//2+1]
    f_init = surface + moffat2d
    f_fit = fit_g(f_init, x, y, z)
    if(plot):
        plt.figure(figsize=(8, 2.5))
        plt.subplot(1, 3, 1)
        plt.imshow(z)
        plt.title("Data")
        plt.subplot(1, 3, 2)
        plt.imshow(f_fit(x, y))
        plt.title("Model")
        plt.subplot(1, 3, 3)
        plt.imshow(z - f_fit(x, y))
        plt.title("Residual")
        plt.show()

    xoffset = int(f_fit[1].x_0.value)
    yoffset = int(f_fit[1].y_0.value)
    starx += xoffset
    stary += yoffset

    log.info(f"Il centroide della stella è [{starx},{stary}]")
    return [starx, stary]


# funzione che calcola la differenza da applicare alle coordinate in arcsec (DA TESTARE)
def calc_offset(centroid=[c.xmax[1]//2, c.ymax[1]//2], center=[c.xmax[1]//2, c.ymax[1]//2]):
    DX = center[0]-centroid[0]
    DY = center[1]-centroid[1]
    DS = DX*c.pixscale*u.arcsec
    DII = DY*c.pixscale*u.arcsec

    return [DS, DII]


# funzione che applica la correzione (DA TESTARE)
def set_offset(offset=[0*u.arcsec, 0*u.arcsec]):
    c = devices.tel.targetradec
    ra = Angle(c[0])+Angle(offset[1])
    dec = Angle(c[1])+Angle(offset[1])
    new = [ra.to_string(unit=hour), dec.to_string(unit=degree)]
    devices.tel.targetradec = new


# funzione main
# chiede un target o nome/coordinate/Texp
# chiede il punto della ccd in cui si vuole centrare la stella
# esegue tutti i metodi successivi in sequenza
def center_star(target={}, objname="test", radec=[], duration=0, center=[1000, 1000]):

    #filename = track_and_take(target, objname, radec, duration)
    centroid3 = calc_centroid(filename)
    offset = calc_offset(centroid3, center)
    set_offset(offset)
