# Shallow ceiling timbers fitted to public room interfaces and actual clearances.
from dataclasses import dataclass
from hearth import Box, ContractError
from hearth.kernel import Capability, Contract, Rule, Plan
from hearth.blocks import name_of, log


@dataclass(frozen=True)
class CeilingTimbers:
    def capability(self):
        return Capability('interior.ceiling-timbers', ('decoration', 'timber-frame', 'ceiling-trim'),
                          assumptions=('ceiling-decoration interface with actual overhead support',),
                          adaptations=('omit cells at stair voids, lights and protected headroom',),
                          guarantees=('one-cell projection', 'retained headroom', 'overhead bearing'))

    def negotiate(self, ctx, parameters):
        if len(ctx.bindings) != 1:
            raise ContractError('ceiling-host', ctx.path)
        port = ctx.view.port(ctx.bindings[0].host, ctx.bindings[0].port)
        if port.kind != 'ceiling-decoration' or port.frame != ctx.frame:
            raise ContractError('ceiling-interface', ctx.path, ctx.frame.origin)
        w, d = port.facts['width'], port.facts['depth']
        points = {(x, 0, z): 'x' for z in (2, d - 3) for x in range(1, w - 1)}
        points.update({(w // 2, 0, z): 'z' for z in range(1, d - 1)})
        members = []
        for q, axis in sorted(points.items()):
            area = ctx.frame.box(Box(q, q))
            above = (q[0], q[1] + 1, q[2])
            if name_of(ctx.state(q)) == 'air' and ctx.view.supports(ctx.world(above)) and not ctx.view.protected(area):
                members.append((q, axis))
        anchors = tuple((q[0], 1, q[2]) for q, _ in members)
        box = Box((1, 0, 1), (w - 2, 0, d - 2))
        if not all(port.region.contains(ctx.world(q)) for q in box.corners()):
            raise ContractError('ceiling-region', ctx.path)
        return Contract(box, rules=(Rule('support', anchors), Rule('expected', tuple(q for q, _ in members), data={'names': ['stripped_dark_oak_log']})),
                        decisions={'members': members, 'omitted_for_structure_or_clearance': len(points) - len(members)})

    def realize(self, ctx, contract):
        p = Plan()
        for q, axis in contract.decisions['members']:
            p.block(q, log('stripped_dark_oak', axis), 'ceiling-member')
        return p
