# %% [markdown]
# # Specifying resolutions
# When defining entities, ResolutionSpecs can be attached to control the mesh size within or near the entity and its boundaries.

# %%
from collections import OrderedDict

import shapely

from meshwell.cad_occ import cad_occ
from meshwell.mesh import mesh
from meshwell.occ_xao_writer import write_xao
from meshwell.polysurface import PolySurface
from meshwell.resolution import ConstantInField, ExponentialField, ThresholdField
from meshwell.visualization import plot2D

# %% [markdown]
# ## Default characteristic length
# When creating a Model mesh, default_characteristic_length is used to set the default mesh size.

# %%
box1 = shapely.box(0, 0, 10, 10)
box2 = shapely.box(2, 2, 4, 4)
box3 = shapely.box(8, 4, 12, 6)

boxes = OrderedDict()
boxes["box3"] = box3
boxes["box2"] = box2
boxes["box1"] = box1

polysurfaces = []
for i, (box_name, box) in enumerate(boxes.items()):
    polysurfaces.append(
        PolySurface(
            polygons=box,
            physical_name=box_name,
            mesh_order=i,
        )
    )

write_xao(cad_occ(polysurfaces), "model.xao")


# %%

for default_characteristic_length in [10, 1, 0.5]:
    output_mesh = mesh(
        dim=2,
        input_file="model.xao",
        output_file="default_resolution.msh",
        n_threads=1,
        default_characteristic_length=default_characteristic_length,
    )

    plot2D(
        output_mesh,
        title=f"default_characteristic_length = {default_characteristic_length}",
        wireframe=True,
    )

# %% [markdown]
# ## Constant Resolution
# Constant resolution can be assigned within entities or their boundaries using ConstantInField specification.

# %%

output_mesh = mesh(
    dim=2,
    input_file="model.xao",
    output_file="constant_resolution.msh",
    n_threads=1,
    default_characteristic_length=2,
    resolution_specs={
        "box2": [ConstantInField(resolution=0.3, apply_to="surfaces")],
        "box3": [ConstantInField(resolution=0.3, apply_to="curves")],
    },
)

plot2D(output_mesh, title="ConstantInField", wireframe=True)

# %% [markdown]
# ## Threshold-based Resolution
# This creates a mesh that transitions from fine resolution near boundaries to coarse resolution away from them

# %%

output_mesh = mesh(
    dim=2,
    input_file="model.xao",
    output_file="threshold_resolution.msh",
    n_threads=1,
    default_characteristic_length=2,
    resolution_specs={
        "box2": [
            ThresholdField(sizemin=0.3, distmax=5, sizemax=2.0, apply_to="curves"),
            ConstantInField(resolution=0.3, apply_to="surfaces"),
        ],
        "box3": [
            ThresholdField(sizemin=0.1, distmax=2, sizemax=0.5, apply_to="curves"),
            ThresholdField(
                sizemin=0.5, distmax=5, sizemax=2, distmin=2, apply_to="curves"
            ),
        ],
    },
)


plot2D(output_mesh, title="ThresholdField", wireframe=True)

# %% [markdown]
# ## Exponential Resolution
# Creates a mesh with exponentially varying resolution based on distance from boundaries

# %%

output_mesh = mesh(
    dim=2,
    input_file="model.xao",
    output_file="exponential_resolution.msh",
    n_threads=1,
    default_characteristic_length=2,
    resolution_specs={
        "box2": [
            ExponentialField(
                sizemin=0.3, lengthscale=2, growth_factor=2.0, apply_to="curves"
            ),
            ConstantInField(resolution=0.3, apply_to="surfaces"),
        ],
        "box3": [
            ExponentialField(
                sizemin=0.2, lengthscale=2, growth_factor=2.0, apply_to="curves"
            ),
        ],
    },
)

plot2D(output_mesh, title="ExponentialField", wireframe=True)


# ## Background mesh
# It is also possible to pass a gmsh .pos file to fully parametrize the size field vs position. This is typically more useful once a physical solution has been calculated on an initial mesh, and a new size field has been calculated from the solution. Many solvers also have built-in remeshing, and hence only need an initial mesh before doing their own refinement

# %%
