from shinsapp import SHINSApp
from util.shinsLogger import logger
from util import variables as uvars
from util import exception
from util import functions as ufun
import time

from SHARKNIR_gen_base import SHARKNIR_gen_base

class SHARKNIR_cal_ADC1_mindisp(SHINSApp):
    """
    The task of this template is to apply a fixed rotation of a given
    angle just to the ADC1 prism for a given number of steps, in order to find
    the minimum dispersion position with respect to the minimum dispersion position of ADC2.

    A third parameter will indicate the type of rotation to be applied:
    co-rotatin or contro-rotating

    """

    @exception.handler_for_run
    def run(self, ins_s=None, det_s=None, tel_s=None, rtc_s=None, **kwargs):

        logger.info(__name__)
        obsController = self.obsController
        self.status = uvars.EXIT_SUCCESS

        self.gen_base = SHARKNIR_gen_base()
        gen_base = self.gen_base

        fitsDir = uvars.fitsDir + obsController.getCurrentDataDirectory() +'/'
        
        ##########
        # ADC ON #
        ##########
        if(self.paragraph("Turning ON the ADC")):
            
            logger.info("Turning ON the ADCs...")
            ins_s_adc = {"ADC_Mode":"ON"}
            obsController.setupInstrument(ins_s_adc)
            logger.info("Turning ON the ADCs... DONE")
            
        ################
        # LOOP ACTIONS #
        ################

        logger.info("Setting ADC2_PosAng to MIN_DISP Position ...")
        obsController.setupInstrument({"ADC2_PosAng":"min_disp"})
        logger.info("Setting ADC2_PosAng to MIN_DISP Position ... DONE")        

        val = float(ins_s["AMOUNT"])
        steps = int(ins_s["STEPS"])
        metric_array = []
        
        logger.debug("Waiting for 20 seconds")
        time.sleep(20)

        pos_array = [val*s for s in range(-steps//2 + 1 , steps//2 + 1)]

        for pos in pos_array:

            ################
            # MOVING ADC 1 #
            ################
            
            if(self.paragraph("Setting ADC1 Pos_Ang at {} deg".format(pos))):
                logger.info("Setting ADC1_PosAng to {} deg ...".format(pos))
                obsController.setupInstrument({"ADC1_PosAng" : pos})
                logger.info("Setting ADC1_PosAng to {} deg ... DONE".format(pos))
                
            logger.debug("Waiting for 10 seconds")
            time.sleep(10)
                
            ##################
            # EXPOSING SASHA #
            ##################

            self.status, self.filenames, self.other = gen_base.main(det_s=det_s, ng_max=2)

            #####################
            # MEASURE THE IMAGE #
            #####################
            if self.paragraph("MEASURE THE IMAGE"):

                im_arr = []
                for image_name in self.filenames:
                    logger.debug("reading {}".format(image_name))
                    img_ = read_image(fitsDir+image_name, saly.detector)
                    im_arr.append(img_)

                logger.debug("Making mean of {} images".format(len(im_arr)))
                meaned = np.mean(np.asarray(im_arr), 0)

                logger.debug("Loading")
                saly.PSFNoCoro.LoadImage(meaned)
                saly.PSFNoCoro.removeHotPixels(verbose=True)
                logger.debug("Getting Metric")

                if rtc_s["METRIC"] == "Strehl":

                    metric = saly.PSFNoCoro.getStrehl(0.0145, method='esposito', wavelengthInUm=1.091,
                                                      Dpup=8.22*0.98, eps=0.0, verbose=False,
                                                      doNotRecompDLPsf=True, returnSR=True)

                elif rtc_s["METRIC"] == "EE_frac":

                    radius = 2.35 * 0.7 # Depends on wavelength and pixel scale. 70% of lampda/D
                    metric = saly.PSFNoCoro.getEncircledEnergy(radius=radius, oversample=5, box=20,
                                                                    verbose=False, display=False,
                                                                    returnEE=True)

                metric_array.append(metric)
                logger.info("{} is {}".format(rtc_s["METRIC"], metric))

        # End of loop

        #####################################
        # FITTING FILES TO COMPUTE MIN DISP #
        #####################################
        if self.paragraph("FITTING FILES FOR THIS THROUGH FOCUS"):

            logger.info("This is the array of measured {}  for each image:".format(rtc_s["METRIC"]))
            logger.info("{}".format(metric_array))

            # Get best width
            # First we polyfit the strhels:
            # pos_array is the amplitude vector: [-0.1, -0.08, #-0.06, -0.04, -0.02, 0.0, 0.02, 0.04, 0.06, 0.08, 0.1]),

            pol_degree = 2

            logger.info("Fitting a {} degree polynomial to that array!".format(pol_degree))

            fit  = np.polyfit(pos_array, metric_array, pol_degree)
            y_fit = np.poly1d(fit)

            # Then we calculate the local maximum of the polynomial.
            # x_best is the best amplitude that we want to pass to the DM

            minimization = minimize(lambda x: -1 * y_fit(x), bounds=(np.array(pos_array).min(),
                                                                     np.array(pos_array).max()),
                                                                     method='bounded')
            best_metric = minimization.x

            logger.info("Base array: {}".format(base_array))
            logger.info("Pos array:  {}".format(pos_array))
            logger.info("Best {} is at x={} ".format(rtc_s["METRIC"], best_metric))

        #################################
        # SETTING BOTH ADCS TO MIN_DISP #
        #################################            
        if(self.paragraph("Setting ADCs in min_disp position")):
            logger.info("Setting ADCs in min_disp position... ")
            ins_s_adc = {"ADC1_PosAng":"min_disp",
                         "ADC1_PosAng":"min_disp"}
            obsController.setupInstrument(ins_s_adc)
            logger.info("Setting ADCs in min_disp position... DONE")
            logger.debug("Waiting for 10 seconds")
            time.sleep(10)

        ###########
        # ADC OFF #
        ###########
        if(self.paragraph("Turning OFF the ADC")):

            logger.info("Turning OFF the ADCs...")
            ins_s_adc = {"ADC_Mode":"OFF"}
            obsController.setupInstrument(ins_s_adc)
            logger.info("Turning OFF the ADCs... DONE")
