<reponame>WenyinWei/MHDpy<gh_stars>0

def CoilSelectionUI(path_coilsys_folder, grid_col=4):
    from ipywidgets import GridspecLayout, Checkbox, Layout, Button, HBox 
    from IPython.display import display
    import asyncio, time
    from .file import read_coil, coilsys_names

    def wait_for_change(widget1, widget2): 
        future = asyncio.Future()
        def getvalue(change):
            future.set_result(change.description)
            widget1.on_click(getvalue, remove=True)
            widget2.on_click(getvalue, remove=True)
            # we need to free up the binding to getvalue to avoid an IvalidState error
            # buttons don't support unobserve
            # so use `remove=True` 
        widget1.on_click(getvalue) 
        widget2.on_click(getvalue)
        return future

    button1=Button(description="Confirm Coil System") 
    button2=Button(description="Confirm Coils") 

    async def f():
        while True:
            x = await wait_for_change(button1,button2) 
            if x == "Confirm Coil System": 
                print("Loading...")
                # Coil system names that are selected
                coilsys_sel_names.clear()
                coilsys_sel_names.extend([box.description for box in coilsys_checkbx if box.value])
                _, coil_info = read_coil(path_coilsys_folder, coilsys_sel_names, only_info=True)
                print("These coil systems have been selected:", coilsys_sel_names)

                coil_names = {coilsys:list(coil_info[coilsys].keys())  for coilsys in coilsys_sel_names}
                coil_checkbx_s = []
                coil_sum = sum([len(coil_names[coilsys]) for coilsys in coil_names.keys()])

                grid = GridspecLayout((coil_sum-1)//grid_col + 1, grid_col)
                for coilsys in coilsys_sel_names:
                    coil_checkbx_s.extend([Checkbox(value=True, description=coilsys+'/'+coil, layout=Layout(height='auto', width='auto')) for coil in coil_names[coilsys]])

                for i, coil_checkbx in enumerate( coil_checkbx_s):
                    grid[i//grid_col, i - i//grid_col*grid_col] = coil_checkbx
                display(grid)
            else:
                # Coil names that are selected
                coil_sel_names.clear()
                coil_sel_names.update({coilsys:[] for coilsys in coilsys_sel_names})
                for box in coil_checkbx_s:
                    if box.value:    
                        coilsys, coil = box.description.split('/')
                        coil_sel_names[coilsys].append(coil) 
                print("These coil(s) have been selected", coil_sel_names)
                print("")
                print("Finished updating the coilsys_sel_names & coil_sel_names. If you want to update the names again, run the code one more time.")
                return


    coilsys_names = coilsys_names(path_coilsys_folder)
        
    coilsys_checkbx = [Checkbox(value=False, description=coilsys, layout=Layout(height='auto', width='auto')) for coilsys in coilsys_names]
    grid = GridspecLayout((len(coilsys_names)-1)//grid_col + 1, grid_col)
    for i in range(len(coilsys_names)):
        grid[i//grid_col, i - i//grid_col*grid_col] = coilsys_checkbx[i]
    print('Pick the coils you want to calculate the field components :')
    display(grid)

    coilsys_sel_names = ['NOT_TO_BE_EMPTY'] 
    coil_sel_names = {'NOT_TO_BE_EMPTY':[]}
    asyncio.create_task(f())
    display(HBox([button1,button2]))
    return coilsys_sel_names, coil_sel_names

# Choose a flux surface
# i_Q avoid non-monotone q(s) curve
from numpy import ndarray as _ndarray
def query_surf_index_Q(Q:_ndarray, i_Q=0) -> int:
    import numpy as np
    q_choice = float(input(f'Safety factor of the surface? ({np.min(Q):.3f} - {np.max(Q):.3f}) '))
    if q_choice > np.max(Q) or q_choice < np.min(Q):
        raise RuntimeWarning("Out of the reasonable q safety factor range.")
    i_surf = np.argmin(np.abs(Q[i_Q:] - q_choice))+i_Q
    return i_surf

def query_surf_index_S(S:_ndarray) -> int:
    import numpy as np
    s_choice = float(input(f's magnetic radius of the surface?  ({np.min(S):.4f} - {np.max(S):.4f}) '))
    if s_choice > np.max(S) or s_choice < np.min(S):
        raise RuntimeWarning("Out of the reasonable s magnetic radius range.")
    i_surf = np.argmin(np.abs(S - s_choice))
    return i_surf

def query_surf_index(Q:_ndarray=None, S:_ndarray=None, i_Q=0) -> int:
    import numpy as np
    assert( Q is not None or S is not None )

    if Q is not None and S is not None:
        if int(input("Choose a B surface by safety factor or s magnetic radius? (1 for q, 0 for s) "))==1: 
            i_surf = query_surf_index_Q(Q, i_Q)
        else:
            i_surf = query_surf_index_S(S)
    elif Q is not None and S is None: 
        i_surf = query_surf_index_Q(Q, i_Q)
    elif Q is None and S is not None: 
        i_surf = query_surf_index_S(S)
    return i_surf

def query_n_har(Q:_ndarray) -> int:
    if Q[0]>0:
        n_har = int(input('n (toroidal mode number)? (should be NEGATIVE because q>0)'))
    else:
        n_har = int(input('n (toroidal mode number)? (should be POSITIVE because q<0)'))
    return n_har

def CoilSelectionUI(path_coilsys_folder, grid_col=4):
    from ipywidgets import GridspecLayout, Checkbox, Layout, Button, HBox 
    from IPython.display import display
    import asyncio, time
    from .file import read_coil, coilsys_names

    def wait_for_change(widget1, widget2): 
        future = asyncio.Future()
        def getvalue(change):
            future.set_result(change.description)
            widget1.on_click(getvalue, remove=True)
            widget2.on_click(getvalue, remove=True)
            # we need to free up the binding to getvalue to avoid an IvalidState error
            # buttons don't support unobserve
            # so use `remove=True` 
        widget1.on_click(getvalue) 
        widget2.on_click(getvalue)
        return future

    button1=Button(description="Confirm Coil System") 
    button2=Button(description="Confirm Coils") 

    async def f():
        while True:
            x = await wait_for_change(button1,button2) 
            if x == "Confirm Coil System": 
                print("Loading...")
                # Coil system names that are selected
                coilsys_sel_names.clear()
                coilsys_sel_names.extend([box.description for box in coilsys_checkbx if box.value])
                _, coil_info = read_coil(path_coilsys_folder, coilsys_sel_names, only_info=True)
                print("These coil systems have been selected:", coilsys_sel_names)

                coil_names = {coilsys:list(coil_info[coilsys].keys())  for coilsys in coilsys_sel_names}
                coil_checkbx_s = []
                coil_sum = sum([len(coil_names[coilsys]) for coilsys in coil_names.keys()])

                grid = GridspecLayout((coil_sum-1)//grid_col + 1, grid_col)
                for coilsys in coilsys_sel_names:
                    coil_checkbx_s.extend([Checkbox(value=True, description=coilsys+'/'+coil, layout=Layout(height='auto', width='auto')) for coil in coil_names[coilsys]])

                for i, coil_checkbx in enumerate( coil_checkbx_s):
                    grid[i//grid_col, i - i//grid_col*grid_col] = coil_checkbx
                display(grid)
            else:
                # Coil names that are selected
                coil_sel_names.clear()
                coil_sel_names.update({coilsys:[] for coilsys in coilsys_sel_names})
                for box in coil_checkbx_s:
                    if box.value:    
                        coilsys, coil = box.description.split('/')
                        coil_sel_names[coilsys].append(coil) 
                print("These coil(s) have been selected", coil_sel_names)
                print("")
                print("Finished updating the coilsys_sel_names & coil_sel_names. If you want to update the names again, run the code one more time.")
                return


    coilsys_names = coilsys_names(path_coilsys_folder)
        
    coilsys_checkbx = [Checkbox(value=False, description=coilsys, layout=Layout(height='auto', width='auto')) for coilsys in coilsys_names]
    grid = GridspecLayout((len(coilsys_names)-1)//grid_col + 1, grid_col)
    for i in range(len(coilsys_names)):
        grid[i//grid_col, i - i//grid_col*grid_col] = coilsys_checkbx[i]
    print('Pick the coils you want to calculate the field components :')
    display(grid)

    coilsys_sel_names = ['NOT_TO_BE_EMPTY'] 
    coil_sel_names = {'NOT_TO_BE_EMPTY':[]}
    asyncio.create_task(f())
    display(HBox([button1,button2]))
    return coilsys_sel_names, coil_sel_names

# Choose a flux surface
# i_Q avoid non-monotone q(s) curve
from numpy import ndarray as _ndarray
def query_surf_index_Q(Q:_ndarray, i_Q=0) -> int:
    import numpy as np
    q_choice = float(input(f'Safety factor of the surface? ({np.min(Q):.3f} - {np.max(Q):.3f}) '))
    if q_choice > np.max(Q) or q_choice < np.min(Q):
        raise RuntimeWarning("Out of the reasonable q safety factor range.")
    i_surf = np.argmin(np.abs(Q[i_Q:] - q_choice))+i_Q
    return i_surf

def query_surf_index_S(S:_ndarray) -> int:
    import numpy as np
    s_choice = float(input(f's magnetic radius of the surface?  ({np.min(S):.4f} - {np.max(S):.4f}) '))
    if s_choice > np.max(S) or s_choice < np.min(S):
        raise RuntimeWarning("Out of the reasonable s magnetic radius range.")
    i_surf = np.argmin(np.abs(S - s_choice))
    return i_surf

def query_surf_index(Q:_ndarray=None, S:_ndarray=None, i_Q=0) -> int:
    import numpy as np
    assert( Q is not None or S is not None )

    if Q is not None and S is not None:
        if int(input("Choose a B surface by safety factor or s magnetic radius? (1 for q, 0 for s) "))==1: 
            i_surf = query_surf_index_Q(Q, i_Q)
        else:
            i_surf = query_surf_index_S(S)
    elif Q is not None and S is None: 
        i_surf = query_surf_index_Q(Q, i_Q)
    elif Q is None and S is not None: 
        i_surf = query_surf_index_S(S)
    return i_surf

def query_n_har(Q:_ndarray) -> int:
    if Q[0]>0:
        n_har = int(input('n (toroidal mode number)? (should be NEGATIVE because q>0)'))
    else:
        n_har = int(input('n (toroidal mode number)? (should be POSITIVE because q<0)'))
    return n_har
# we need to free up the binding to getvalue to avoid an IvalidState error # buttons don't support unobserve # so use `remove=True` # Coil system names that are selected # Coil names that are selected # Choose a flux surface # i_Q avoid non-monotone q(s) curve