# title: 035 寝ぼけた巨人
from pathlib import Path
from dataclasses import replace,asdict
import sys,json,hashlib
import pyxel
from model import *
from ui_support import Text,ReleaseGate
ROOT=Path(__file__).resolve().parent
W,H=384,410
class App:
 def __init__(self):
  self.text=Text();self.gate=ReleaseGate();self.level=0;self.title=True;self.pause=False;self.frame=0;self.events=[];self.snap=None
  self.baseline='--baseline' in sys.argv;self.demo='--demo' in sys.argv;self.s=initial(self.board)
  self.controls=[getattr(pyxel,'KEY_'+k) for k in ('LEFT','RIGHT','UP','DOWN','Z','SPACE','P','R','M','N','1','2','3')]
  self.replay=None
  if self.demo:self.replay=json.loads((ROOT/'solver_results.json').read_text(encoding='utf-8'))['A_alternate']['inputs']
  pyxel.init(W,H,title='035 寝ぼけた巨人',fps=30,display_scale=2,capture_sec=30,headless='--headless' in sys.argv)
  pyxel.run(self.update,self.draw)
 @property
 def board(self):return replace(LEVELS[self.level],left_sweep=False) if self.baseline else LEVELS[self.level]
 def log(self,kind):
  self.events.append({'frame':self.frame,'kind':kind,'level':self.level,'title':self.title,'pause':self.pause,'tick':self.s.tick,'hp':self.s.hp,'hands':[asdict(h) for h in self.s.hands],'status':self.s.status,'events':self.s.events,'hash':state_hash(self.s)})
 def reset(self):self.s=initial(self.board);self.pause=False;self.gate.enter()
 def update(self):
  self.frame+=1
  if pyxel.btnp(pyxel.KEY_F12):self.snap='manual_runtime';self.log('screenshot')
  if self.demo:
   if self.frame==30:self.title=False;self.log('start')
   if self.frame>=30 and self.frame-30<len(self.replay):
    self.s=step(self.board,self.s,Input(**self.replay[self.frame-30]));
    if self.s.events:self.log('step')
   if self.frame in (1,90,130,210,370,610,740):self.snap=f'runtime_{self.frame}'
   if self.frame==750:
    self.save('replay.json');pyxel.screencast(str(ROOT/'media/sleepy_giant_automatic_silent.gif'),scale=1);pyxel.quit()
   return
  if self.gate.blocked(any(pyxel.btn(k) for k in self.controls)):return
  for n,k in enumerate((pyxel.KEY_1,pyxel.KEY_2,pyxel.KEY_3)):
   if pyxel.btnp(k):self.level=n;self.reset();self.log('level');return
  if self.title:
   if pyxel.btnp(pyxel.KEY_SPACE):self.title=False;self.reset();self.log('start')
   return
  if pyxel.btnp(pyxel.KEY_M):self.title=True;self.pause=False;self.gate.enter();self.log('menu');return
  if pyxel.btnp(pyxel.KEY_R):self.reset();self.log('reset');return
  if pyxel.btnp(pyxel.KEY_P):self.pause=not self.pause;self.gate.enter();self.log('pause');return
  if self.pause:return
  if self.s.status!='playing':
   if pyxel.btnp(pyxel.KEY_N):self.level=(self.level+1)%3;self.reset();self.log('next')
   return
  inp=Input(int(pyxel.btn(pyxel.KEY_RIGHT))-int(pyxel.btn(pyxel.KEY_LEFT)),int(pyxel.btn(pyxel.KEY_DOWN))-int(pyxel.btn(pyxel.KEY_UP)),pyxel.btn(pyxel.KEY_Z))
  self.s=step(self.board,self.s,inp)
  if self.s.events:self.log('step')
 def save(self,name):
  (ROOT/'media'/name).write_text(json.dumps({'label':'automatic silent actual Pyxel renderer' if self.demo else 'Agent-operated native Linux QA, not human playtest','fps':30,'source_sha256':{n:hashlib.sha256((ROOT/n).read_bytes()).hexdigest() for n in ('app.py','model.py','ui_support.py')},'events':self.events},ensure_ascii=False,indent=2),encoding='utf-8')
 def xy(self,x,y):return 32+x*2,60+y*2
 def box(self,a,c,b,d,col):x,y=self.xy(a,c);pyxel.rect(x,y,(b-a)*2,(d-c)*2,col)
 def danger(self,it,active):
  col=8 if active else 4
  if it.kind=='circle':
   x,y,r=it.region;xx,yy=self.xy(x,y);pyxel.circ(xx,yy,r*2,col);pyxel.circb(xx,yy,r*2,8 if not active else 10)
  else:
   a,c,b,d=it.region;self.box(a,c,b,d,col)
   for y in range(int(max(4,c)),int(min(104,d)),4):
    x1,y1=self.xy(max(4,a),y);x2,y2=self.xy(min(156,b),y);pyxel.line(x1,y1,x2,y2,8 if not active else 10)
 def draw(self):
  self.text.boxes=[];s=self.s;l=self.board;visual=replace(s,tick=max(0,s.tick-1));a,age=slot(visual)
  pyxel.cls(1);self.text(12,5,'035 寝ぼけた巨人',7);self.text(12,26,'ABC'[self.level]+' '+l.name,10)
  self.text(278,6,f'体力 {s.hp}/3',11)
  pyxel.rect(32,60,320,216,3);pyxel.rectb(40,68,304,200,11)
  for xx in range(50,340,24):pyxel.pset(xx,255,11)
  hx,hy=self.xy(80,18);pyxel.elli(hx-35,hy-30,70,45,13);pyxel.line(hx-17,hy-7,hx-8,hy-7,1);pyxel.line(hx+8,hy-7,hx+17,hy-7,1);pyxel.circ(hx,hy+8,4,5)
  if s.intent and 0<=age<54 and s.hands[s.intent.arm].hp>0:self.danger(s.intent,age>=48)
  for st in l.stones:
   self.box(*st,5);x,y=self.xy(st[0],st[1]);pyxel.rect(x+2,y+2,(st[2]-st[0])*2-4,3,13)
  for i,((x,y),h) in enumerate(zip(HANDS,s.hands)):
   xx,yy=self.xy(x,y);phase=arm_phase(visual,i)
   if h.hp<=0:pyxel.circb(xx,yy,18,5);self.text(xx-12,yy-7,'済',6);continue
   pyxel.line(xx,yy-24,hx+(-20 if i==0 else 20),hy+13,13)
   pyxel.circ(xx,yy,20,15 if phase=='recover' else 9)
   if h.awake:pyxel.circ(xx-6,yy-3,4,7);pyxel.pset(xx-6,yy-3,1)
   else:pyxel.line(xx-10,yy-2,xx-2,yy-2,1)
   pyxel.line(xx+3,yy-2,xx+11,yy-2,1)
   if h.hit_lock:
    for dy in (-8,0,8):pyxel.line(xx-17,yy+dy,xx+17,yy+dy,7)
   self.text(xx-28,yy+21,f'{"LR"[i]} 鈴{h.hp}',7)
   for j in range(h.hp):pyxel.circ(xx-8+j*8,yy+14,3,10)
  xx,yy=self.xy(s.x,s.y)
  if not s.invuln or s.tick%4<2:
   pyxel.circ(xx,yy,8,7);pyxel.circ(xx,yy-3,4,12);pyxel.line(xx,yy,xx+s.facing[0]*12,yy+s.facing[1]*12,10)
  if 6<=s.sword_age-1<12:pyxel.line(xx,yy,xx+s.facing[0]*36,yy+s.facing[1]*36,10);pyxel.circb(xx+s.facing[0]*25,yy+s.facing[1]*25,8,7)
  if a<0:label=f'準備 あと{60-s.tick}  次 L'
  else:
   phase=arm_phase(visual,a);label=f'{"LR"[a]} '+{'rest':'休み','gone':'外れた腕の休み','telegraph':'予兆','active':'攻撃','recover':'戻り'}[phase]+f'  次 {"LR"[1-a]} {120-age}'
  self.text(12,284,label,10)
  self.text(12,305,'叩いた腕だけ、次が大振り。',7)
  self.text(12,325,'包帯は次の寝返り後に外れる。',6)
  self.text(12,348,'矢印 移動  Z 一打  P 停止・説明',7)
  self.text(12,368,'1-3 庭選択  R 再開  M 表紙',7)
  self.text(12,391,'自動入力・無音 / 人の試遊ではない' if self.demo else '時間制限なし。待つだけでも安全。',13)
  if self.baseline:self.text(216,284,'比較:左は小',9)
  if s.status!='playing' and not self.title:
   pyxel.rect(66,138,252,78,1);pyxel.rectb(66,138,252,78,7)
   self.text(84,151,'鈴が外れた！' if s.status=='won' else '大振りに触れた',10)
   self.text(84,178,'R 同じ庭  N 次の庭',7)
  if self.title or self.pause:
   pyxel.rect(18,86,348,180,1);pyxel.rectb(18,86,348,180,7)
   for j,line in enumerate(('両手の鈴を、3回ずつ叩いて外す。','左を起こすと横帯、右なら縦帯。','予兆の縞は固定。普通に歩いて避ける。','攻撃中は叩けない。戻りが好機。','1-3 庭選択  SPACE 開始' if self.title else 'P 再開。時計も剣も止まります。')):self.text(30,99+j*32,line,10 if j==4 else 7)
  for x,y,w,h,text in self.text.boxes:assert x>=0 and y>=0 and x+w<=W and y+h<=H,(x,y,w,h,text)
  if self.snap:pyxel.screenshot(str(ROOT/f'media/{self.snap}.png'),scale=2);self.save('replay_partial.json' if self.demo else 'gui_events.json');self.snap=None
if __name__=='__main__':App()
