"""Deterministic per-roster critical ratings and guarded experimental ROM hooks."""
import json
import struct
from pathlib import Path

ORIGIN = 0x2FF400
TABLE = 0x2FF800
ATTRIBUTES = 0x2FFC00
LOOKUP = ORIGIN + 8
ABBR = 'ANA BOS BUF CGY CAR CHI COL CBJ DAL DET EDM FLA LAK MIN MTL NSH NJD NYI NYR OTT PHI PIT SJS SEA STL TBL TOR UTA VAN VGK WSH WPG'.split()


def ratings(rom):
    players = []
    for team in range(32):
        record = struct.unpack_from('>I', rom, 0x30E + 4 * team)[0]
        address = record + struct.unpack_from('>H', rom, record)[0]
        for slot in range(26):
            length = struct.unpack_from('>H', rom, address)[0]
            name = rom[address+2:address+length].rstrip(b'\0').decode('latin1').replace('_', ' ')
            r = [int(c, 16) for c in rom[address+length+1:address+length+8].hex()]
            goalie = not (r[6] or r[8] or r[9])
            quality = 2*r[3] + 2*r[5] + 2*r[9] + r[8] + r[12] + r[1] + r[2]
            players.append(dict(team=team, abbr=ABBR[team], slot=slot, name=name, goalie=goalie, quality=quality))
            address += length + 8
    qualities = [p['quality'] for p in players if not p['goalie']]
    low, high = min(qualities), max(qualities)
    assert high > low
    overrides = json.loads(Path(__file__).with_name('crit_overrides.json').read_text())
    known = {p['abbr'] + ':' + p['name'] for p in players}
    assert overrides.keys() <= known, 'unknown critical-rating override player'
    for p in players:
        value = 0 if p['goalie'] else 20 + (80*(p['quality']-low)+(high-low)//2)//(high-low)
        value = overrides.get(p['abbr'] + ':' + p['name'], value)
        assert type(value) is int and 0 <= value <= 100
        assert not p['goalie'] or value == 0, 'goalies keep zero crit'
        p['crit'] = value
    return bytes(p['crit'] for p in players), players


def install(data, assemble, patch):
    code = assemble('critical', ORIGIN)
    assert len(code) <= TABLE - ORIGIN, 'critical code overlaps ratings'
    patch(ORIGIN, b'\xFF' * len(code), code)
    velocity = assemble('critical_velocity', 0x2ED800)
    assert len(velocity) <= 0x800, 'critical velocity overlaps the next code bank'
    patch(0x2ED800, b'\xFF' * len(velocity), velocity)
    table, players = ratings(data)
    assert len(table) == 32*26 and TABLE+len(table) <= ATTRIBUTES
    patch(TABLE, b'\xFF' * len(table), table)
    patch(0xC4A8, bytes.fromhex('3438bedec5fc0044'), bytes.fromhex('4ef9002ff4004e71'))
    patch(0x8D68, bytes.fromhex('b87c00026300000a'), bytes.fromhex('4ef9002ff4044e71'))
    # Preserve all 17 native attributes; append our own row before the terminator.
    original = bytes(data[0x208FE2:0x209158])
    assert len(original) == 17*22 and data[0x209158:0x20915A] == b'\xFF\xFF'
    row = struct.pack('>H', 18) + b'[ Crit Chance  ]' + struct.pack('>HH', 0, 12)
    assert len(row) == 22
    blob = original + row + b'\xFF\xFF'
    assert ATTRIBUTES + len(blob) <= 0x2FFFC0
    patch(ATTRIBUTES, b'\xFF' * len(blob), blob)
    for operand, offset in [(0x84DC, 0), (0x8BD8, 0), (0xFC85E, 22), (0xFC890, 22)]:
        patch(operand, struct.pack('>I', 0x208FE2+offset), struct.pack('>I', ATTRIBUTES+offset))
    return players
