"""Native input/lifecycle fixtures shared by the momentum qualification suite."""
from pathlib import Path
from menu_protocol import native_down_count,down_count,render_probe,requested_menu_expectations
import json,struct
class Scenarios:
 def __init__(self,args,here,out,asm,core,pack,native_run=None,registers=None):self.a=args;self.h=here;self.o=out;self.asm=asm;self.core=core;self.pack=pack;self.native_run=native_run;self.registers=registers
 def battery(self,gp=0,momentum=(0,1,0,1),others=1,bad=None):
  base=bytearray(self.a.seed.read_bytes()[1:16384:2]);base[0xe20:0x1ffe]=bytes(0x11de);banks=[]
  for slot,length in [(1,84),(2,31),(3,7)]:
   b=bytearray(base);struct.pack_into('>4s6H',b,0x1000,b'SESN',gp,11 if gp else 0,gp,length,0 if gp else slot,3);struct.pack_into('>4s4H',b,0x1ea0,b'S3SV',1,slot,7,1);b[0x1ef0:0x1ef6]=b'PC\x01\xfe\x01\xfe';b[0x1ff6:0x1ffe]=bytes([0x55,0,0,2,2,0,0x5a,0]);b[0x1eee:0x1ef0]=bytes([0x50,0xaf]);banks.append(b)
  b=bytearray(banks[0])+b''.join(banks);b[0x2e18:0x2e20]=b'RP\x01\xfeCT\x01\xfe';b[0x4d20:0x4d22]=bytes([0x50,0xaf]);b[0xe20:0xe25]=b[0x2e20:0x2e25]=bytes.fromhex('123456789a')
  for off,tag in [(0x4d22,0x60),(0x4d2a,0x70),(0x4d32,0x72),(0x4d3a,0x74),(0x4d42,0x76),(0x4d4a,0x78)]:b[off:off+8]=bytes([tag+others,(tag+others)^255])*4
  for slot,value in enumerate(momentum):b[0x4d52+2*slot:0x4d54+2*slot]=bytes([0x7a+value,0x85-value])
  if bad:b[0x4d54:0x4d56]=bytes.fromhex(bad)
  return self.pack(b)
 def menu(self):
  p=self.o/'menu-observer';p.mkdir(exist_ok=True);code=self.asm(p,render_probe(""" cmpi.w #48,%d7
 bne.s original
 move.w %sr,-(%sp)
 addq.w #1,0xffffef90
 move.w 0xffffb028,0xffffef92
 move.l %a1,0xffffef94
 move.w (%sp)+,%sr
original: jmp 0x11ba4
""",self.a.rom,[48]),0x2fea00);b=bytearray(self.a.rom.read_bytes());assert b[0x2fea00:0x2fea00+len(code)]==b'\xff'*len(code) and b[0xf7bf4:0xf7bfa]==bytes.fromhex('4ef900011ba4');b[0x2fea00:0x2fea00+len(code)]=code;b[0xf7bf4:0xf7bfa]=bytes.fromhex('4ef9002fea00');rom=p/'rom.bin';rom.write_bytes(b);rows=[]
  def game(name,raw,commands,checks,dev,render=False,finish=False,others=1):
   for frame,expected in checks.items():
    for field in ['faceoff','plan','strict','alt','charge','pickup']:
     expected[field]=0 if name.startswith('fresh-') or name.startswith('restart-') and int(frame) in [3900,4600] else others
   requested_menu_expectations(self.a.rom,name,checks)
   q=self.o/name;q.mkdir(exist_ok=True);(q/'battery.srm').write_bytes(raw);(q/'spec.json').write_text(json.dumps(dict(name=name,checks=checks,render=render,finish=finish)));self.core(q,rom,commands,dict(GPGX_DEV=str(dev),GPGX_SRM=str(q/'battery.srm'),GPGX_HOOK=str(self.h/'testcases/momentum_menu_hook.py')));report=json.loads((q/'results.json').read_text());assert len(report)>=len(checks);rows.append(dict(name=name,observations=report));return(q/'battery.srm').read_bytes()
  down=[f'{900+50*i} press 1 Down 5' for i in range(down_count(self.a.rom,24))]
  for dev in [513,1281]:
   game('fresh-'+str(dev),bytes(65536),down+['2250 snap','2401 exit'],{2200:dict(momentum=0)},dev,True,others=0)
   raw=game('choices-'+str(dev),self.battery(momentum=(0,0,0,0)),down+['2300 press 1 Right 5','2450 snap','2500 press 1 Left 5','2700 press 1 Right 5','2901 exit'],{2200:dict(momentum=0,full_goalie='123456789a'),2400:dict(momentum=1,momentum_records='7a857b847a857a85'),2600:dict(momentum=0,momentum_records='7a857a857a857a85'),2800:dict(momentum=1,full_goalie='123456789a')},dev,True)
   game('reboot-'+str(dev),raw,['1301 exit'],{1200:dict(momentum=1,momentum_records='7a857b847a857a85')},dev)
   nav=['1100 press 1 Right 5','1300 press 1 Right 5','1500 press 1 Right 5','1700 press 1 Left 5','1900 press 1 Left 5','2100 press 1 Left 5','2301 exit']
   raw=game('slots-'+str(dev),self.battery(),nav,{1000:dict(momentum=1,slot=1),1200:dict(momentum=0,slot=2),1400:dict(momentum=1,slot=3),1600:dict(momentum=0,view=0),1800:dict(momentum=1,slot=3),2000:dict(momentum=0,slot=2),2200:dict(momentum=1,slot=1)},dev)
   game('slots-reboot-'+str(dev),raw,['1301 exit'],{1200:dict(momentum=1,slot=1)},dev)
   game('locked-'+str(dev),self.battery(1),down+['2300 press 1 Left 5','2500 press 1 Right 5','2701 exit'],{2200:dict(momentum=1,gp=1),2400:dict(momentum=1,gp=1),2600:dict(momentum=1,gp=1)},dev,True)
   for bad in ['0000','7b85','00ff','ffff']:game('corrupt-'+bad+'-'+str(dev),self.battery(1,bad=bad),['1100 press 1 Right 5','1300 press 1 Left 5','1501 exit'],{1000:dict(momentum=0,gp=1),1400:dict(momentum=0,gp=1)},dev)
   native=[f'{1000+200*i} press 1 Right 5' for i in range(3)]+[f'{1800+40*i} press 1 Down 5' for i in range(native_down_count(self.a.rom,23))]
   raw=game('native-'+str(dev),self.battery(1),native+['2900 press 1 Right 5','3101 exit'],{2850:dict(momentum=0,view=0),3000:dict(momentum=1,view=0,momentum_records='7b847b847a857b84',full_goalie='123456789a')},dev,True)
   game('native-reboot-'+str(dev),raw,[f'{1000+200*i} press 1 Right 5' for i in range(3)]+['1701 exit'],{1600:dict(momentum=1,view=0,full_goalie='123456789a')},dev)
   restart=['1100 press 1 Right 5','1300 press 1 Start 5']+[f'{1700+i*100} press 1 Down 5' for i in range(8)]+['2600 press 1 C 5','2900 press 1 Start 5','3200 press 1 C 5','3500 press 1 A 5','3500 press 1 C 5','4100 press 1 Left 5','4500 press 1 Right 5','4700 press 1 Right 5','5101 exit']
   game('restart-'+str(dev),self.battery(1,momentum=(0,1,1,1)),restart,{3100:dict(momentum=1,slot=2,gp=1),3900:dict(momentum=0,slot=2,gp=0),4300:dict(momentum=1,slot=1),4600:dict(momentum=0,slot=2),4900:dict(momentum=1,slot=3)},dev)
   raw=game('first-game-'+str(dev),self.battery(),['1500 press 1 Start 5','1900 press 1 Start 5','16001 exit'],{1200:dict(momentum=1),3300:dict(momentum=1)},dev,finish=dict(momentum=1,gp=1))
   game('completed-lock-'+str(dev),raw,down+['2300 press 1 Left 5','2601 exit'],{2200:dict(momentum=1,gp=1),2500:dict(momentum=1,gp=1)},dev,True)
  print('PASS',len(rows),'native Momentum menu/render/boot/slot/lock/restart/real completion cases',flush=True);return dict(cases=rows,encoding='0On(default),1Off; independent of Easy assistance')

from momentum_native_checks import events,gates,entries
Scenarios.events=events
Scenarios.gates=gates
Scenarios.entries=entries
from momentum_native_checks import game
Scenarios.game=game

from momentum_native_checks import negative
Scenarios.negative=negative
