import netCDF4
import numpy as np

from ..OmegaHMesh import OmegaHMesh

INT_UNUSED = 2000000000
DBL_UNUSED = 2.0e30
STR_UNUSED = "UNUSED                                                                                              "


# first one is on positive side
def sort_edge_to_face_map(edge_to_face_map, face2edge_map) -> np.ndarray:
    nedges = edge_to_face_map.shape[0]

    for i in range(0, nedges):
        first_face = edge_to_face_map[i, 0]
        for j in range(0, 3):
            edge = face2edge_map[first_face, 2 * j]
            if edge == i:
                first_face_sign = face2edge_map[first_face, 2 * j + 1]
                if first_face_sign == -1:  # then swap
                    edge_to_face_map[i, 0], edge_to_face_map[i, 1] = (
                        edge_to_face_map[i, 1],
                        edge_to_face_map[i, 0],
                    )
                    continue
                if first_face_sign == 1:
                    continue
                else:
                    raise RuntimeError(f"Sign cannon be {first_face_sign}")

    return edge_to_face_map


def write_nodes_to_file(node_coords, wallfile_name="wallfile.txt"):
    num_nodes = int(node_coords.shape[0] / 2)
    wallfile = open(wallfile_name, "w")
    wallfile.write("# Wallfile automatically generated by script\n")
    wallfile.write("1 \n")
    wallfile.write("#\n")
    wallfile.write("%d\n" % num_nodes)
    wallfile.write("#\n")
    for i in range(0, num_nodes):
        wallfile.write(
            "%26.20f   %26.20f \n" % (node_coords[2 * i], node_coords[2 * i + 1])
        )
    wallfile.close()


def write_dummy_bg_files_aux(nzone, nwall_input, stratum_start):
    nwallsegs = nwall_input
    nperline = 10

    f = open("plasmafile.txt", "w")
    f.write("%10s %10s %10s %10s %10s\n" % ("zone", "T(1)", "N(1)", "T(2)", "N(2)"))
    for i in range(0, nzone):
        f.write("%i %e %e %e %e\n" % (i + 1, 100.0, 1e19, 100.0, 1e19))
    f.close()

    f = open("sourcefile.txt", "w")
    f.write("#\nstratum\n#\n")
    for i in range(0, nwallsegs):
        f.write("%d " % (stratum_start + i))
        if ((i + 1) % nperline == 0) and i != nwallsegs - 1:
            f.write("\n")
    f.write("\n")
    f.write("#\nsegment\n#\n")
    for i in range(0, nwallsegs):
        f.write("%d " % (0))
        if ((i + 1) % nperline == 0) and i != nwallsegs - 1:
            f.write("\n")
    f.write("\n")
    f.write("#\nF\n#\n")
    for i in range(0, nwallsegs):
        f.write("%e " % (1e20))
        if ((i + 1) % nperline == 0) and i != nwallsegs - 1:
            f.write("\n")


def convert2degas2(
    mesh_filename, netcdf_filename="geometry.nc", create_aux_files=True, tol=1e-10
):
    """Convert Omega_h mesh to Degas2 NetCDF geometry file and auxiliary files.

    Parameters
    ----------
    mesh_filename : str
        Path to the Omega_h mesh file (.osh) to be converted. This mesh must have a boundary layer. See [workflow documentation](https://gist.github.com/Fuad-HH/ea6d4913adcfb4d857ef2a77aebaaa2c)
        for details on how to create such a mesh.
    netcdf_filename : str, optional
        Desired name for the output NetCDF file. Default is "geometry.nc".
    create_aux_files : bool, optional
        If True, creates auxiliary files (plasmafile.txt, sourcefile.txt, wallfile.txt) required for Degas2 simulations. Default is True.
    tol : float, optional
        Tolerance for numerical comparisons when determining edge orientations and other geometric properties. Default is 1e-10.

    """

    assert netcdf_filename.endswith(".nc"), (
        "Degas2 mesh name should end with .nc but given {}".format(netcdf_filename)
    )
    with OmegaHMesh(mesh_filename) as mesh:
        assert mesh.has_boundary_layer, (
            "Degas2 requires mesh to have a boundary layer. Use addBonudaryLayer tool from tomms."
        )
        [edge_coefficients, edge_types_, boundary_edge_ids, face2edge_map] = (
            mesh.get_all_geometry_info(tol=tol)
        )
        Ntri = face2edge_map.shape[0]  # ncells
        boundary_face_flag = mesh.get_boundary_face_flag()
        cell_bounding_boxes = mesh.get_cell_bounding_boxes()
        tri_volumes = mesh.get_cell_volumes()
        centroids = mesh.get_cell_centroids().reshape((Ntri, 2))
        edge_coordinates = mesh.get_edge_coordinates()
        edge_to_face_map = mesh.get_edge_to_face_map()
        # sorted like e0+, e0-, e1+, e1-, ...
        edge_to_face_map_sorted = sort_edge_to_face_map(edge_to_face_map, face2edge_map)
        wall_nodes_flag = mesh.get_integer_tag_array(0, "isOnWall")
        first_wall_adjacent_faces = mesh.get_wall_adjacent_triangles()
        first_wall_edges = mesh.get_wall_edge_ids()

        if create_aux_files:
            node_coordinates = mesh.get_node_coordinates()
            write_nodes_to_file(node_coordinates)

    num_boundary_face = boundary_face_flag.sum()
    Nplasma = Ntri - num_boundary_face
    Nedge = edge_coefficients.shape[0]
    Nwall = boundary_edge_ids.shape[0]
    # universal cells has Nwall boundaries and each triangle has 3 sides and 2 cut surfaces
    nboundaries = Nwall + 3 * Ntri
    nneighbors = Nedge * 2
    num_first_wall_points = np.sum(wall_nodes_flag)
    num_zones = Nplasma + num_first_wall_points
    nsectors = 2 * num_first_wall_points

    if create_aux_files:
        write_dummy_bg_files_aux(Nplasma, num_first_wall_points, Nplasma + 1)

    # FIXME NETCDF_CLASSIC is limited to 2GB
    root_g = netCDF4.Dataset(netcdf_filename, mode="w", format="NETCDF4_CLASSIC")

    # *********************************************************************************************** #
    # ---------------------------------- Dimensions ------------------------------------------------- #
    # *********************************************************************************************** #

    root_g.createDimension("vector", 3)
    root_g.createDimension("string", 300)
    root_g.createDimension("cell_info_ind", 4)
    root_g.createDimension("cell_ind", Ntri + 1)
    root_g.createDimension("surface_ind", Nedge)
    root_g.createDimension("boundary_ind", nboundaries)
    root_g.createDimension("neighbor_ind", nneighbors + 1)
    root_g.createDimension("neg_pos", 2)
    root_g.createDimension("surface_info_ind", 2)
    root_g.createDimension("surface_tx_ind", 2)
    root_g.createDimension("tx_ind_1", 3)
    root_g.createDimension("tx_ind_2", 4)
    root_g.createDimension("transform_ind", 1)
    root_g.createDimension("coeff_ind", 11)
    root_g.createDimension("zone_type_ind", 4)
    root_g.createDimension("zone_index_ind", 4)
    root_g.createDimension("zone_ind", num_zones + 1)
    root_g.createDimension("sector_ind", nsectors + 1)
    root_g.createDimension("sector_neg_pos_ind", 2)
    root_g.createDimension("sector_type_ind", 17)
    root_g.createDimension("vacuum_ind", 1)
    root_g.createDimension("plasma_ind", num_first_wall_points + 1)
    root_g.createDimension("target_ind", num_first_wall_points + 1)
    root_g.createDimension("wall_ind", 1)
    root_g.createDimension("exit_ind", 1)
    root_g.createDimension("sc_diag_name_string", 40)
    root_g.createDimension("diag_grp_ind", 4)
    root_g.createDimension("sc_diag_ind", 3 * num_first_wall_points)
    root_g.createDimension("de_symbol_string", 24)
    root_g.createDimension("de_name_string", 100)
    root_g.createDimension("de_grp_ind", 1)
    root_g.createDimension("de_zone_frags_ind", 100)
    root_g.createDimension("de_tot_view_ind", 1)
    root_g.createDimension("de_start_end_ind", 2)
    root_g.createDimension("de_view_ind", 1)
    print("Dimensions created. Now creating variables...")

    # *********************************************************************************************** #
    # ---------------------------------- Variables -------------------------------------------------- #
    # *********************************************************************************************** #

    ncells_var = root_g.createVariable("ncells", "i4")
    nsurfaces_var = root_g.createVariable("nsurfaces", "i4")
    nboundaries_var = root_g.createVariable("nboundaries", "i4")
    nneighbors_var = root_g.createVariable("nneighbors", "i4")
    ntransforms_var = root_g.createVariable("ntransforms", "i4")
    geometry_symmetry_var = root_g.createVariable("geometry_symmetry", "i4")
    universal_cell_min_var = root_g.createVariable(
        "universal_cell_min", "f8", ("vector",)
    )
    universal_cell_max_var = root_g.createVariable(
        "universal_cell_max", "f8", ("vector",)
    )
    universal_cell_vol_var = root_g.createVariable("universal_cell_vol", "f8")
    cells_var = root_g.createVariable(
        "cells",
        "i4",
        (
            "cell_ind",
            "cell_info_ind",
        ),
    )
    surfaces_var = root_g.createVariable(
        "surfaces",
        "i4",
        (
            "surface_ind",
            "surface_info_ind",
            "neg_pos",
        ),
    )
    surfaces_tx_ind_var = root_g.createVariable(
        "surfaces_tx_ind",
        "i4",
        (
            "surface_ind",
            "surface_tx_ind",
            "neg_pos",
        ),
    )
    surfaces_tx_mx_var = root_g.createVariable(
        "surfaces_tx_mx",
        "f8",
        (
            "transform_ind",
            "tx_ind_2",
            "tx_ind_1",
        ),
    )
    surface_sectors_var = root_g.createVariable(
        "surface_sectors",
        "i4",
        (
            "surface_ind",
            "surface_info_ind",
            "neg_pos",
        ),
    )
    boundaries_var = root_g.createVariable("boundaries", "i4", ("boundary_ind",))
    neighbors_var = root_g.createVariable("neighbors", "i4", ("neighbor_ind",))
    surface_coeffs_var = root_g.createVariable(
        "surface_coeffs",
        "f8",
        (
            "surface_ind",
            "coeff_ind",
        ),
    )
    surface_points_var = root_g.createVariable(
        "surface_points",
        "f8",
        (
            "surface_ind",
            "neg_pos",
            "vector",
        ),
    )
    zn_num_var = root_g.createVariable("zn_num", "i4")
    zone_type_num_var = root_g.createVariable("zone_type_num", "i4", ("zone_type_ind",))
    zone_type_var = root_g.createVariable("zone_type", "i4", ("zone_ind",))
    zone_index_var = root_g.createVariable(
        "zone_index",
        "i4",
        (
            "zone_ind",
            "zone_index_ind",
        ),
    )
    zone_index_min_var = root_g.createVariable(
        "zone_index_min", "i4", ("zone_index_ind",)
    )
    zone_index_max_var = root_g.createVariable(
        "zone_index_max", "i4", ("zone_index_ind",)
    )
    zone_pointer_var = root_g.createVariable("zone_pointer", "i4", ("zone_ind",))
    zone_volume_var = root_g.createVariable("zone_volume", "f8", ("zone_ind",))
    zone_center_var = root_g.createVariable(
        "zone_center",
        "f8",
        (
            "zone_ind",
            "vector",
        ),
    )
    zone_min_var = root_g.createVariable(
        "zone_min",
        "f8",
        (
            "zone_ind",
            "vector",
        ),
    )
    zone_max_var = root_g.createVariable(
        "zone_max",
        "f8",
        (
            "zone_ind",
            "vector",
        ),
    )
    nsectors_var = root_g.createVariable("nsectors", "i4")
    strata_var = root_g.createVariable("strata", "i4", ("sector_ind",))
    sector_strata_segment_var = root_g.createVariable(
        "sector_strata_segment", "i4", ("sector_ind",)
    )
    sectors_var = root_g.createVariable("sectors", "i4", ("sector_ind",))
    sector_zone_var = root_g.createVariable("sector_zone", "i4", ("sector_ind",))
    sector_surface_var = root_g.createVariable("sector_surface", "i4", ("sector_ind",))
    sector_points_var = root_g.createVariable(
        "sector_points",
        "f8",
        (
            "sector_ind",
            "sector_neg_pos_ind",
            "vector",
        ),
    )
    sector_type_pointer_var = root_g.createVariable(
        "sector_type_pointer",
        "i4",
        (
            "sector_ind",
            "sector_type_ind",
        ),
    )
    sc_vacuum_num_var = root_g.createVariable("sc_vacuum_num", "i4")
    vacuum_sector_var = root_g.createVariable("vacuum_sector", "i4", ("vacuum_ind",))
    sc_plasma_num_var = root_g.createVariable("sc_plasma_num", "i4")
    plasma_sector_var = root_g.createVariable("plasma_sector", "i4", ("plasma_ind",))
    sc_target_num_var = root_g.createVariable("sc_target_num", "i4")
    target_sector_var = root_g.createVariable("target_sector", "i4", ("target_ind",))
    target_material_var = root_g.createVariable(
        "target_material", "i4", ("target_ind",)
    )
    target_temperature_var = root_g.createVariable(
        "target_temperature", "f8", ("target_ind",)
    )
    target_recyc_coef_var = root_g.createVariable(
        "target_recyc_coef", "f8", ("target_ind",)
    )
    sc_wall_num_var = root_g.createVariable("sc_wall_num", "i4")
    wall_sector_var = root_g.createVariable("wall_sector", "i4", ("wall_ind",))
    wall_material_var = root_g.createVariable("wall_material", "i4", ("wall_ind",))
    wall_temperature_var = root_g.createVariable(
        "wall_temperature", "f8", ("wall_ind",)
    )
    wall_recyc_coef_var = root_g.createVariable("wall_recyc_coef", "f8", ("wall_ind",))
    sc_exit_num_var = root_g.createVariable("sc_exit_num", "i4")
    exit_sector_var = root_g.createVariable("exit_sector", "i4", ("exit_ind",))
    sc_diagnostic_grps_var = root_g.createVariable("sc_diagnostic_grps", "i4")
    sc_diag_max_bins_var = root_g.createVariable("sc_diag_max_bins", "i4")
    diagnostic_grp_name_var = root_g.createVariable(
        "diagnostic_grp_name",
        "c",
        (
            "diag_grp_ind",
            "sc_diag_name_string",
        ),
    )
    diagnostic_num_sectors_var = root_g.createVariable(
        "diagnostic_num_sectors", "i4", ("diag_grp_ind",)
    )
    diagnostic_var_var = root_g.createVariable(
        "diagnostic_var", "i4", ("diag_grp_ind",)
    )
    diagnostic_tab_index_var = root_g.createVariable(
        "diagnostic_tab_index", "i4", ("diag_grp_ind",)
    )
    diagnostic_min_var = root_g.createVariable(
        "diagnostic_min", "f8", ("diag_grp_ind",)
    )
    diagnostic_delta_var = root_g.createVariable(
        "diagnostic_delta", "f8", ("diag_grp_ind",)
    )
    diagnostic_spacing_var = root_g.createVariable(
        "diagnostic_spacing", "i4", ("diag_grp_ind",)
    )
    diagnostic_grp_base_var = root_g.createVariable(
        "diagnostic_grp_base", "i4", ("diag_grp_ind",)
    )
    sc_diag_size_var = root_g.createVariable("sc_diag_size", "i4")
    diagnostic_sector_tab_var = root_g.createVariable(
        "diagnostic_sector_tab", "i4", ("sc_diag_ind",)
    )
    de_grps_var = root_g.createVariable("de_grps", "i4")
    de_max_bins_var = root_g.createVariable("de_max_bins", "i4")
    de_zone_frags_dim_var = root_g.createVariable("de_zone_frags_dim", "i4")
    de_zone_frags_size_var = root_g.createVariable("de_zone_frags_size", "i4")
    detector_name_var = root_g.createVariable(
        "detector_name",
        "c",
        (
            "de_grp_ind",
            "de_name_string",
        ),
    )
    detector_num_views_var = root_g.createVariable(
        "detector_num_views", "i4", ("de_grp_ind",)
    )
    detector_var_var = root_g.createVariable("detector_var", "i4", ("de_grp_ind",))
    detector_tab_index_var = root_g.createVariable(
        "detector_tab_index", "i4", ("de_grp_ind",)
    )
    detector_min_var = root_g.createVariable("detector_min", "f8", ("de_grp_ind",))
    detector_delta_var = root_g.createVariable("detector_delta", "f8", ("de_grp_ind",))
    detector_spacing_var = root_g.createVariable(
        "detector_spacing", "i4", ("de_grp_ind",)
    )
    detector_total_views_var = root_g.createVariable("detector_total_views", "i4")
    de_view_points_var = root_g.createVariable(
        "de_view_points",
        "f8",
        (
            "de_tot_view_ind",
            "de_start_end_ind",
            "vector",
        ),
    )
    de_view_algorithm_var = root_g.createVariable(
        "de_view_algorithm", "i4", ("de_tot_view_ind",)
    )
    de_view_halfwidth_var = root_g.createVariable(
        "de_view_halfwidth", "f8", ("de_tot_view_ind",)
    )
    de_zone_frags_var = root_g.createVariable(
        "de_zone_frags", "f8", ("de_zone_frags_ind",)
    )
    de_zone_frags_start_var = root_g.createVariable(
        "de_zone_frags_start", "i4", ("de_tot_view_ind",)
    )
    de_zone_frags_num_var = root_g.createVariable(
        "de_zone_frags_num", "i4", ("de_tot_view_ind",)
    )
    de_zone_frags_zones_var = root_g.createVariable(
        "de_zone_frags_zones", "i4", ("de_zone_frags_ind",)
    )
    de_zone_frags_min_zn_var = root_g.createVariable(
        "de_zone_frags_min_zn", "i4", ("de_tot_view_ind",)
    )
    de_zone_frags_max_zn_var = root_g.createVariable(
        "de_zone_frags_max_zn", "i4", ("de_tot_view_ind",)
    )
    de_view_base_var = root_g.createVariable("de_view_base", "i4", ("de_grp_ind",))
    de_view_size_var = root_g.createVariable("de_view_size", "i4")
    de_view_tab_var = root_g.createVariable("de_view_tab", "i4", ("de_view_ind",))
    print("Variables created. Now filling the variables...")

    # *********************************************************************************************** #
    # ---------------------------------- Fill Up ---------------------------------------------------- #
    # *********************************************************************************************** #

    ncells_var[:] = Ntri
    nsurfaces_var[:] = Nedge
    nboundaries_var[:] = nboundaries
    nneighbors_var[:] = nneighbors
    ntransforms_var[:] = 0  # hardcoded to zero
    geometry_symmetry_var[:] = 2  # hardcoded to two

    assert cell_bounding_boxes.size == Ntri * 4
    universal_cell_min = [
        np.min(cell_bounding_boxes[0::4]),
        0.0,
        np.min(cell_bounding_boxes[1::4]),
    ]
    universal_cell_max = [
        np.max(cell_bounding_boxes[2::4]),
        6.28318530717959,
        np.max(cell_bounding_boxes[3::4]),
    ]
    universal_cell_min_var[:] = universal_cell_min
    universal_cell_max_var[:] = universal_cell_max

    total_volume = tri_volumes.sum()
    universal_cell_vol_var[:] = total_volume

    # ------------------------- Cells -------------------------------------- #
    # in note ncells = Ntri = Nplasma + 2*Nwall; ncells = ncells = Ntri+2*Nwall
    cells = -1 + np.zeros([Ntri + 1, 4], dtype=int)  # ask
    cells[0, 0:4] = [1, Nwall, Nwall, 0]

    cells[1 : Ntri + 1, 0] = 1 + Nwall + 3 * np.array(range(0, Ntri), dtype=int)
    cells[1 : Ntri + 1, 1] = 3
    cells[1 : Ntri + 1, 2] = 3

    cells[1 : Nplasma + 1, 3] = np.array(range(1, Nplasma + 1), dtype=int)
    # now comes the wall zones
    for i, face in enumerate(first_wall_adjacent_faces[1::2]):
        cells[face + 1, 3] = i + Nplasma + 1

    assert np.max(cells[:, 3]) == num_zones, (
        f"Error: Upto this point, {np.max(cells[:, 3])=} != {num_zones=}"
    )

    # all remaining cells has zone_id = num_zones
    # the remaining values should be -1
    cells[cells[:, 3] == -1, 3] = num_zones + 1

    cells_var[:] = cells

    # ------------------------- Boundaries ------------------------------------ #
    boundaries = np.zeros(nboundaries, dtype=int)
    for i in range(0, Ntri):
        bdy_start = Nwall + i * 3

        boundaries[bdy_start : bdy_start + 3] = [
            (face2edge_map[i, 0] + 1) * face2edge_map[i, 1],
            (face2edge_map[i, 2] + 1) * face2edge_map[i, 3],
            (face2edge_map[i, 4] + 1) * face2edge_map[i, 5],
        ]

    # fill edges of the universal cell
    boundaries[0:Nwall] = np.sign(boundary_edge_ids) * (np.abs(boundary_edge_ids) + 1)

    boundaries_var[:] = boundaries

    # ------------------------ Surfaces ------------------------------------ #
    surfaces = np.zeros([Nedge, 2, 2], dtype=int)
    # pointers uses 0 based indexing
    surfaces[:Nedge, 0, 0] = 1 + 2 * np.arange(Nedge)
    surfaces[:Nedge, 0, 1] = 0 + 2 * np.arange(Nedge)
    # number of faces
    for i in range(0, Nedge):
        pos_face = edge_to_face_map_sorted[i, 0]
        neg_face = edge_to_face_map_sorted[i, 1]
        if neg_face == -1 and pos_face != -1:
            surfaces[i, 1, 0] = 0
            surfaces[i, 1, 1] = 1
        elif pos_face == -1 and neg_face != -1:
            surfaces[i, 1, 1] = 0
            surfaces[i, 1, 0] = 1
        elif neg_face != -1 and pos_face != -1:
            surfaces[i, 1, :] = 1
        else:
            raise RuntimeError(
                f"pos_face={pos_face}, neg_face={neg_face} is not correct!"
            )

    surfaces_var[:] = surfaces

    # ----------------------- Neighbors (Edge Adjacency Info) ----------------- #
    neighbors = np.zeros(nneighbors + 1, dtype=int)
    edge_to_face_map_sorted[~(edge_to_face_map_sorted == -1)] += (
        1  # use one based indexing
    )
    neighbors[0 : Nedge * 2] = edge_to_face_map_sorted.reshape(Nedge * 2)
    neighbors_var[:] = neighbors

    # --------------------- Surface Coefficients ------------------------------ #
    # TODO Create a better documentation of the coeffs
    surface_coeffs = np.zeros([Nedge, 11])
    # -b ^ 2 for a cone, -R ^ 2 for a cylinder, -Z0 for a plane
    surface_coeffs[:Nedge, 0] = edge_coefficients[:, 3]
    # b = 0 for a cylinder, 1/2 for a plane, and intercept for a cone
    surface_coeffs[:Nedge, 3] = edge_coefficients[:, 2]
    # m=1 for cylinder, slope for a cone, and 0 for a plane
    surface_coeffs[:Nedge, 4] = edge_coefficients[:, 0]
    surface_coeffs[:Nedge, 5] = edge_coefficients[:, 0]
    # in notebook np.where(np.logical_or(planecond,cylcond),np.zeros(Nsurfs),-np.ones(Nsurfs))
    surface_coeffs[:Nedge, 6] = edge_coefficients[:, 1]
    # cone up or down direction flag
    surface_coeffs[:Nedge, 10] = edge_coefficients[:, 4]

    surface_coeffs_var[:] = surface_coeffs

    # --------------------- Zones ------------------------------------------ #
    zone_center = np.zeros([num_zones + 1, 3])
    zone_center[0:Nplasma, 0] = centroids[0:Nplasma, 0]
    zone_center[0:Nplasma, 2] = centroids[0:Nplasma, 1]
    # they do specify it with the first cells centroid we are doing it with the last
    # for this zone
    assert cells[-1, 3] == num_zones + 1, (
        f"The next 2 lines won't work if this assertion fails. {cells[-1,3]=}, {num_zones=}"
    )
    zone_center[-1, 0] = centroids[-1, 0]
    zone_center[-1, 2] = centroids[-1, 1]
    # wall adjacent zones
    outside_wall_faces = first_wall_adjacent_faces[1::2]
    zone_center[Nplasma:-1, 0] = centroids[outside_wall_faces, 0]
    zone_center[Nplasma:-1, 2] = centroids[outside_wall_faces, 1]

    zone_center_var[:] = zone_center

    zone_min = np.zeros((num_zones + 1, 3))
    zone_min[0:Nplasma, 0] = cell_bounding_boxes[0 : Nplasma * 4 : 4]
    zone_min[0:Nplasma, 2] = cell_bounding_boxes[1 : Nplasma * 4 : 4]

    zone_min[Nplasma:-1, 0] = cell_bounding_boxes[outside_wall_faces * 4]
    zone_min[Nplasma:-1, 2] = cell_bounding_boxes[outside_wall_faces * 4 + 1]

    zone_min[-1, :] = [universal_cell_min[0], 0.0, universal_cell_min[2]]
    zone_min_var[:] = zone_min

    zone_max = np.zeros((num_zones + 1, 3))
    zone_max[0:Nplasma, 0] = cell_bounding_boxes[2 : Nplasma * 4 : 4]
    zone_max[0:Nplasma, 2] = cell_bounding_boxes[3 : Nplasma * 4 : 4]

    zone_max[Nplasma:-1, 0] = cell_bounding_boxes[outside_wall_faces * 4 + 2]
    zone_max[Nplasma:-1, 2] = cell_bounding_boxes[outside_wall_faces * 4 + 3]

    zone_max[-1, :] = [universal_cell_max[0], 0.0, universal_cell_max[2]]
    zone_max_var[:] = zone_max

    zone_volume = np.empty(num_zones + 1)
    zone_volume[0:Nplasma] = tri_volumes[0:Nplasma]

    zone_volume[Nplasma:-1] = tri_volumes[outside_wall_faces]

    # do not move this line here in this block
    zone_volume[-1] = total_volume - np.sum(zone_volume)
    zone_volume_var[:] = zone_volume

    zone_type = 2 * np.ones(num_zones + 1, dtype=int)
    zone_type[Nplasma:] = 3
    zone_type_var[:] = zone_type

    zone_pointer = np.array(range(1, num_zones + 2))
    zone_pointer[Nplasma:] = np.array(range(1, num_zones - Nplasma + 2))
    zone_pointer_var[:] = zone_pointer

    # ----------------------- Surface Points ------------------------------ #
    surface_points = np.zeros([Nedge, 2, 3])
    surface_points[0:Nedge, :, 0] = edge_coordinates[0 : Nedge * 4 : 2].reshape(
        (Nedge, 2)
    )
    surface_points[0:Nedge, :, 2] = edge_coordinates[1 : Nedge * 4 : 2].reshape(
        (Nedge, 2)
    )
    surface_points_var[:] = surface_points

    # -------------------------- Sector ---------------------------------------- #

    sc_plasma_num = num_first_wall_points
    sc_target_num = num_first_wall_points
    plasma_sector = np.zeros(sc_plasma_num + 1, dtype=int)
    target_sector = np.zeros(sc_target_num + 1, dtype=int)
    plasma_sector[0] = INT_UNUSED
    target_sector[0] = INT_UNUSED
    plasma_sector[1:] = 1.0 + 2 * np.arange(sc_plasma_num)
    target_sector[1:] = 2.0 + 2 * np.arange(sc_target_num)
    plasma_sector_var[:] = plasma_sector
    target_sector_var[:] = target_sector

    sectors = np.empty([nsectors + 1], dtype=int)
    sectors[0] = INT_UNUSED
    sector_surface = np.zeros(nsectors + 1, dtype=int)
    sector_surface[0] = INT_UNUSED
    for i in range(0, first_wall_edges.shape[0]):
        wall_edge = first_wall_edges[i]
        plasma_cell = first_wall_adjacent_faces[2 * i + 0]
        wall_cell = first_wall_adjacent_faces[2 * i + 1]
        positive_cell = edge_to_face_map_sorted[wall_edge, 0] - 1
        if plasma_cell == positive_cell:
            sector_surface[2 * i + 1] = wall_edge + 1
            sector_surface[2 * i + 2] = -wall_edge - 1
            sectors[2 * i + 1] = i * 2 + 2
            sectors[2 * i + 2] = i * 2 + 1
        elif wall_cell == positive_cell:
            sector_surface[2 * i + 1] = -wall_edge - 1
            sector_surface[2 * i + 2] = wall_edge + 1
            sectors[2 * i + 1] = i * 2 + 1
            sectors[2 * i + 2] = i * 2 + 2
        else:
            raise RuntimeError(
                f"Incorrect cells {wall_edge=}, {plasma_cell=}, {wall_cell=}, {positive_cell=} "
            )

    sector_surface_var[:] = sector_surface
    sectors_var[:] = sectors

    surface_sectors = np.zeros([Nedge, 2, 2], dtype=int)

    # the real edges (not cut surfaces) will have sectors associated
    current_sector_pointer = 1
    for i in range(0, Nedge):
        # fixme replace this isin for performance
        if not np.isin(i, first_wall_edges):
            surface_sectors[i, 1, :] = 0
            surface_sectors[i, 0, :] = current_sector_pointer
        else:
            surface_sectors[i, 1, :] = 1
            surface_sectors[i, 0, 0] = current_sector_pointer
            surface_sectors[i, 0, 1] = current_sector_pointer + 1
            current_sector_pointer += 2

    surface_sectors_var[:] = surface_sectors

    sector_points = np.zeros([nsectors + 1, 2, 3])
    sector_points[0, :, :] = DBL_UNUSED
    for i in range(0, num_first_wall_points):
        wall_edge = first_wall_edges[i]
        sector_points[2 * i + 1, 0, :] = [
            edge_coordinates[wall_edge * 4 + 0],
            0.0,
            edge_coordinates[wall_edge * 4 + 1],
        ]
        sector_points[2 * i + 1, 1, :] = [
            edge_coordinates[wall_edge * 4 + 2],
            0.0,
            edge_coordinates[wall_edge * 4 + 3],
        ]
        sector_points[2 * i + 2, :, :] = sector_points[2 * i + 1, :, :]

    sector_points_var[:] = sector_points

    sector_zone = np.zeros(nsectors + 1, dtype=int)
    sector_zone[0] = INT_UNUSED
    sector_zone[1:] = cells[first_wall_adjacent_faces + 1, 3]
    sector_zone_var[:] = sector_zone

    sector_type_pointer = INT_UNUSED * np.ones([nsectors + 1, 17], dtype=int)
    sc_diag_size = 3 * num_first_wall_points
    diagnostic_sector_tab = np.zeros(sc_diag_size, dtype=int)

    sector_type_pointer[0, :] = INT_UNUSED / 2
    # fixme after surface, face, etc.
    for i in range(0, num_first_wall_points):
        sector_type_pointer[2 * i + 1, 1] = i + 1
        sector_type_pointer[2 * i + 2, 2] = i + 1
        sector_type_pointer[2 * i + 2, 5:8] = i + 1

        diagnostic_sector_tab[i] = 2 * i + 2
        diagnostic_sector_tab[num_first_wall_points + i] = 2 * i + 2
        diagnostic_sector_tab[2 * num_first_wall_points + i] = 2 * i + 2

    sector_type_pointer_var[:] = sector_type_pointer
    diagnostic_sector_tab_var[:] = diagnostic_sector_tab

    # collection of signed edges, each cells are bounded by its strata
    # sector is surface and a direction, something physically happennign
    # for each sector, what strata is associated with
    # strata index and which edge is it

    sector_strata_segment = np.zeros(nsectors + 1, dtype=int)
    sector_strata_segment[0] = INT_UNUSED
    # can be wrong since difference in boundary layer
    sector_strata_segment_var[:] = sector_strata_segment

    # ----------------------- Unmodified Variables ----------------------------- #
    de_grps = 0
    de_view_size = 1
    de_grps = 0
    de_max_bins = 0
    de_zone_frags_size = 0
    de_zone_frags_ind = 100
    detector_total_views = 0
    sc_vacuum_num = 0
    sc_plasma_num = num_first_wall_points
    sc_target_num = num_first_wall_points
    sc_wall_num = 0
    sc_exit_num = 0
    sc_diagnostic_grps = 3
    sc_diag_max_bins = 4

    zn_num_var[:] = num_zones + 1
    vacuum_sector = np.zeros(sc_vacuum_num + 1, dtype=int)
    vacuum_sector[0] = INT_UNUSED
    de_view_tab = INT_UNUSED * np.ones(de_view_size, dtype=int)
    de_view_tab_var[:] = de_view_tab
    de_view_size_var[:] = de_view_size
    de_view_base = INT_UNUSED * np.ones(de_grps + 1, dtype=int)
    de_view_base_var[:] = de_view_base
    sc_plasma_num_var[:] = sc_plasma_num
    sc_target_num_var[:] = sc_target_num
    sc_wall_num_var[:] = sc_wall_num
    sc_exit_num_var[:] = sc_exit_num
    sc_diagnostic_grps_var[:] = sc_diagnostic_grps
    sc_diag_max_bins_var[:] = sc_diag_max_bins
    sc_vacuum_num_var[:] = sc_vacuum_num

    # target_temperature = np.zeros(sc_target_num+1) # ask
    Twall = 300 * 1.380649e-23
    target_temperature = Twall * np.ones(sc_target_num + 1)
    target_temperature[0] = DBL_UNUSED
    target_temperature_var[:] = target_temperature

    wall_recyc_coef = np.zeros(sc_wall_num + 1)
    wall_recyc_coef[0] = DBL_UNUSED
    wall_recyc_coef_var[:] = wall_recyc_coef

    exit_sector = np.zeros(sc_exit_num + 1, dtype=int)
    exit_sector[0] = INT_UNUSED
    exit_sector_var[:] = exit_sector

    vacuum_sector = np.zeros(sc_vacuum_num + 1, dtype=int)
    wall_sector = np.zeros(sc_wall_num + 1, dtype=int)
    wall_material = np.zeros(sc_wall_num + 1, dtype=int)
    wall_temperature = np.zeros(sc_wall_num + 1)
    wall_recyc_coef = np.zeros(sc_wall_num + 1)
    vacuum_sector[0] = INT_UNUSED
    wall_sector[0] = INT_UNUSED
    wall_material[0] = INT_UNUSED
    wall_temperature[0] = DBL_UNUSED
    wall_recyc_coef[0] = DBL_UNUSED
    vacuum_sector_var[:] = vacuum_sector
    wall_sector_var[:] = wall_sector
    wall_material_var[:] = wall_material
    wall_temperature_var[:] = wall_temperature

    recyc_coef = 0.99
    target_recyc_coef = recyc_coef * np.ones(sc_target_num + 1)
    target_recyc_coef[0] = DBL_UNUSED
    target_recyc_coef_var[:] = target_recyc_coef

    target_material = 6 * np.ones(sc_target_num + 1, dtype=int)
    target_material[0] = INT_UNUSED
    target_material_var[:] = target_material

    zone_type_num = np.zeros(4, dtype=int)
    zone_type_num[1] = Nplasma
    zone_type_num[2] = num_zones - Nplasma + 1
    zone_type_num_var[:] = zone_type_num

    surfaces_tx_ind = np.zeros([Nedge, 2, 2], dtype=int)
    surfaces_tx_mx = DBL_UNUSED * np.ones([1, 4, 3])
    surfaces_tx_ind_var[:] = surfaces_tx_ind
    surfaces_tx_mx_var[:] = surfaces_tx_mx

    zone_index = np.zeros([num_zones + 1, 4], dtype=int)
    zone_index[:, 3] = np.array(range(1, num_zones + 2))
    zone_index_var[:] = zone_index

    zone_index_min = np.zeros(4, dtype=int)
    zone_index_max = np.zeros(4, dtype=int)
    zone_index_min_var[:] = zone_index_min
    zone_index_max_var[:] = zone_index_max

    nsectors_var[:] = nsectors

    strata = np.empty(nsectors + 1)
    strata[0] = INT_UNUSED
    # can be wrong since I didn't find exact definition.
    strata[1:] = np.repeat(
        np.arange(Nplasma + 1, Nplasma + 1 + num_first_wall_points), 2
    )
    strata_var[:] = strata

    detector_num_views = INT_UNUSED * np.ones(shape=de_grps + 1, dtype=int)
    detector_var = INT_UNUSED * np.ones(de_grps + 1, dtype=int)
    detector_tab_index = INT_UNUSED * np.ones(de_grps + 1, dtype=int)
    detector_min = DBL_UNUSED * np.ones(de_grps + 1)
    detector_delta = DBL_UNUSED * np.ones(de_grps + 1)
    detector_spacing = INT_UNUSED * np.ones(de_grps + 1, dtype=int)
    de_view_points = DBL_UNUSED * np.ones([detector_total_views + 1, 2, 3])
    de_view_algorithm = INT_UNUSED * np.ones(detector_total_views + 1, dtype=int)
    de_view_halfwidth = DBL_UNUSED * np.ones(detector_total_views + 1)
    de_zone_frags = np.zeros(de_zone_frags_ind)
    de_zone_frags_start = np.zeros(detector_total_views + 1, dtype=int)
    de_zone_frags_num = np.zeros(detector_total_views + 1, dtype=int)
    de_zone_frags_zones = np.zeros(de_zone_frags_ind)
    de_zone_frags_zones[:] = 4
    de_zone_frags_zones[0] = INT_UNUSED
    de_zone_frags_min_zn = np.zeros(detector_total_views + 1, dtype=int)
    de_zone_frags_max_zn = np.zeros(detector_total_views + 1, dtype=int)
    detector_num_views_var[:] = detector_num_views
    detector_var_var[:] = detector_var
    detector_tab_index_var[:] = detector_tab_index
    detector_min_var[:] = detector_min
    detector_delta_var[:] = detector_delta
    detector_spacing_var[:] = detector_spacing
    detector_total_views_var[:] = detector_total_views
    de_view_points_var[:] = de_view_points
    de_view_algorithm_var[:] = de_view_algorithm
    de_view_halfwidth_var[:] = de_view_halfwidth
    de_zone_frags_var[:] = de_zone_frags
    de_zone_frags_start_var[:] = de_zone_frags_start
    de_zone_frags_num_var[:] = de_zone_frags_num
    de_zone_frags_zones_var[:] = de_zone_frags_zones
    de_zone_frags_min_zn_var[:] = de_zone_frags_min_zn
    de_zone_frags_max_zn_var[:] = de_zone_frags_max_zn
    diagnostic_num_sectors = np.array([INT_UNUSED, Nwall, Nwall, Nwall])
    diagnostic_var = np.array([INT_UNUSED, 0, 1, 2])
    diagnostic_tab_index = np.array([INT_UNUSED, 0, 4, 4])
    diagnostic_spacing = np.array([INT_UNUSED, 0, 2, 1])
    diagnostic_grp_base = np.array([INT_UNUSED, 0, Nwall, 2 * Nwall])
    diagnostic_min = np.array([DBL_UNUSED, 0, -45.5803383244769, 0.174532925199433])
    diagnostic_delta = np.array([DBL_UNUSED, 0, 2.30258509299405, 0.349065850398866])
    diagnostic_num_sectors_var[:] = diagnostic_num_sectors
    diagnostic_var_var[:] = diagnostic_var
    diagnostic_tab_index_var[:] = diagnostic_tab_index
    diagnostic_min_var[:] = diagnostic_min
    diagnostic_delta_var[:] = diagnostic_delta
    diagnostic_spacing_var[:] = diagnostic_spacing
    diagnostic_grp_base_var[:] = diagnostic_grp_base
    sc_diag_size_var[:] = sc_diag_size
    de_grps_var[:] = de_grps
    de_max_bins_var[:] = de_max_bins
    de_zone_frags_dim = 100
    de_zone_frags_dim_var[:] = de_zone_frags_dim
    de_zone_frags_size_var[:] = de_zone_frags_size

    diagnostic_grp_name_var[0, :] = "UNUSED                                  "
    diagnostic_grp_name_var[1, :] = "Wall and Target Counts                  "
    diagnostic_grp_name_var[2, :] = "Wall and Target Energy Spectrum         "
    diagnostic_grp_name_var[3, :] = "Wall and Target Angle Spectrum          "
    detector_name_var[:] = (
        "UNUSED                                                                                              "
    )

    root_g.sync()
    root_g.close()
