# -*- coding: utf-8 -*-
"""
Created on Fri Apr  5 10:43:09 2024

@author: zilya
"""

import numpy as np
from LF_scanner.pypulseq.Sequence.sequence import Sequence
from LF_scanner.pypulseq.calc_duration import calc_duration
from LF_scanner.pypulseq.check_timing import check_timing
from LF_scanner.pypulseq.make_adc import make_adc
from LF_scanner.pypulseq.make_delay import make_delay
from LF_scanner.pypulseq.make_sinc_pulse import make_sinc_pulse
from LF_scanner.pypulseq.make_trapezoid import make_trapezoid
from LF_scanner.pypulseq.opts import Opts


def seqgen_STEAM(param):
    
    
    # Read scanner parameters from the params structure
    scanner_parameters = Opts(max_grad = param.G_amp_max, grad_unit = 'Hz/m',\
                              max_slew = param.G_slew_max, slew_unit = 'Hz/m/s',\
                              grad_raster_time = param.grad_raster_time,\
                              rf_raster_time = param.rf_raster_time,\
                              adc_raster_time = 1/(param.BW_per_point*param.N_point),\
                              block_duration_raster = max(param.grad_raster_time,param.rf_raster_time))
    
    # For all modules
    gradient_ramp_time = param.grad_raster_time*np.ceil((scanner_parameters.max_grad/scanner_parameters.max_slew)/param.grad_raster_time)
        
    # Calculate slice-selection gradient
    pulse_bandwidth = np.double(param.t_BW_product_ex) / np.double(param.t_ex) #In Hz
    gradient_voxel_amp = np.double(pulse_bandwidth) / np.double(param.voxel_thkn) #In Hz/m
    gradient_voxel = make_trapezoid(channel = 'z',
                                    flat_time = np.double(param.t_ex), 
                                    rise_time = gradient_ramp_time, 
                                    amplitude = gradient_voxel_amp, 
                                    system = scanner_parameters)
    # Generate the RF pulse
    exc_pulse = make_sinc_pulse(flip_angle = np.radians(param.FA),\
                                duration = np.double(param.t_ex),\
                                phase_offset = 0,\
                                time_bw_product = param.t_BW_product_ex,\
                                delay = gradient_ramp_time,\
                                system = scanner_parameters)
   
    # Generate the ADC event
    adc_module = make_adc(num_samples = param.N_point, duration = 0.5/param.BW_per_point, delay = gradient_ramp_time, system = scanner_parameters)
    
    # Calculate the TE pause time
    tau_delay1 = param.tau1 - calc_duration(gradient_voxel)
    tau_delay1 = param.grad_raster_time*np.floor(tau_delay1/param.grad_raster_time)
    
    tau_delay2 = param.tau2 - calc_duration(gradient_voxel)
    tau_delay2 = param.grad_raster_time*np.floor(tau_delay2/param.grad_raster_time)
    
    tau_delay3 = param.tau1 - 0.5*calc_duration(gradient_voxel)
    tau_delay3 = param.grad_raster_time*np.floor(tau_delay3/param.grad_raster_time)
    
    tau_delay1 = make_delay(tau_delay1) if tau_delay1 > 0 else None
    tau_delay2 = make_delay(tau_delay2) if tau_delay2 > 0 else None
    tau_delay3 = make_delay(tau_delay3) if tau_delay3 > 0 else None
    

    # Calculate the TR fillers
    tr_delay = param.TR - (2*param.tau1 + param.tau2 + calc_duration(adc_module) + 0.5*calc_duration(gradient_voxel))
    tr_delay = param.grad_raster_time*np.floor(tr_delay/param.grad_raster_time)
    tr_delay = make_delay(tr_delay) if tr_delay > 0 else None
    
    seq = Sequence(system = scanner_parameters)
    phase_shift = 0
    # Dummy scans
    for n in range(int(param.average)):
        gradient_voxel.channel = 'z'
        seq.add_block(exc_pulse,gradient_voxel)
        if tau_delay1 is not None:
            seq.add_block(tau_delay1)
        gradient_voxel.channel = 'y'
        seq.add_block(exc_pulse,gradient_voxel)
        if tau_delay2 is not None:
            seq.add_block(tau_delay2)
        gradient_voxel.channel = 'x'
        seq.add_block(exc_pulse,gradient_voxel)
        if tau_delay3 is not None:
            seq.add_block(tau_delay3)
        seq.add_block(adc_module)
        if tr_delay is not None:
            seq.add_block(tr_delay)
    print(check_timing(seq)) 
    return seq
