# Retained assessment of the new garden-house client and independently frozen environments.
import argparse
import json
from pathlib import Path
from hearth import Scene, Box
from hearth.environment import Terrain, gradient, wave, radial
from hearth.randomness import Scope
from hearth.components import adapted
from hearth.lodges import lodge_scene
from hearth.persistence import semantic_digest
from hearth.quality_assessment import evaluate, summary, fingerprint
from examples.garden_house import garden_house


def detail_signature(scene):
    """Measure functional stations and actual facade openings separately from massing."""
    stations, openings = [], []
    building = scene.nodes['/building']
    floor = building.contract.decisions['floor']
    for n in scene.nodes.values():
        if n.type.startswith('furnishing.suite.') or n.type.startswith('window.'):
            local = building.frame.inverse(n.frame.origin)
            place = (local[0], local[1] - floor, local[2])
            shape = sorted(n.frame.inverse(p) for p in scene.cells(n.path))
            record = (n.type, place, shape)
            (stations if n.type.startswith('furnishing.suite.') else openings).append(record)
    return {'stations': fingerprint(sorted(stations)), 'facade_openings': fingerprint(sorted(openings)),
            'station_count': len(stations), 'window_count': len(openings),
            'window_boxes': sum(n.type == 'garden.window-box' for n in scene.nodes.values())}


def run(seed, output, artifacts):
    output.mkdir(parents=True, exist_ok=True)
    artifacts.mkdir(parents=True, exist_ok=True)
    domain = Box((-10, -4, -20), (52, 72, 52))
    environments, inputs, rows, details = [], [], [], {}
    # Freeze all six environments before any of their structure seeds are used.
    for index in range(6):
        es = 4100 + seed * 100 + index
        rng = Scope(es).stream('detail-environment')
        field = (gradient(rng.choice((-.025, 0, .025)), rng.choice((-.02, 0, .02)), 4)
                 + wave(.3, .13, .09, rng.random() * 6)).blend(
                     gradient(-.02, .02, 4) + radial(18, 18, 36, .5), radial(8, 10, 44, .6)).clamp(2, 6)
        water = rng.choice((None, 4, 5))
        scene = Scene(es, domain=domain)
        scene.place('land', Terrain(domain, field, water))
        scene.finalize()
        environments.append(scene)
        inputs.append({'environment_seed': es, 'expression': field.expression(), 'water': water, 'digest': semantic_digest(scene)})
    (output / 'frozen-inputs.json').write_text(json.dumps(inputs, indent=2))

    def retain(label, specification, recipe, artifact=None):
        def measured():
            scene = recipe()
            details[label] = detail_signature(scene)
            return scene
        row = evaluate(label, specification, measured, artifact)
        if label in details:
            row['details'] = details[label]
        rows.append(row)
        (output / 'results.json').write_text(json.dumps(rows, indent=2))
        print(label, row['status'], flush=True)

    for bs in range(8):
        label = f'court-{bs}'
        artifact = artifacts / (label + '.litematic') if bs in (0, 2, 7) else None
        retain(label, {'structure_seed': bs, 'environment_seed': 4100 + seed * 100, 'garden': True},
               lambda b=bs: lodge_scene(garden_house(b), b, 4100 + seed * 100), artifact)
    for index, environment in enumerate(environments):
        for bs in (2, 7):
            def place(e=environment, b=bs):
                scene = e.fork(seed=b)
                scene.compose_choice(adapted('building', garden_house(b), '/land'))
                return scene.finalize()
            label = f'new-pair-{index}-{bs}'
            artifact = artifacts / (label + '.litematic') if index in (0, 1) else None
            retain(label, {**inputs[index], 'structure_seed': bs, 'garden': False}, place, artifact)
            if semantic_digest(environment) != inputs[index]['digest']:
                raise AssertionError('Frozen environmental input changed')
    result = {'client': summary(rows[:8]), 'independent_pairs': summary(rows[8:]),
              'interior_signatures': len({d['stations'] for d in details.values()}),
              'opening_signatures': len({d['facade_openings'] for d in details.values()})}
    (output / 'summary.json').write_text(json.dumps(result, indent=2))
    (artifacts / 'manifest.json').write_text(json.dumps([r for r in rows if 'artifact' in r], indent=2))
    return result


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--seed', type=int, default=0)
    parser.add_argument('--output', type=Path, default=Path('reports/building-detail/new-composition'))
    parser.add_argument('--artifacts', type=Path, default=Path('samples/building-detail/new-composition'))
    args = parser.parse_args()
    print(json.dumps(run(args.seed, args.output, args.artifacts), indent=2))
