from math import pi

import numpy as np

import pypulseq as pp

Nx = 128
Ny = 128
n_slices = 3

system = pp.Opts(
    max_grad=32,
    grad_unit="mT/m",
    max_slew=130,
    slew_unit="T/m/s",
    grad_raster_time=10e-6,
    rf_ringdown_time=10e-6,
    rf_dead_time=100e-6,
)
seq = pp.Sequence(system)

fov = 220e-3
slice_thickness = 5e-3
slice_gap = 15e-3

delta_z = n_slices * slice_gap
rf_offset = 0
z = np.linspace((-delta_z / 2), (delta_z / 2), n_slices) + rf_offset

# =========
# RF90, RF180
# =========
flip = 12 * pi / 180
rf, gz, _ = pp.make_sinc_pulse(
    flip_angle=flip,
    system=system,
    duration=2e-3,
    slice_thickness=slice_thickness,
    apodization=0.5,
    time_bw_product=4,
    return_gz=True,
)

flip90 = 90 * pi / 180
rf90 = pp.make_block_pulse(
    flip_angle=flip90, system=system, duration=500e-6, time_bw_product=4
)

# =========
# Readout
# =========
delta_k = 1 / fov
k_width = Nx * delta_k
readout_time = 6.4e-3
gx = pp.make_trapezoid(
    channel="x", system=system, flat_area=k_width, flat_time=readout_time
)
adc = pp.make_adc(num_samples=Nx, duration=gx.flat_time, delay=gx.rise_time)

# =========
# Prephase and Rephase
# =========
phase_areas = (np.arange(Ny) - (Ny / 2)) * delta_k
gy_pre = pp.make_trapezoid(
    channel="y", system=system, area=phase_areas[-1], duration=2e-3
)

gx_pre = pp.make_trapezoid(channel="x", system=system, area=-gx.area / 2, duration=2e-3)

gz_reph = pp.make_trapezoid(
    channel="z", system=system, area=-gz.area / 2, duration=2e-3
)

# =========
# Spoilers
# =========
pre_time = 8e-4
gx_spoil = pp.make_trapezoid(
    channel="x", system=system, area=gz.area * 4, duration=pre_time * 4
)
gy_spoil = pp.make_trapezoid(
    channel="y", system=system, area=gz.area * 4, duration=pre_time * 4
)
gz_spoil = pp.make_trapezoid(
    channel="z", system=system, area=gz.area * 4, duration=pre_time * 4
)

# =========
# Delays
# =========
TE, TI, TR = 13e-3, 140e-3, 65e-3
delay_TE = (
    TE - pp.calc_duration(rf) / 2 - pp.calc_duration(gy_pre) - pp.calc_duration(gx) / 2
)
delay_TE = pp.make_delay(delay_TE)
delay_TI = TI - pp.calc_duration(rf90) / 2 - pp.calc_duration(gx_spoil)
delay_TI = pp.make_delay(delay_TI)
delay_TR = (
    TR
    - pp.calc_duration(rf) / 2
    - pp.calc_duration(gx) / 2
    - pp.calc_duration(gy_pre)
    - TE
)
delay_TR = pp.make_delay(delay_TR)

for j in range(n_slices):
    freq_offset = gz.amplitude * z[j]
    rf.freq_offset = freq_offset

    for i in range(Ny):
        seq.add_block(rf90)
        seq.add_block(gx_spoil, gy_spoil, gz_spoil)
        seq.add_block(delay_TI)
        seq.add_block(rf, gz)
        gy_pre = pp.make_trapezoid(
            channel="y", system=system, area=phase_areas[i], duration=2e-3
        )
        seq.add_block(gx_pre, gy_pre, gz_reph)
        seq.add_block(delay_TE)
        seq.add_block(gx, adc)
        gy_pre = pp.make_trapezoid(
            channel="y", system=system, area=-phase_areas[i], duration=2e-3
        )
        seq.add_block(gx_spoil, gy_pre)
        seq.add_block(delay_TR)

seq.set_definition(key="Name", value="2D T1 MPRAGE")
seq.write("2d_mprage_pypulseq.seq")
