# Grounded masonry stacks composed with measured foundations and roof abutments.
from dataclasses import dataclass
from hearth import Box
from hearth.kernel import Capability, Contract, Domain, Rule, Plan
from hearth.blocks import state_of, slab


@dataclass(frozen=True)
class Chimney:
    height: int
    cap_overhang: int = 0

    def capability(self):
        return Capability('masonry.chimney', ('chimney', 'support'), inputs={'height': Domain(10, 48), 'cap_overhang': Domain(choices=(0, 1))}, assumptions=('supported three by two base',), guarantees=('continuous masonry shaft', 'capped flue'))

    def negotiate(self, ctx, parameters):
        load = tuple((x, y, z) for x in (0, 2) for z in range(2) for y in range(-1, self.height))
        e = self.cap_overhang
        cap = tuple((x, self.height, z) for x in range(-e, 3 + e) for z in range(-e, 2 + e))
        flue = tuple((1, self.height + 1, z) for z in range(2))
        return Contract(Box((-e, 0, -e), (2 + e, self.height + 2, 1 + e)), rules=(Rule('support', load), Rule('expected', cap, data={'names': ['stone_brick_slab']}), Rule('expected', flue, data={'names': ['cobblestone_wall']})), decisions={'height': self.height, 'cap_overhang': e, 'function': 'exterior flue; no simulated indoor heating'})

    def realize(self, ctx, contract):
        p = Plan()
        rng = ctx.rng('masonry')
        for x in range(3):
            for z in range(2):
                for y in range(self.height):
                    material = 'stone_bricks' if y in (0, 1, self.height - 3, self.height - 1) else rng.choice(('stone_bricks', 'stone_bricks', 'andesite', 'cobblestone'))
                    p.block((x, y, z), state_of(material), 'shaft')
        for x in range(-self.cap_overhang, 3 + self.cap_overhang):
            for z in range(-self.cap_overhang, 2 + self.cap_overhang):
                p.block((x, self.height, z), slab('stone_brick', 'top'), 'cap')
        for z in range(2):
            p.block((1, self.height + 1, z), state_of('cobblestone_wall', north='low' if z else 'none', south='none' if z else 'low', east='none', west='none', up='true', waterlogged='false'), 'flue')
        return p
