#  Copyright (c) 2020, Manfred Moitzi
#  License: MIT License
from typing import List
import ezdxf
from ezdxf.math import Shape2d
from ezdxf.render.forms import square
from ezdxf.lldxf import const

DX = 11
SIZE = 10


def add_hatch(
    msp,
    external: List[Shape2d] = None,
    *,
    outermost: List[Shape2d] = None,
    default: List[Shape2d] = None,
    hatch_style=const.HATCH_STYLE_NESTED,
    offset=(0, 0),
):
    def add(path, flags: int, color: int, layer: str):
        path.translate(offset)
        hatch.paths.add_polyline_path(path.vertices, flags=flags)
        msp.add_lwpolyline(
            path.vertices,
            close=True,
            dxfattribs={"color": color, "layer": layer},
        )

    hatch = msp.add_hatch(2, dxfattribs={"layer": "HATCH"})
    hatch.dxf.hatch_style = hatch_style
    if external:
        for path in external:
            add(path.copy(), const.BOUNDARY_PATH_EXTERNAL, 1, "EXTERNAL-PATH")
    if outermost:
        for path in outermost:
            add(path.copy(), const.BOUNDARY_PATH_OUTERMOST, 3, "OUTERMOST-PATH")
    if default:
        for path in default:
            add(path.copy(), const.BOUNDARY_PATH_DEFAULT, 5, "DEFAULT-PATH")


def add_hatch_boundaries_test(msp, offset):
    x, y = offset
    external = Shape2d(square(SIZE))
    outermost = Shape2d(square(SIZE - 2))
    outermost.translate((1, 1))
    default0 = Shape2d(square(SIZE - 4))
    default0.translate((2, 2))
    default1 = Shape2d(square(SIZE - 6))
    default1.translate((3, 3))
    add_hatch(
        msp,
        external=None,
        outermost=None,
        default=[external, outermost, default0, default1],
        hatch_style=const.HATCH_STYLE_NESTED,
        offset=offset,
    )
    add_hatch(
        msp,
        external=[default0, default1],
        outermost=[external, outermost],
        hatch_style=const.HATCH_STYLE_OUTERMOST,
        offset=(x + DX, y),
    )
    add_hatch(
        msp,
        external=[external, outermost, default0, default1],
        hatch_style=const.HATCH_STYLE_IGNORE,
        offset=(x + DX + DX, y),
    )


def add_hatch_boundaries_adjacent(msp, offset):
    x, y = offset
    external = Shape2d(square(SIZE))
    outermost = Shape2d(square(SIZE - 2))
    outermost.translate((1, 1))
    default0 = Shape2d(square(2))
    default0.translate((2, 2))
    default1 = Shape2d(square(2))
    default1.translate((6, 2))
    add_hatch(
        msp,
        external=[external],
        outermost=[outermost],
        default=[default0, default1],
        hatch_style=const.HATCH_STYLE_NESTED,
        offset=offset,
    )
    add_hatch(
        msp,
        external=[external],
        outermost=[outermost],
        default=[default0, default1],
        hatch_style=const.HATCH_STYLE_OUTERMOST,
        offset=(x + DX, y),
    )
    add_hatch(
        msp,
        external=[external],
        outermost=[outermost],
        default=[default0, default1],
        hatch_style=const.HATCH_STYLE_IGNORE,
        offset=(x + DX + DX, y),
    )


def add_hatch_boundaries_overlapping(msp, offset):
    x, y = offset
    external = Shape2d(square(SIZE))
    outermost = Shape2d(square(SIZE - 2))
    outermost.translate((1, 1))
    default0 = Shape2d(square(3))
    default0.translate((2, 2))
    default1 = Shape2d(square(3))
    default1.translate((4, 3))
    add_hatch(
        msp,
        external=[external],
        outermost=[outermost],
        default=[default0, default1],
        hatch_style=const.HATCH_STYLE_NESTED,
        offset=offset,
    )
    add_hatch(
        msp,
        external=[external],
        outermost=[outermost],
        default=[default0, default1],
        hatch_style=const.HATCH_STYLE_OUTERMOST,
        offset=(x + DX, y),
    )
    add_hatch(
        msp,
        external=[external],
        outermost=[outermost],
        default=[default0, default1],
        hatch_style=const.HATCH_STYLE_IGNORE,
        offset=(x + DX + DX, y),
    )


def add_hatch_boundaries_nested(msp, offset):
    x, y = offset
    external = Shape2d(square(SIZE))
    outermost = Shape2d(square(SIZE - 2))
    outermost.translate((1, 1))
    default0 = Shape2d(square(SIZE - 4))
    default0.translate((2, 2))
    default1 = Shape2d(square(SIZE - 6))
    default1.translate((3, 3))
    add_hatch(
        msp,
        [external],
        outermost=[outermost],
        default=[default0, default1],
        hatch_style=const.HATCH_STYLE_NESTED,
        offset=offset,
    )
    add_hatch(
        msp,
        [external],
        outermost=[outermost],
        default=[default0, default1],
        hatch_style=const.HATCH_STYLE_OUTERMOST,
        offset=(x + DX, y),
    )
    add_hatch(
        msp,
        [external],
        outermost=[outermost],
        default=[default0, default1],
        hatch_style=const.HATCH_STYLE_IGNORE,
        offset=(x + DX + DX, y),
    )


if __name__ == "__main__":
    doc = ezdxf.new()
    add_hatch_boundaries_nested(doc.modelspace(), offset=(0, 0))
    add_hatch_boundaries_adjacent(doc.modelspace(), offset=(0, 11))
    add_hatch_boundaries_overlapping(doc.modelspace(), offset=(0, 22))
    doc.set_modelspace_vport(40, (15, 20))
    doc.saveas("hatch_boundaries.dxf")
