import requests
import numpy as np

from loguru import logger
from tqdm import tqdm

from slic.core.condition import ValueCondition
from slic.utils.npy import nice_arange
from slic.core.scanner.scaninfo import ScanInfo




def start_sequence(n: int = 100, pulse_phase: float = np.pi/8):
    parameters = {"n":n, "pulse_phase":pulse_phase}
    url = "http://oscillations.psi.ch:8000/pulse"
    r = requests.get(url, params=parameters)

    d = r.json()
    return d['pids']


def happy_condition():
    """ Enjoy the bright side of life.
    """

    get_value = lambda: 1
    check_time = 0.1
    vmin = 0
    vmax = 2
    wait_time = 0.1
    required_fraction = 0
    condition = ValueCondition(get_value, check_time, vmin, vmax, wait_time, required_fraction)
    
    return condition
    

def scan_with_sync(adjustable, start_pos, end_pos, step_size, n_pulses, filename, acquisitions = [None], return_to_initial_values=False, condition=None, spreadsheet=None, stand_client=None):
    """
    Convenience function to make scans when pulse tube synchronisation is used for a given 
    start position, end position and step size.
    
    """
    positions = nice_arange(start_pos, end_pos, step_size)

    scan_with_sync_seq(adjustable, positions, n_pulses, filename, acquisitions=acquisitions, return_to_initial_values=return_to_initial_values, condition=condition, spreadsheet=spreadsheet, stand_client=stand_client)
    

def scan_with_sync_seq(adjustable, positions, n_pulses, filename, acquisitions = [None], return_to_initial_values=False, condition=None, spreadsheet=None, stand_client=None):
    """
    Convenience function to make scans when pulse tube synchronisation is used for a given
    list of positions to drive to.
    """
    
    scaninfo = ScanInfo(filename, "scan_info", [adjustable], positions)
    assert len(acquisitions) == 1, f"Only one acquisition can be given for one run. Now {acquisitions} were given."
    
    for i, position in enumerate(tqdm(positions)):
        # Go to the position and save readback
        adjustable.set_target_value(position).wait()
        readback = adjustable.get_current_value()
        scaninfo.append([position], [readback], None, None)
        
        if condition is None:
            condition = happy_condition()
            
        successful_acquisition = False
        
        while not successful_acquisition:
            condition.clear_and_start_counting()
            
            try:
                # Measure the step and save the pids 
                # It's done with try because every now and then the pids are not retrieved and the step should be repeated.
                pids = start_sequence(n_pulses)
                scan_info_d = scaninfo.to_sfdaq_dict()
                successful_acquisition = condition.stop_counting_and_analyze()
            except KeyboardInterrupt:
                print('Aborting.')
                break
            except:
                successful_acquisition = False

        # Retrieve data
        # First step needs to be made separately, because a run number can't be given to sfdaq until the folder is created.
        # TODO: is this still true?
        if i == 0:
            for acquisition in acquisitions:    
                first_step = acquisition.retrieve(filename, pulseids=pids, scan_info=scan_info_d)
                run_number= first_step['run_number']
        else:
            for acquisition in acquisitions:
                acquisition.retrieve(filename, pulseids=pids, run_number=run_number, scan_info=scan_info_d)
            
    if return_to_initial_values:
        adjustable.set_target_value(positions[0]).wait()

    # Write to stand table
    if stand_client is not None:
        if spreadsheet is not None:
            stand_client.add_row(run=str(run_number), filename=filename, n_pulses=str(n_pulses), sync="True", **spreadsheet.get_adjs_values())
        else:
            stand_client.add_row(run=str(run_number), filename=filename, n_pulses=str(n_pulses), sync="True")
    
    print(f"Scan {run_number} finished")